赞
踩
(1)掌握双线性光强插值模型。
二、实验步骤
(1)建立三维坐标系 Oxyz,原点位于屏幕客户区中心,x 轴水平向右为正,
y 轴铅直向上为正,z 轴垂直于屏幕指向观察者。
(2)绘制体心和坐标系中心重合的球体表面,使用 ZBuffer 消隐算法进行消
隐。使用 Z‐Buffer 算法对球面进行深度消隐,然后使用有效边表算法进行填充。
为了减少渲染的面片数,先使用凸多面体消隐算法对球体不可见面片进行剔除。
然后使用 Z‐Buffer 算法对可见面片进行消隐,最后使用有效边表算法进行填充。
使用 CZBuffer 类对象 zbuf 进行处理,区分了三角面片和四边形面片两种情况。
(3)使用单点光源对球体进行照射生成 Gouraud 光照模型,光源位置位于
球体右上方。使用 CTestView 类的构造函数设置默认的光源颜色和材质颜色。场景中使用了一个点光源,光源颜色为白色,位于屏幕右上方。物体材质为红色。
(4)使用键盘方向键旋转球体。
(5)使用动画按钮,播放或停止球体动画。
三、实验结果
四、实验体会
在本次实验中,通过不断的探索和实践,我掌握了双线性光强插值模型的构造,知道怎么使用ZBuffer消隐算法进行消隐,怎么使用单点光源对球体进行照射生成Gourand光照模型,在这些过程中,我熟悉了光照模型、双线性颜色插值算法以及对有效边表填充算法更加熟练。虽然过程中遇到了许多困难,但在再次阅读教材理解教材后,以及跟同学讨论后,最终通过不断的努力将实验完成了,学会了将理论知识用在实践中,收获良多。
附录:源代码
创建的相应类
- #include "stdafx.h"
- #include "Test.h"
-
- #include "TestDoc.h"
- #include "TestView.h"
-
- #include "math.h"
- #define PI 3.1415926
- #define ROUND(x) int(x+0.5)
-
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
-
- /
- // CTestView
-
- IMPLEMENT_DYNCREATE(CTestView, CView)
-
- BEGIN_MESSAGE_MAP(CTestView, CView)
- //{{AFX_MSG_MAP(CTestView)
- ON_WM_KEYDOWN()
- ON_COMMAND(ID_MENUPlay, OnMENUPlay)
- ON_WM_TIMER()
- ON_UPDATE_COMMAND_UI(ID_MENUPlay, OnUpdateMENUPlay)
- ON_WM_ERASEBKGND()
- //}}AFX_MSG_MAP
- // Standard printing commands
- ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
- ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
- ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
- END_MESSAGE_MAP()
-
- /
- // CTestView construction/destruction
-
- CTestView::CTestView()
- {
- // TODO: add construction code here
- P=NULL;F=NULL;
- Play=FALSE;
- R=1000,d=800,Phi=90.0,Theta=0;
- LightNum=1;//光源个数
- pLight=new CLighting(LightNum);//一维光源动态数组
- pLight->Light[0].SetPosition(800,800,800);//设置光源位置坐标
- for(int i=0;i<LightNum;i++)
- {
- pLight->Light[i].L_Diffuse=CRGB(1.0,1.0,1.0);//光源的漫反射颜色
- pLight->Light[i].L_Specular=CRGB(1.0,1.0,1.0);//光源镜面高光颜色
- pLight->Light[i].L_C0=1.0;//常数衰减系数
- pLight->Light[i].L_C1=0.0000001;//线性衰减系数
- pLight->Light[i].L_C2=0.00000001;//二次衰减系数
- pLight->Light[i].L_OnOff=TRUE;//光源开启
- }
- pMaterial=new CMaterial;//一维材质动态数组
- pMaterial->M_Ambient=CRGB(0.547,0.08,0.0);//材质对环境光的反射率
- pMaterial->M_Diffuse=CRGB(0.85,0.08,0.0);//材质对漫反射光的反射率
- pMaterial->M_Specular=CRGB(0.828,0.8,0.8);//材质对镜面反射光的反射率
- pMaterial->M_Emit=CRGB(0.2,0.0,0.0);//材质自身发散的颜色
- pMaterial->M_Exp=20.0;//高光指数
- }
-
- CTestView::~CTestView()
- {
- if(pLight!=NULL)
- {
- delete [] pLight;
- pLight=NULL;
- }
- if(pMaterial!=NULL)
- {
- delete pMaterial;
- pMaterial=NULL;
- }
- if(P!=NULL)
- {
- delete[] P;
- P=NULL;
- }
- for(int n=0;n<N1;n++)//注意撤销次序,先列后行,与设置相反
- {
- delete[] F[n];
- F[n]=NULL;
- }
- delete[] F;
- F=NULL;
- }
-
- BOOL CTestView::PreCreateWindow(CREATESTRUCT& cs)
- {
- // TODO: Modify the Window class or styles here by modifying
- // the CREATESTRUCT cs
-
- return CView::PreCreateWindow(cs);
- }
-
- /
- // CTestView drawing
-
- void CTestView::OnDraw(CDC* pDC)
- {
- CTestDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
- // TODO: add draw code for native data here
- DoubleBuffer();
- }
-
- /
- // CTestView printing
-
- BOOL CTestView::OnPreparePrinting(CPrintInfo* pInfo)
- {
- // default preparation
- return DoPreparePrinting(pInfo);
- }
-
- void CTestView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
- {
- // TODO: add extra initialization before printing
- }
-
- void CTestView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
- {
- // TODO: add cleanup after printing
- }
-
- /
- // CTestView diagnostics
-
- #ifdef _DEBUG
- void CTestView::AssertValid() const
- {
- CView::AssertValid();
- }
-
- void CTestView::Dump(CDumpContext& dc) const
- {
- CView::Dump(dc);
- }
-
- CTestDoc* CTestView::GetDocument() // non-debug version is inline
- {
- ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTestDoc)));
- return (CTestDoc*)m_pDocument;
- }
- #endif //_DEBUG
-
- /
- // CTestView message handlers
- void CTestView::ReadPoint()//读入点坐标
- {
- int gafa = 4, gbeta = 4;//面片夹角
- N1 = 180 / gafa, N2 = 360 / gbeta;//N1 为纬度区域,N2 为经度区域
- P = new CP3[(N1-1)*N2 + 2];//P 为球的顶点
- //纬度方向除南北极点外有"N1-1"个点,"2"代表南北极两个点
- double afa1, beta1, r = 300;//r 为球体半径
- //计算北极点坐标
- P[0].x = 0;
- P[0].y = r;
- P[0].z = 0;
- //按行循环计算球体上的点坐标
- for (int i = 0; i < N1-1; i++)
- {
- afa1 = (i + 1)*gafa*PI / 180;
- for (int j = 0; j < N2; j++)
- {
- beta1 = j * gbeta*PI / 180;
- P[i*N2 + j + 1].x = r * sin(afa1)*sin(beta1);
- P[i*N2 + j + 1].y = r * cos(afa1);
- P[i*N2 + j + 1].z = r * sin(afa1)*cos(beta1);
- }
- }
- //计算南极点坐标
- P[(N1-1)*N2 + 1].x = 0;
- P[(N1-1)*N2 + 1].y = -r;
- P[(N1-1)*N2 + 1].z = 0;
- }
-
-
- void CTestView::PointColor()//计算面片顶点颜色
- {
- for (int i = 0; i < (N1-1)*N2 + 2; i++)//遍历所有点
- {
- CVector PNormal(P[i]);//点的位置矢量代表共享该点的所有面的平均法矢量
- P[i].c = pLight->Lighting(ViewPoint, P[i], PNormal, pMaterial);//调用光照函数
- }
- }
-
- void CTestView::ReadFace()//读入面表
- {
- //设置二维动态数组
- F = new CFace *[N1];//设置行
- for (int n = 0; n < N1; n++)
- {
- F[n] = new CFace[N2];//设置列
- }
- for (int j = 0; j < N2; j++)//构造北极三角形面片
- {
- int tempj = j + 1;
- if (tempj == N2) tempj = 0;//面片的首尾连接
- int NorthIndex[3];//北极三角形面片索引号数组
- NorthIndex[0] = 0;
- NorthIndex[1] = j + 1;
- NorthIndex[2] = tempj + 1;
- F[0][j].SetEN(3);
- for (int k = 0; k < F[0][j].En; k++)
- {
- F[0][j].p[k] = NorthIndex[k];
- }
- }
- for (int i = 1; i < N1-1; i++)//构造球体四边形面片
- {
- for (int j = 0; j < N2; j++)
- {
- int tempi = i + 1;
- int tempj = j + 1;
- if (tempj == N2) tempj = 0;
- int BodyIndex[4];//球体四边形面片索引号数组
- BodyIndex[0] = (i-1)*N2 + j + 1;
- BodyIndex[1] = (tempi-1)*N2 + j + 1;
- BodyIndex[2] = (tempi-1)*N2 + tempj + 1;
- BodyIndex[3] = (i-1)*N2 + tempj + 1;
- F[i][j].SetEN(4);
- for (int k = 0; k < F[i][j].En; k++)
- {
- F[i][j].p[k] = BodyIndex[k];
- }
- }
- }
- for (int j = 0; j < N2; j++)//构造南极三角形面片
- {
- int tempj = j + 1;
- if (tempj == N2) tempj = 0;
- int SouthIndex[3];//南极三角形面片索引号数组
- SouthIndex[0] = (N1-2)*N2 + j + 1;
- SouthIndex[1] = (N1-1)*N2 + 1;
- SouthIndex[2] = (N1-2)*N2 + tempj + 1;
- F[N1-1][j].SetEN(3);
- for (int k = 0; k < F[N1-1][j].En; k++)
- {
- F[N1-1][j].p[k] = SouthIndex[k];
- }
- }
- }
-
-
- void CTestView::InitParameter()
- {
- k[1]=sin(PI*Theta/180);//sin(theta)
- k[2]=sin(PI*Phi/180);//sin(phi)
- k[3]=cos(PI*Theta/180);//cos(theta)
- k[4]=cos(PI*Phi/180);//cos(phi)
- k[5]=k[2]*k[3];//sin(phi)*cos(theta)
- k[6]=k[2]*k[1];//sin(phi)*sin(theta)
- k[7]=k[4]*k[3];//cos(phi)*cos(theta)
- k[8]=k[4]*k[1];//cos(phi)*sin(theta)
- ViewPoint.x=R*k[6];//用户坐标系的视点球坐标
- ViewPoint.y=R*k[4];
- ViewPoint.z=R*k[5];
- }
-
- void CTestView::PerProject(CP3 P)
- {
- CP3 ViewP;
- ViewP.x=P.x*k[3]-P.z*k[1];//观察坐标系三维坐标
- ViewP.y=-P.x*k[8]+P.y*k[2]-P.z*k[7];
- ViewP.z=-P.x*k[6]-P.y*k[4]-P.z*k[5]+R;
- ViewP.c=P.c;
- ScreenP.x=d*ViewP.x/ViewP.z;//屏幕坐标系三维坐标
- ScreenP.y=ROUND(d*ViewP.y/ViewP.z);
- ScreenP.z=d*(R-ViewP.z)/ViewP.z;
- ScreenP.c=ViewP.c;
- }
-
- void CTestView::DoubleBuffer()//双缓冲
- {
- CDC* pDC=GetDC();
- CRect rect;//定义客户区
- GetClientRect(&rect);//获得客户区的大小
- pDC->SetMapMode(MM_ANISOTROPIC);//pDC自定义坐标系
- pDC->SetWindowExt(rect.Width(),rect.Height());//设置窗口范围
- pDC->SetViewportExt(rect.Width(),-rect.Height());//x轴水平向右,y轴垂直向上
- pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);//屏幕中心为原点
- CDC MemDC;//内存DC
- CBitmap NewBitmap,*pOldBitmap;//内存中承载图像的临时位图
- MemDC.CreateCompatibleDC(pDC);//建立与屏幕pDC兼容的MemDC
- NewBitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//创建兼容位图
- pOldBitmap=MemDC.SelectObject(&NewBitmap);//将兼容位图选入MemDC
- MemDC.FillSolidRect(rect,RGB(128,0,0));//填充背景色
- MemDC.SetMapMode(MM_ANISOTROPIC);//MemDC自定义坐标系
- MemDC.SetWindowExt(rect.Width(),rect.Height());
- MemDC.SetViewportExt(rect.Width(),-rect.Height());
- MemDC.SetViewportOrg(rect.Width()/2,rect.Height()/2);
- DrawObject(&MemDC);
- pDC->BitBlt(-rect.Width()/2,-rect.Height()/2,rect.Width(),rect.Height(),&MemDC,-rect.Width()/2,-rect.Height()/2,SRCCOPY);//将内存位图拷贝到屏幕
- MemDC.SelectObject(pOldBitmap);//恢复位图
- NewBitmap.DeleteObject();//删除位图
- MemDC.DeleteDC();//删除MemDC
- ReleaseDC(pDC);//释放DC
- }
-
- void CTestView::DrawObject(CDC *pDC)//绘制球面
- {
- PointColor();
- CZBuffer *zbuf = new CZBuffer;//申请内存
- zbuf->InitDeepBuffer(800, 800, -1000);//深度初始化
- CPi3 Point3[3];//南北极顶点数组
- CPi3 Point4[4];//球体顶点数组
- for (int i = 0; i < N1; i++)
- {
- for (int j = 0; j < N2; j++)
- {
- CVector VS(P[F[i][j].p[1]], ViewPoint);//面的视矢量
- F[i][j].SetNormal(P[F[i][j].p[0]], P[F[i][j].p[1]], P[F[i][j].p[2]]);
- if (Dot(VS, F[i][j].Normal) >= 0)//背面剔除
- {
- if (F[i][j].En == 3)//三角形面片
- {
- for (int m = 0; m < F[i][j].En; m++)
- {
- PerProject(P[F[i][j].p[m]]);
- Point3[m] = ScreenP;
- }
- zbuf->SetPoint(Point3, 3);//设置顶点
- zbuf->CreateBucket();//建立桶表
- zbuf->CreateEdge();//建立边表
- zbuf->Gouraud(pDC);//填充三角形
- zbuf->ClearMemory();//内存清理
- }
- else//四边形面片
- {
- for (int m = 0; m < F[i][j].En; m++)
- {
- PerProject(P[F[i][j].p[m]]);
- Point4[m] = ScreenP;
- }
- zbuf->SetPoint(Point4, 4);//设置顶点
- zbuf->CreateBucket();//建立桶表
- zbuf->CreateEdge();//建立边表
- zbuf->Gouraud(pDC);//填充四边形
- zbuf->ClearMemory();//内存清理
- }
- }
- }
- }
- delete zbuf;//释放内存
- }
-
- void CTestView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
- {
- // TODO: Add your message handler code here and/or call default
- if(!Play)
- {
- switch(nChar)
- {
- case VK_UP:
- afa=-5;
- tran.RotateX(afa);
- break;
- case VK_DOWN:
- afa=5;
- tran.RotateX(afa);
- break;
- case VK_LEFT:
- beta=-5;
- tran.RotateY(beta);
- break;
- case VK_RIGHT:
- beta=5;
- tran.RotateY(beta);
- break;
- default:
- break;
- }
- Invalidate(FALSE);
- }
- CView::OnKeyDown(nChar, nRepCnt, nFlags);
- }
-
- void CTestView::OnMENUPlay()
- {
- // TODO: Add your command handler code here
- Play=Play?FALSE:TRUE;
- if (Play)//设置定时器
- SetTimer(1,15,NULL);
- else
- KillTimer(1);
- }
-
- void CTestView::OnTimer(UINT nIDEvent)
- {
- // TODO: Add your message handler code here and/or call default
- afa=5;beta=5;
- tran.RotateX(afa);
- tran.RotateY(beta);
- Invalidate(FALSE);
- CView::OnTimer(nIDEvent);
- }
-
-
- void CTestView::OnUpdateMENUPlay(CCmdUI* pCmdUI)//动画按钮状态函数
- {
- // TODO: Add your command update UI handler code here
- if(Play)
- {
- pCmdUI->SetCheck(TRUE);
- pCmdUI->SetText("停止");
- }
- else
- {
- pCmdUI->SetCheck(FALSE);
- pCmdUI->SetText("播放");
- }
- }
-
- BOOL CTestView::OnEraseBkgnd(CDC* pDC)//禁止背景刷新
- {
- // TODO: Add your message handler code here and/or call default
- return TRUE;
- }
-
- void CTestView::OnInitialUpdate()
- {
- CView::OnInitialUpdate();
- // TODO: Add your specialized code here and/or call the base class
- ReadPoint();
- ReadFace();
- tran.SetMat(P,(N1-1)*N2+2);
- InitParameter();
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。