当前位置:   article > 正文

计算机图形学实验四球体 Gouraud 光照模型_gourand光照模型

gourand光照模型
  • 实验目的

(1)掌握双线性光强插值模型。

二、实验步骤

(1)建立三维坐标系 Oxyz,原点位于屏幕客户区中心,x 轴水平向右为正,

y 轴铅直向上为正,z 轴垂直于屏幕指向观察者。

(2)绘制体心和坐标系中心重合的球体表面,使用 ZBuffer 消隐算法进行消

隐。使用 Z‐Buffer 算法对球面进行深度消隐,然后使用有效边表算法进行填充。

为了减少渲染的面片数,先使用凸多面体消隐算法对球体不可见面片进行剔除。

然后使用 Z‐Buffer 算法对可见面片进行消隐,最后使用有效边表算法进行填充。

使用 CZBuffer 类对象 zbuf 进行处理,区分了三角面片和四边形面片两种情况。

(3)使用单点光源对球体进行照射生成 Gouraud 光照模型,光源位置位于

球体右上方。使用 CTestView 类的构造函数设置默认的光源颜色和材质颜色。场景中使用了一个点光源,光源颜色为白色,位于屏幕右上方。物体材质为红色。

(4)使用键盘方向键旋转球体。

(5)使用动画按钮,播放或停止球体动画。

、实验结果

 

四、实验体会

在本次实验中,通过不断的探索和实践,我掌握了双线性光强插值模型的构造,知道怎么使用ZBuffer消隐算法进行消隐,怎么使用单点光源对球体进行照射生成Gourand光照模型,在这些过程中,我熟悉了光照模型、双线性颜色插值算法以及对有效边表填充算法更加熟练。虽然过程中遇到了许多困难,但在再次阅读教材理解教材后,以及跟同学讨论后,最终通过不断的努力将实验完成了,学会了将理论知识用在实践中,收获良多。

附录:源代码

