实验八 真实感图形
DESCRIPTION
实验八 真实感图形. 一 . 概述:. 本实验是根据真实感图形生成的光照技术、消影技术、几何纹理映射等相关理论,编程实现具有三维立体感的立体图形的绘制 。 由于本实验难度较大,所以对本科不要求必须完成求。故仅作为附加实验,给能力较强的同学们提供锻炼机会。. 二.实验的主要目的:. 1 、检查学生对真实感图形生成相关技术的掌握情况; 2 、培养学生根据真实感图形生成的光照技术、消影技术、几何纹理映射等等相关技术之一,具有编程实现三维立体感图形的能力; 3 、培养学生根据相关理论,能借助计算机分析解决实际问题的能力。 - PowerPoint PPT PresentationTRANSCRIPT
实验八 真实感图形
一 . 概述:
本实验是根据真实感图形生成的光照技术、消影技术、几何纹理映射等相关理论,编程实现具有三维立体感的立体图形的绘制 。
由于本实验难度较大,所以对本科不要求必须完成求。故仅作为附加实验,给能力较强的同学们提供锻炼机会。
二.实验的主要目的:
1 、检查学生对真实感图形生成相关技术的掌握情况;2 、培养学生根据真实感图形生成的光照技术、消影技术、
几何纹理映射等等相关技术之一,具有编程实现三维立体感图形的能力;
3 、培养学生根据相关理论,能借助计算机分析解决实际问题的能力。
注:对于本科生来说,此实验难度稍大。所以不要求必须完成,仅作锻炼与参考。
三.实验步骤:
1 .建立 project ; 2 .选择欲创建的文档类型;3 .根据真实感图形生成中的消隐技术、或光照技术、图形反走样技术、或阴影生成技术,设计出一个具有真实感图形的程序;4 .编译、调试、运行,并检查是否得到预期结果;5 .按要求书写并提交试验报告。
附录: 下面仅对消影技术的实现进行编程演示:实验步骤和方法与前面的实验一相同,区别仅在于: 是在“。。。 View.cpp” 文件之后,按要求添加如下代码:
void CMyView::Project(float X, float Y, float Z)// 此函数求点的平行投影和透视投影坐标值{ XObs = -X * Aux1 + Y * Aux3; YObs = -X * Aux5 - Y * Aux6 + Z * Aux4; // 求透视投影坐标值
ZObs = -X * Aux7 - Y * Aux8 - Z * Aux2 + Rol; XProj = DE * XObs / ZObs; YProj = DE * YObs / ZObs;
}
void CMyView::WLineTo(float X, float Y, float Z,CDC*pDC)// 用三维点坐标直接从当前点画线到一点的函数{
Project(X, Y, Z); // 将三维点作投影 XScreen = floor(0.5 + XProj * Scale +400); // 圆整(立体在
屏幕上初始的 X 坐标位置 ) YScreen = floor(0.5 + 300 - YProj); // 圆整(立体在屏幕上初
始的 Y 坐标位置) pDC->LineTo(XScreen, YScreen); // 画线到一点
}
void CMyView::WMoveTo(float X, float Y, float Z,CDC*pDC)// 三维坐标下直接将当前点移动到某点的函数{
Project (X, Y, Z); // 将三维点作投影 XScreen = floor(0.5 + XProj * Scale + 400); //// 圆整(立体在
屏幕上初始的 X 坐标位置 ) YScreen = floor(0.5 + 300 - YProj); // 圆整(立体在屏幕上初
始的 Y 坐标位置) pDC->MoveTo(XScreen, YScreen); // 移动到某点
}
void CMyView::ReadVertics()// 此函数用来给数组 St 的元素赋顶点坐标值{ St[1][1] = 40; St[1][2] = 154; St[1][3] =-20; St[2][1] = 40; St[2][2] = 154; St[2][3] = 0; St[3][1] = 40; St[3][2] =46; St[3][3] = 0; St[4][1] = 40; St[4][2] =46; St[4][3] =-20; St[5][1] =-40; St[5][2] =46; St[5][3] =-20; St[6][1] =-40; St[6][2] = 154; St[6][3] =-20; St[7][1] =-40; St[7][2] = 154; St[7][3] = 0; St[8][1] = 0; St[8][2] = 134; St[8][3] = 40; St[9][1] = 0; St[9][2] =66; St[9][3] = 40; St[10][1] =-40; St[10][2] =46; St[10][3] = 0;}
void CMyView::ReadFaces()// 此函数给数组 Fc 的元素赋表面有关的数据值{ NF= 9; Fc[1][0]=4; Fc[1][1]=1; Fc[1][2]=2; Fc[1][3]=3; Fc[1][4]=4; Fc[2][0]=4; Fc[2][1]=1; Fc[2][2]=6; Fc[2][3]=7; Fc[2][4]=2; Fc[3][0]=3; Fc[3][1]=2; Fc[3][2]=7; Fc[3][3]=8; Fc[4][0]=4; Fc[4][1]=2; Fc[4][2]=8; Fc[4][3]=9; Fc[4][4]=3; Fc[5][0]=4; Fc[5][1]=1; Fc[5][2]=4; Fc[5][3]=5; Fc[5][4]=6; Fc[6][0]=4; Fc[6][1]=7; Fc[6][2]=10; Fc[6][3]=9; Fc[6][4]=8; Fc[7][0]=3; Fc[7][1]=3; Fc[7][2]=9; Fc[7][3]=10; Fc[8][0]=4; Fc[8][1]=10; Fc[8][2]=5; Fc[8][3]=4; Fc[8][4]=3; Fc[9][0]=4; Fc[9][1]=5; Fc[9][2]=10; Fc[9][3]=7; Fc[9][4]=6;}
void CMyView::VisionVector(int St1) /* 该函数用于求观察方向矢量 St1 is the first point of a face. */{ v1=O1-St[St1][1]; v2=O2-St[St1][2]; v3=O3-St[St1][3];}
void CMyView::NormalVector(int St1, int St2, int St3)// 此函数用表面三个顶点调用求该表面的法矢 // St_i is the i_th point of a face. {float P1, P2, P3, Q1, Q2, Q3; // 求一个向量
P1 = St[St2][1] - St[St1][1];P2 = St[St2][2] - St[St1][2];P3 = St[St2][3] - St[St1][3];
// 求另一个向量Q1 = St[St3][1] - St[St1][1];Q2 = St[St3][2] - St[St1][2];Q3 = St[St3][3] - St[St1][3];
// 用向量积求法向量n1 = P2 * Q3 - Q2 * P3;n2 = P3 * Q1 - Q3 * P1;n3 = P1 * Q2 - Q1 * P2;
}
float CMyView::ScaleProduct(float v1, float v2, float v3, float n1, float n2, float n3)// 此函数用于求观察方
向矢量与表面法矢的数量积{float SProduct;
SProduct = v1 * n1 + v2 * n2 + v3 * n3;return(SProduct);
}
void CMyView::DrawFace(CDC*pDC)// 画出立体上的平面{int S, NS, No;float X, Y, Z, X0, Y0, Z0;
NS = Fc[F][0];for ( S = 1; S<= NS; S++ ){ No = Fc[F][S]; X = St[No][1]; Y = St[No][2]; Z = St[No][3];
if ( S == 1 ) { WMoveTo(X, Y, Z,pDC); X0 = X; Y0 = Y; Z0 = Z;
} else WLineTo(X, Y, Z,pDC);}WLineTo (X0, Y0, Z0,pDC);
}
void CMyView::DrawObject()// 此函数用于绘出消隐立体图 {
int St1, St2, St3;CDC*pDC=GetDC();CPen pen1(PS_SOLID,1,(COLORREF)1),pen2(PS_DOT,1,(COLORREF)
1);CPen *pOldPen=pDC->SelectObject(&pen1);for ( F = 1; F<= NF; F++ ){ St1 = Fc[F][1]; St2 = Fc[F][2]; St3 = Fc[F][3]; VisionVector(St1); // 求观察方向矢量 NormalVector(St1, St2, St3); // 求表面法矢 if ( ScaleProduct( v1,v2,v3,n1,n2,n3 ) > 0 ) // 判断数量积正否
{ pDC->SelectObject(&pen1); DrawFace(pDC); // 数量积大于零,表面可见,画出此表面
} else; }pDC->SelectObject(pOldPen);ReleaseDC(pDC);
}
void CMyView::VisionPoint()// 此函数用于给出视点位置{ // 投影时初始值即正弦值和余弦值及其乘积的计算、赋值
float Th, Ph; Th = 3.1415926 * Theta / 180; Ph = 3.1415926 * Phi / 180; Aux1 = sin(Th); Aux2 = sin(Ph); Aux3 = cos(Th); Aux4 = cos(Ph); Aux5 = Aux3 * Aux2; Aux6 = Aux1 * Aux2; Aux7 = Aux3 * Aux4; Aux8 = Aux1 * Aux4; // 给出视点位置 O1 = Rol * Aux7; O2 = Rol * Aux8; O3 = Rol * Aux2;}
void CMyView::Mydraw(){
RedrawWindow();ReadVertics();ReadFaces();// 绘出透视投影下的凸多面体图形VisionPoint(); // 给出视点位置
DrawObject(); // 画出立体的图形}
void CMyView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {// 此函数用来利用上下左右键移动视点角度位置 ,C 键切换投影类型 .
switch(nChar){case VK_UP: / 上下左右键选择
Phi=Phi-IncAng;Mydraw();break;
case VK_DOWN:Phi=Phi+IncAng;Mydraw();break;
case VK_RIGHT:Theta=Theta+IncAng;Mydraw();break;
case VK_LEFT:Theta=Theta-IncAng;Mydraw();break;
default:break;
}}
void CMyView::OnTumianti() {// 透视投影赋初值
Rol = 600.0; S=1; Theta = 60; Phi = 135; DE = 1000; Mydraw(); CDC*pDC=GetDC(); // 指出控制旋转防向键 pDC->TextOut(10,10," 按下键盘上的“↑”、“↓”、“←”、“→”箭头可从各方位观看图形 "); ReleaseDC(pDC);}
void CMyView::OnYuanjin() {
CClientDC *pdc=new CClientDC(this);CPen *pen1=new CPen(PS_SOLID,1,RGB(0,0XFF,0));CPen *pen2=new CPen(PS_SOLID,1,RGB(255,0,0));CPen *OldPen=pdc->SelectObject(pen1);
CBrush brush; brush.CreateSolidBrush(RGB(0,0,0)); CBrush *oldbrush=(CBrush*)pdc->SelectObject(&brush);
int flag,k1,k2,r1,r2,n,d,m,p;int xs1,xs2,xs3,xs4,ys1,ys2,ys3,ys4,i,j,lastp;double x,y,z,thx,th1,th3,yw,zw,xw,thy,th2;double PI,ed,od,eh,zzw,ppw;double xs[50][17],zs[50][17],ys[50][17],zc[50][17],x1,y1;int zz[850],pp[850];
r1=100;r2=40;k1=20;k2=16;ed=1500; eh=0;od=0;n=0;PI=3.14159;th3=1;thx=0.9;
// 计算顶点坐标值 for(d=-1;d<=1;d+=2){
for(th1=0; th1<=2*PI+0.1;th1+=2*PI/k1){ n=n+1; m=0; for(th2=0;th2<=2*PI+0.1;th2+=2*PI/k2){ m=m+1;
x=r1+r2*cos(th2);y=r2*sin(th2);z=0;thy=th1;
//Call rot_yzw=z; xw=x;x=zw*cos(thy)-xw*sin(thy);z=zw*sin(thy)+xw*cos(thy);//x=x+r1/2*d;
if(d==1) {yw=y;zw=z;y=yw*cos(PI/2)-zw*sin(PI/2);z=yw*sin(PI/2)+zw*cos(PI/2);
}thy=th3; //Call rot_y zw=z; xw=x;x=zw*cos(thy)-xw*sin(thy);z=zw*sin(thy)+xw*cos(thy); // Call rot_x:yw=y; zw=z;y=yw*cos(thx)-zw*sin(thx);z=yw*sin(thx)+zw*cos(thx); //Call persx=x*ed/(ed-od-z);y=(y*ed-eh*(od+z))/(ed-od-z);
xs[n][m]=x;ys[n][m]=y;zs[n][m]=z;
}
//next th2
flag=0;//Next th1
}flag=0;//Next d
}// 计算 center 值p=0; for(n=1;n<=k1;n+=1){
for(m=1;m<=k2;m+=1){zc[n][m]=int((zs[n][m]+zs[n+1][m+1])/2);
zz[p]=zc[n][m]; pp[p]=p; p=p+1; //Next m //Next n
}}
lastp=p-1;
//排序 for(i=2;i<=lastp;i+=1){
for(j=i-1;j>=0;j+=-1){ if(zz[j]>zz[j+1]) {
zzw=zz[j]; zz[j]=zz[j+1]; zz[j+1]=zzw; ppw=pp[j]; pp[j]=pp[j+1]; pp[j+1]=ppw;
} }
}
// 绘图 for(p=0;p<=lastp;p+=1){ n=int(pp[p]/k2)+1; m=pp[p]%k2+1; if(n!=(k1+1)){
xs1=int(xs[n][m]); ys1=int(ys[n][m]); xs2=int(xs[n+1][m]); ys2=int(ys[n+1][m]); xs3=int(xs[n+1][m+1]); ys3=int(ys[n+1][m+1]); xs4=int(xs[n][m+1]); ys4=int(ys[n][m+1]); //}
if (abs(ys1*(xs2-xs3)+ys2*(xs3-xs1)+ys3*(xs1-xs2))>80){
pdc->MoveTo(xs1+320,ys1+200);pdc->SelectObject(pen1); pdc->LineTo(xs2+320,ys2+200);
pdc->LineTo(xs3+320,ys3+200); pdc->LineTo(xs4+320,ys4+200); pdc->LineTo(xs1+320,ys1+200);
//paint x=(xs[n][m]+xs[n+1][m+1])/2; y=(ys[n][m]+ys[n+1][m+1])/2; x1=int(x+320); y1=int(y+200); pdc->ExtFloodFill(x1,y1,RGB(0,255,0),0); //endif }
pdc->MoveTo(xs1+320,ys1+200); pdc->SelectObject(pen2);
pdc->LineTo(xs2+320,ys2+200); pdc->LineTo(xs3+320,ys3+200); pdc->LineTo(xs4+320,ys4+200); pdc->LineTo(xs1+320,ys1+200); } }pdc->SelectObject(OldPen); delete pen1; delete pen2; pdc->DeleteDC();}
其运行结果如下: 用“↑”、“↓”、“←”、“→”键控制图形的旋转,以观察消隐效果。
光照示例 ( 程序略 ) :