编写立方体的一点透视投影图

一.窗口到视区的坐标变换
1.窗口:在世界坐标系中,设置一矩阵区域,来观察整幅图中的部分内容。
在这里插入图片描述
2.视区:窗口映射到显示器上的区域称为视区。
在这里插入图片描述
3.坐标变换

(xv-xv_min)/(xv_max-xv_min)=(xw-xw_min)/(xw_max-xw_min)
(yv-yv_min)/(yv_max-yv_min)=(yw-yw_min)/(yw_max-yw_min)    **[(xv,yv)为视区中的坐标]**

转化为:

xv=xv_min+(xw-xw_min)Sx
yv=yv_min+(yw-yw_min)Sy

其中缩放因子Sx,Sy:

Sx=(xv_max-xv_min)/(xw_max-xw_min)
Sy=(yv_max-yv_min)/(yw_max-yw_min)      **[当Sx=Sy,物体保持相似性]**

二.一点透视投影
1.透视投影
透视投影的视线(投影线)是从视点(观察点)出发,视线是不平行的。模拟人眼观察物体的过程。透视图是通过透视中心(视点),将空间立体投影到二维平面(投影面)所产生的图形,具有较强的立体感。

2.一点透视
不平行于投影平面的平行线汇聚的一点称为灭点,在坐标轴上的灭点叫做主灭点。主灭点数和投影平面切割坐标轴的数量相对应。一点透视即有一个主灭点Z_prp。
在这里插入图片描述
3.透视变换方程的推导
(1)坐标为(x,y,z)的P点到观察平面上点(xp,yp,zp)的透视投影。
在这里插入图片描述
这里,直线AB的参数化方程:

     x’=x-xu ①
     y’=y-yu ②
     z’=z-(z-z_prp)u ③
     u∈[0,1]

我们可通过③式得到u值求解①②:
当u=0,位于P=(x,y,z)处;u=1,位于投影参考点(0,0,z_prp)处
在观察平面上,z’=z_vp(z_vp是投影平面在Z轴的截距)

z_vp=z-(z-z_prp)u 
u=(z-z_vp)/(z-z_prp)

将u值代入x’和y’的方程,得到透视变换方程:

xp=x[(z_prp-z_vp)/(z_prp-z)]=x *dp/(z_prp-z)
yp=y[(z_prp-z_vp)/(z_prp-z)]=y *dp/(z_prp-z)

其中, dp= z_prp-z_vp是投影参考点到观察平面的距离
这里代码中直接设置z_vp =0,则

xp=x* z_prp/(z_prp-z)=x *1/( 1-z/z_prp)
yp=y* z_prp/(z_prp-z)=y *1/( 1-z/z_prp)

三.主要步骤
1.确定空间坐标系中八个点坐标(数组存放);

2.确定立方体各顶点的投影坐标(这里z_vp=0,即投影在xoy面);

xp=x* z_prp/(z_prp-z)=x *1/( 1-z/z_prp)
yp=y* z_prp/(z_prp-z)=y *1/( 1-z/z_prp)

在这里插入图片描述
图中红线即得到的投影区域(其中有重合点),这样得到的图形没有立体感,所以可以在最初设置x和y的平移量使之有立体感

3.将投影坐标变换到屏幕上,最后连线即可。

xv=xv_min+(xw-xw_min)Sx
yv=yv_min+(yw-yw_min)Sy
Sx=(xv_max-xv_min)/(xw_max-xw_min)
Sy=(yv_max-yv_min)/(yw_max-yw_min)

代码如下:

#include<graphics.h>
#include<stdio.h>

float xt,yt,d;//xt,yt为平移量,d为视点z_prp坐标 