创建的相应类

  1. #include "stdafx.h"
  2. #include "Test.h"
  3. #include "TestDoc.h"
  4. #include "TestView.h"
  5. #include "math.h"
  6. #define PI 3.1415926
  7. #define ROUND(x) int(x+0.5)
  8. #ifdef _DEBUG
  9. #define new DEBUG_NEW
  10. #undef THIS_FILE
  11. static char THIS_FILE[] = __FILE__;
  12. #endif
  13. /
  14. // CTestView
  15. IMPLEMENT_DYNCREATE(CTestView, CView)
  16. BEGIN_MESSAGE_MAP(CTestView, CView)
  17. //{{AFX_MSG_MAP(CTestView)
  18. ON_WM_KEYDOWN()
  19. ON_COMMAND(ID_MENUPlay, OnMENUPlay)
  20. ON_WM_TIMER()
  21. ON_UPDATE_COMMAND_UI(ID_MENUPlay, OnUpdateMENUPlay)
  22. ON_WM_ERASEBKGND()
  23. //}}AFX_MSG_MAP
  24. // Standard printing commands
  25. ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
  26. ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
  27. ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
  28. END_MESSAGE_MAP()
  29. /
  30. // CTestView construction/destruction
  31. CTestView::CTestView()
  32. {
  33. // TODO: add construction code here
  34. P=NULL;F=NULL;
  35. Play=FALSE;
  36. R=1000,d=800,Phi=90.0,Theta=0;
  37. LightNum=1;//光源个数
  38. pLight=new CLighting(LightNum);//一维光源动态数组
  39. pLight->Light[0].SetPosition(800,800,800);//设置光源位置坐标
  40. for(int i=0;i<LightNum;i++)
  41. {
  42. pLight->Light[i].L_Diffuse=CRGB(1.0,1.0,1.0);//光源的漫反射颜色
  43. pLight->Light[i].L_Specular=CRGB(1.0,1.0,1.0);//光源镜面高光颜色
  44. pLight->Light[i].L_C0=1.0;//常数衰减系数
  45. pLight->Light[i].L_C1=0.0000001;//线性衰减系数
  46. pLight->Light[i].L_C2=0.00000001;//二次衰减系数
  47. pLight->Light[i].L_OnOff=TRUE;//光源开启
  48. }
  49. pMaterial=new CMaterial;//一维材质动态数组
  50. pMaterial->M_Ambient=CRGB(0.547,0.08,0.0);//材质对环境光的反射率
  51. pMaterial->M_Diffuse=CRGB(0.85,0.08,0.0);//材质对漫反射光的反射率
  52. pMaterial->M_Specular=CRGB(0.828,0.8,0.8);//材质对镜面反射光的反射率
  53. pMaterial->M_Emit=CRGB(0.2,0.0,0.0);//材质自身发散的颜色
  54. pMaterial->M_Exp=20.0;//高光指数
  55. }
  56. CTestView::~CTestView()
  57. {
  58. if(pLight!=NULL)
  59. {
  60. delete [] pLight;
  61. pLight=NULL;
  62. }
  63. if(pMaterial!=NULL)
  64. {
  65. delete pMaterial;
  66. pMaterial=NULL;
  67. }
  68. if(P!=NULL)
  69. {
  70. delete[] P;
  71. P=NULL;
  72. }
  73. for(int n=0;n<N1;n++)//注意撤销次序,先列后行,与设置相反
  74. {
  75. delete[] F[n];
  76. F[n]=NULL;
  77. }
  78. delete[] F;
  79. F=NULL;
  80. }
  81. BOOL CTestView::PreCreateWindow(CREATESTRUCT& cs)
  82. {
  83. // TODO: Modify the Window class or styles here by modifying
  84. // the CREATESTRUCT cs
  85. return CView::PreCreateWindow(cs);
  86. }
  87. /
  88. // CTestView drawing
  89. void CTestView::OnDraw(CDC* pDC)
  90. {
  91. CTestDoc* pDoc = GetDocument();
  92. ASSERT_VALID(pDoc);
  93. // TODO: add draw code for native data here
  94. DoubleBuffer();
  95. }
  96. /
  97. // CTestView printing
  98. BOOL CTestView::OnPreparePrinting(CPrintInfo* pInfo)
  99. {
  100. // default preparation
  101. return DoPreparePrinting(pInfo);
  102. }
  103. void CTestView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  104. {
  105. // TODO: add extra initialization before printing
  106. }
  107. void CTestView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  108. {
  109. // TODO: add cleanup after printing
  110. }
  111. /
  112. // CTestView diagnostics
  113. #ifdef _DEBUG
  114. void CTestView::AssertValid() const
  115. {
  116. CView::AssertValid();
  117. }
  118. void CTestView::Dump(CDumpContext& dc) const
  119. {
  120. CView::Dump(dc);
  121. }
  122. CTestDoc* CTestView::GetDocument() // non-debug version is inline
  123. {
  124. ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTestDoc)));
  125. return (CTestDoc*)m_pDocument;
  126. }
  127. #endif //_DEBUG
  128. /
  129. // CTestView message handlers
  130. void CTestView::ReadPoint()//读入点坐标
  131. {
  132. int gafa = 4, gbeta = 4;//面片夹角
  133. N1 = 180 / gafa, N2 = 360 / gbeta;//N1 为纬度区域,N2 为经度区域
  134. P = new CP3[(N1-1)*N2 + 2];//P 为球的顶点
  135. //纬度方向除南北极点外有"N1-1"个点,"2"代表南北极两个点
  136. double afa1, beta1, r = 300;//r 为球体半径
  137. //计算北极点坐标
  138. P[0].x = 0;
  139. P[0].y = r;
  140. P[0].z = 0;
  141. //按行循环计算球体上的点坐标
  142. for (int i = 0; i < N1-1; i++)
  143. {
  144. afa1 = (i + 1)*gafa*PI / 180;
  145. for (int j = 0; j < N2; j++)
  146. {
  147. beta1 = j * gbeta*PI / 180;
  148. P[i*N2 + j + 1].x = r * sin(afa1)*sin(beta1);
  149. P[i*N2 + j + 1].y = r * cos(afa1);
  150. P[i*N2 + j + 1].z = r * sin(afa1)*cos(beta1);
  151. }
  152. }
  153. //计算南极点坐标
  154. P[(N1-1)*N2 + 1].x = 0;
  155. P[(N1-1)*N2 + 1].y = -r;
  156. P[(N1-1)*N2 + 1].z = 0;
  157. }
  158. void CTestView::PointColor()//计算面片顶点颜色
  159. {
  160. for (int i = 0; i < (N1-1)*N2 + 2; i++)//遍历所有点
  161. {
  162. CVector PNormal(P[i]);//点的位置矢量代表共享该点的所有面的平均法矢量
  163. P[i].c = pLight->Lighting(ViewPoint, P[i], PNormal, pMaterial);//调用光照函数
  164. }
  165. }
  166. void CTestView::ReadFace()//读入面表
  167. {
  168. //设置二维动态数组
  169. F = new CFace *[N1];//设置行
  170. for (int n = 0; n < N1; n++)
  171. {
  172. F[n] = new CFace[N2];//设置列
  173. }
  174. for (int j = 0; j < N2; j++)//构造北极三角形面片
  175. {
  176. int tempj = j + 1;
  177. if (tempj == N2) tempj = 0;//面片的首尾连接
  178. int NorthIndex[3];//北极三角形面片索引号数组
  179. NorthIndex[0] = 0;
  180. NorthIndex[1] = j + 1;
  181. NorthIndex[2] = tempj + 1;
  182. F[0][j].SetEN(3);
  183. for (int k = 0; k < F[0][j].En; k++)
  184. {
  185. F[0][j].p[k] = NorthIndex[k];
  186. }
  187. }
  188. for (int i = 1; i < N1-1; i++)//构造球体四边形面片
  189. {
  190. for (int j = 0; j < N2; j++)
  191. {
  192. int tempi = i + 1;
  193. int tempj = j + 1;
  194. if (tempj == N2) tempj = 0;
  195. int BodyIndex[4];//球体四边形面片索引号数组
  196. BodyIndex[0] = (i-1)*N2 + j + 1;
  197. BodyIndex[1] = (tempi-1)*N2 + j + 1;
  198. BodyIndex[2] = (tempi-1)*N2 + tempj + 1;
  199. BodyIndex[3] = (i-1)*N2 + tempj + 1;
  200. F[i][j].SetEN(4);
  201. for (int k = 0; k < F[i][j].En; k++)
  202. {
  203. F[i][j].p[k] = BodyIndex[k];
  204. }
  205. }
  206. }
  207. for (int j = 0; j < N2; j++)//构造南极三角形面片
  208. {
  209. int tempj = j + 1;
  210. if (tempj == N2) tempj = 0;
  211. int SouthIndex[3];//南极三角形面片索引号数组
  212. SouthIndex[0] = (N1-2)*N2 + j + 1;
  213. SouthIndex[1] = (N1-1)*N2 + 1;
  214. SouthIndex[2] = (N1-2)*N2 + tempj + 1;
  215. F[N1-1][j].SetEN(3);
  216. for (int k = 0; k < F[N1-1][j].En; k++)
  217. {
  218. F[N1-1][j].p[k] = SouthIndex[k];
  219. }
  220. }
  221. }
  222. void CTestView::InitParameter()
  223. {
  224. k[1]=sin(PI*Theta/180);//sin(theta)
  225. k[2]=sin(PI*Phi/180);//sin(phi)
  226. k[3]=cos(PI*Theta/180);//cos(theta)
  227. k[4]=cos(PI*Phi/180);//cos(phi)
  228. k[5]=k[2]*k[3];//sin(phi)*cos(theta)
  229. k[6]=k[2]*k[1];//sin(phi)*sin(theta)
  230. k[7]=k[4]*k[3];//cos(phi)*cos(theta)
  231. k[8]=k[4]*k[1];//cos(phi)*sin(theta)
  232. ViewPoint.x=R*k[6];//用户坐标系的视点球坐标
  233. ViewPoint.y=R*k[4];
  234. ViewPoint.z=R*k[5];
  235. }
  236. void CTestView::PerProject(CP3 P)
  237. {
  238. CP3 ViewP;
  239. ViewP.x=P.x*k[3]-P.z*k[1];//观察坐标系三维坐标
  240. ViewP.y=-P.x*k[8]+P.y*k[2]-P.z*k[7];
  241. ViewP.z=-P.x*k[6]-P.y*k[4]-P.z*k[5]+R;
  242. ViewP.c=P.c;
  243. ScreenP.x=d*ViewP.x/ViewP.z;//屏幕坐标系三维坐标
  244. ScreenP.y=ROUND(d*ViewP.y/ViewP.z);
  245. ScreenP.z=d*(R-ViewP.z)/ViewP.z;
  246. ScreenP.c=ViewP.c;
  247. }
  248. void CTestView::DoubleBuffer()//双缓冲
  249. {
  250. CDC* pDC=GetDC();
  251. CRect rect;//定义客户区
  252. GetClientRect(&rect);//获得客户区的大小
  253. pDC->SetMapMode(MM_ANISOTROPIC);//pDC自定义坐标系
  254. pDC->SetWindowExt(rect.Width(),rect.Height());//设置窗口范围
  255. pDC->SetViewportExt(rect.Width(),-rect.Height());//x轴水平向右,y轴垂直向上
  256. pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);//屏幕中心为原点
  257. CDC MemDC;//内存DC
  258. CBitmap NewBitmap,*pOldBitmap;//内存中承载图像的临时位图
  259. MemDC.CreateCompatibleDC(pDC);//建立与屏幕pDC兼容的MemDC
  260. NewBitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//创建兼容位图
  261. pOldBitmap=MemDC.SelectObject(&NewBitmap);//将兼容位图选入MemDC
  262. MemDC.FillSolidRect(rect,RGB(128,0,0));//填充背景色
  263. MemDC.SetMapMode(MM_ANISOTROPIC);//MemDC自定义坐标系
  264. MemDC.SetWindowExt(rect.Width(),rect.Height());
  265. MemDC.SetViewportExt(rect.Width(),-rect.Height());
  266. MemDC.SetViewportOrg(rect.Width()/2,rect.Height()/2);
  267. DrawObject(&MemDC);
  268. pDC->BitBlt(-rect.Width()/2,-rect.Height()/2,rect.Width(),rect.Height(),&MemDC,-rect.Width()/2,-rect.Height()/2,SRCCOPY);//将内存位图拷贝到屏幕
  269. MemDC.SelectObject(pOldBitmap);//恢复位图
  270. NewBitmap.DeleteObject();//删除位图
  271. MemDC.DeleteDC();//删除MemDC
  272. ReleaseDC(pDC);//释放DC
  273. }
  274. void CTestView::DrawObject(CDC *pDC)//绘制球面
  275. {
  276. PointColor();
  277. CZBuffer *zbuf = new CZBuffer;//申请内存
  278. zbuf->InitDeepBuffer(800, 800, -1000);//深度初始化
  279. CPi3 Point3[3];//南北极顶点数组
  280. CPi3 Point4[4];//球体顶点数组
  281. for (int i = 0; i < N1; i++)
  282. {
  283. for (int j = 0; j < N2; j++)
  284. {
  285. CVector VS(P[F[i][j].p[1]], ViewPoint);//面的视矢量
  286. F[i][j].SetNormal(P[F[i][j].p[0]], P[F[i][j].p[1]], P[F[i][j].p[2]]);
  287. if (Dot(VS, F[i][j].Normal) >= 0)//背面剔除
  288. {
  289. if (F[i][j].En == 3)//三角形面片
  290. {
  291. for (int m = 0; m < F[i][j].En; m++)
  292. {
  293. PerProject(P[F[i][j].p[m]]);
  294. Point3[m] = ScreenP;
  295. }
  296. zbuf->SetPoint(Point3, 3);//设置顶点
  297. zbuf->CreateBucket();//建立桶表
  298. zbuf->CreateEdge();//建立边表
  299. zbuf->Gouraud(pDC);//填充三角形
  300. zbuf->ClearMemory();//内存清理
  301. }
  302. else//四边形面片
  303. {
  304. for (int m = 0; m < F[i][j].En; m++)
  305. {
  306. PerProject(P[F[i][j].p[m]]);
  307. Point4[m] = ScreenP;
  308. }
  309. zbuf->SetPoint(Point4, 4);//设置顶点
  310. zbuf->CreateBucket();//建立桶表
  311. zbuf->CreateEdge();//建立边表
  312. zbuf->Gouraud(pDC);//填充四边形
  313. zbuf->ClearMemory();//内存清理
  314. }
  315. }
  316. }
  317. }
  318. delete zbuf;//释放内存
  319. }
  320. void CTestView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  321. {
  322. // TODO: Add your message handler code here and/or call default
  323. if(!Play)
  324. {
  325. switch(nChar)
  326. {
  327. case VK_UP:
  328. afa=-5;
  329. tran.RotateX(afa);
  330. break;
  331. case VK_DOWN:
  332. afa=5;
  333. tran.RotateX(afa);
  334. break;
  335. case VK_LEFT:
  336. beta=-5;
  337. tran.RotateY(beta);
  338. break;
  339. case VK_RIGHT:
  340. beta=5;
  341. tran.RotateY(beta);
  342. break;
  343. default:
  344. break;
  345. }
  346. Invalidate(FALSE);
  347. }
  348. CView::OnKeyDown(nChar, nRepCnt, nFlags);
  349. }
  350. void CTestView::OnMENUPlay()
  351. {
  352. // TODO: Add your command handler code here
  353. Play=Play?FALSE:TRUE;
  354. if (Play)//设置定时器
  355. SetTimer(1,15,NULL);
  356. else
  357. KillTimer(1);
  358. }
  359. void CTestView::OnTimer(UINT nIDEvent)
  360. {
  361. // TODO: Add your message handler code here and/or call default
  362. afa=5;beta=5;
  363. tran.RotateX(afa);
  364. tran.RotateY(beta);
  365. Invalidate(FALSE);
  366. CView::OnTimer(nIDEvent);
  367. }
  368. void CTestView::OnUpdateMENUPlay(CCmdUI* pCmdUI)//动画按钮状态函数
  369. {
  370. // TODO: Add your command update UI handler code here
  371. if(Play)
  372. {
  373. pCmdUI->SetCheck(TRUE);
  374. pCmdUI->SetText("停止");
  375. }
  376. else
  377. {
  378. pCmdUI->SetCheck(FALSE);
  379. pCmdUI->SetText("播放");
  380. }
  381. }
  382. BOOL CTestView::OnEraseBkgnd(CDC* pDC)//禁止背景刷新
  383. {
  384. // TODO: Add your message handler code here and/or call default
  385. return TRUE;
  386. }
  387. void CTestView::OnInitialUpdate()
  388. {
  389. CView::OnInitialUpdate();
  390. // TODO: Add your specialized code here and/or call the base class
  391. ReadPoint();
  392. ReadFace();
  393. tran.SetMat(P,(N1-1)*N2+2);
  394. InitParameter();
  395. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/545224
推荐阅读
相关标签
  

闽ICP备14008679号