void Perspect(float *p){	
	//确定立方体各顶点的投影坐标 
	for(int i=0;i<24;i=i+3){
		p[i]=(p[i]+xt)/(1-p[i+2]/d);
		p[i+1]=(p[i+1]+yt)/(1-p[i+2]/d);
		p[i+2]=0;
	}
	// 得到窗口中xp_min xp_max yp_min yp_max
	float xp_min=p[0];
	float xp_max=p[3];
	for(int i=0;i<24;i=i+3){
		if(p[i]<=xp_min)
			xp_min=p[i];
		else
			xp_max=p[i];
	}
	
	float yp_min=p[1];
	float yp_max=p[4];
	for(int i=1;i<24;i=i+3){
		if(p[i]<=yp_min)
			yp_min=p[i];
		else
			yp_max=p[i];
	}
	//窗口到视区的坐标变换 
	float xv_min=20;
	float xv_max=45;
	float yv_max=20;
	float yv_min=45;
	
	float Sx=(xv_max-xv_min)/(xp_max-xp_min);
	float Sy=(yv_max-yv_min)/(yp_max-yp_min);
	for(int i=0;i<24;i=i+3){
		p[i]=xv_min+(p[i]-xp_min)*Sx;
		p[i+1]=yv_min+(p[i+1]-yp_min)*Sy;
		p[i+2]=0;
	}
	for(int i=0;i<24;i++){
		p[i]=(int)p[i];
	}
	int p1[10]={p[0],p[1],p[3],p[4],p[15],p[16],p[12],p[13],p[0],p[1]};
	int p2[10]={p[6],p[7],p[9],p[10],p[21],p[22],p[18],p[19],p[6],p[7]};
	//连线 
	drawpoly(5,p1);drawpoly(5,p2);
	line(p[0],p[1],p[9],p[10]);
	line(p[3],p[4],p[6],p[7]);
	line(p[15],p[16],p[18],p[19]);
	line(p[12],p[13],p[21],p[22]);
}

int main(){
	initgraph(640,480);
	setbkcolor(BLACK);
	float p[24]={0,0,0,1,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,1,1,0,1,1};
	printf("输入平移的位置:\n");
	printf("xt= ");scanf("%f",&xt);
	printf("yt= ");scanf("%f",&yt);
	printf("输入视点z_prp:\n");
	printf("d= ");scanf("%f",&d);
	Perspect(p);
	getch();
	closegraph();
	return 0;
}

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算机形学实验报告 SA03006073 魏思 介绍: 所有实验用一个MFC程序完成。组合成一个类似于画程序。 程序左边面板上有所有的按钮,要完成功能只要点击相应按钮然后在客户区操作就可以了。 实验一、元的生成 实验要求: 能够生成直线、圆、椭圆、可以按照给定的边界顶点完成区域填充。 实验内容: 直线生成: 使用Bresenhan方法绘制直线。 使用介绍:鼠标点击左边按钮区域的直线按钮,然后在右边客户区域内点下鼠标左键,拖动鼠标倒你想要绘制的地方,就可以形成一条直线。这个直线是用MFC提供的CDC类的LinetTo() 函数绘制的。然后开始自己的直线生成算法来重新绘制,使用红色的线条来绘制直线,系统提供的直线函数是用来做参考的。 椭圆和圆的生成: 使用Bresenhan方法绘制椭圆和圆。 使用介绍:和直线基本类似,鼠标点击按钮区域的椭圆或者是圆按钮,然后在右边客户区域内点下鼠标左键,拖动鼠标倒你想要绘制的地方,就可以形成一个圆或者是椭圆。这个圆或者是椭圆是使用MFC提供的CDC类的Ellipse() 函数绘制的。然后开始自己的椭圆和圆生成算法来重新绘制,使用红色的线条来绘制,系统提供的椭圆或者是圆函数是用来做参考的。 区域填充: 使用边扫描填充法完成区域填充 使用介绍:鼠标点击按钮区域的正方形按钮,会弹出一个输入对话框。在这个对话框里,输入包含你要求填充的区域的线条坐标。格式如下(以正方形为例): (0,0),(100,0) (100,0),(100,100) (100,100),(0,100) (0,100),(0,0) 点击确定以后,首先根据输入的线条坐标绘制出区域的边界。然后使用填充算法来填充区域。 为了方便观察,采用了延时100毫秒显示下一个点。所以有缓慢的感觉,不是算法的问题。 实验二、样条曲线的生成

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值