1、OpenGL学习脚印 关于gluLookAt函数的理解OpenGL学习脚印: 关于gluLookAt函数的理解写在前面 本节借助gluLookAt函数,推导世界坐标转换到照相机坐标的一种方法,重点在于理解UVN相机坐标系,以及变换矩阵的推导。限于笔者水平,如果错误 请纠正我。 gluLookAt函数提供给用户完成模式变换(model-view transformation)中,在将模型坐标系转换都世界坐标系后,进行世界坐标系到照相机坐标系的转换。实际上,照相机的定位也是在世界坐标系下定 义的,这里的转换,可以理解为:从照相机的角度解释世界坐标系中物体的坐标。通过构造一个UVN坐标系来简化这一转
2、换。先直观感受下UVN,UVN坐标系中的照相机模型如下图所示:借助下图正式定义UVN相机坐标系: 与UVN相关的概念包括: 相机位置,或者叫做视点(eyepoint): 观察参考点 (View Reference Point) 相机镜头方向,通过观察平面的法向量指定: 观察平面法向量VPN (View Plane Normal) 相机顶部正朝向:VUV (View Up Vector)形象的表达为: gluLookAt函数原型为: cpp view plaincopyprint?1. voidgluLookAt(GLdoubleeyeX,GLdoubleeyeY,GLdoubleeyeZ,2.
3、 3. GLdoublecenterX,GLdoublecenterY,GLdoublecenterZ,4. 5. GLdoubleupX,GLdoubleupY,GLdoubleupZ);官网关于此函数的描述:gluLookAt 通过指定一个视点、表面场景中心的参考点以及up向量来构造一个视变换矩阵。这个矩阵将代表场景中心的参考点映射到-Z轴,视点映射成为原点。当使用一个特定的投影矩阵时,场景的中心就映射到视口的中心。类似地,由up向量描述的方向投影到投影平面成为+y轴,这样它在视口中向上指向。up向量必须不能与从视点到参考点的直线平行。 那么如何确定u-v-n坐标系呢?计算公式如下: 这里
4、需要注意: OpenGL中使用的相机坐标系是右手坐标系,UVN坐标系是左手坐标系。在构造实际变换矩阵的过程中,OpenGL需要将-n轴翻转为相机坐标系的+z轴,uv轴定位相机坐标系的+x和+y轴。这与推导相机变换矩阵一文最后的结果矩阵有所不同。如何构造视变换矩阵?视变换就是在相机坐标系下解释世界坐标系下的点。而这个变换矩阵的构造,可以看做将相机坐标系变换到与原来的世界坐标系重合。而将世界坐标系变换到与相机坐标系重合,可以看做是这个所求变换的逆过程。 将世界坐标系变换到与相机坐标系重合,实际上进行了两个步骤:第一步将世界坐标系旋转一定角度记作变换R,再将世界坐标系平移到 视点位置记作T,那么这个
5、变换矩阵记为M=TR。要将世界坐标系的点变换到照相机坐标系下,需要使用矩阵M的逆矩阵,即: inverse(M)=inverse(R)*inverse(T)。即所求变换矩阵为inverse(M)。平移矩阵的逆矩阵形式简单,就是取平移量(eyex,eyey,eyez)的相反数,即:那么现在的关键是如何求出旋转矩阵R?上面我们构造的UVN坐标系u-v-n3个基向量可以构造成矩阵:注意这里对n轴进行了翻转,构成右手照相机坐标系。怎么看这个矩阵A呢,矩阵A实际上代表的就是一个旋转矩阵(从矩阵形式上看出)。旋转矩阵的一个特点就是它是正交矩阵,即有inverse(A) = transpose(A).(A-
6、1 = AT)很多教材和博客都说,这里A矩阵可以看做是将世界坐标系转换到与照相机坐标系重合时的旋转矩阵,这一点怎么理解呢?个人理解,矩阵A第四列为0,0,0,1,可以看做是世界坐标系和照相机坐标系原点重合;根据OpenGL学习脚印: 理解坐标系及坐标变换(上) 中所讲,矩阵前3列即变换后的基向量,那么这个基向量(都是单位向量)是如何计算出来的呢?就是通过旋转原来的世界坐标系的基向量来构造的。因此,可以说矩阵A代表的就是将世界坐标系旋转到与相机坐标系重合时的旋转矩阵R,即R = A。则inverse(R) = inverse(A) = transpose(A) 即为:所以gluLookAt所求变
7、换矩阵inverse(M)为:gluLookAt的默认值是(0, 0, 0, 0, 0,-1, 0, 1, 0);通过计算可得出:u=(1,0,0),v=(0,1,0),n=(0,0,-1),这样构成的矩阵M-1即为单位矩阵。下面通过代码来验证下结论。代码绘制一个立方体,设置为透视投影,并通过gluLookAt设置相机方位来查看立方体。注意,为了便于观察视变换矩阵,这里并没有进行其他模型变换;手动计算矩阵时使用了数学库glm来进行向量点积和叉积运算。 cpp view plaincopyprint?1. /计算gluLookAt矩阵2. 3. #include4. #include5. #in
8、clude6. #include7. #pragmacomment(lib,freeglut.lib)8. #pragmacomment(lib,glew32.lib)9. 10. voiduserInit();11. voiddisplay(void);12. voidkeyboardAction(unsignedcharkey,intx,inty);13. voidreshape(intw,inth);14. 15. intmain(intargc,char*argv)16. 17. glutInit(&argc,argv);/初始化GLUT18. 19. glutInitDisplayM
9、ode(GLUT_RGBA|GLUT_DOUBLE);20. glutInitWindowPosition(100,100);21. glutInitWindowSize(512,512);22. glutCreateWindow(gluLookAtdemo);23. 24. glewInit();/使用GLEW时,使用该函数初始化GLEW25. userInit();/自定义的初始化函数26. glutReshapeFunc(reshape);27. glutDisplayFunc(display);28. glutKeyboardFunc(keyboardAction);29. glutM
10、ainLoop();30. return0;31. 32. /自定义初始化函数33. voiduserInit()34. 35. glClearColor(0.0,0.0,0.0,0.0);36. glColor4f(0.6f,0.5f,0.0,0.0);37. 38. /设置视变换矩阵39. voidsetViewMatrix(GLdouble*theMatrix,GLdoubleeyex,GLdoubleeyey,GLdoubleeyez,40. GLdoubletargetx,GLdoubletargety,GLdoubletargetz,41. GLdoublevupx,GLdoubl
11、evupy,GLdoublevupz)42. 43. glm:vec3eye(eyex,eyey,eyez),target(targetx,targety,targetz),vup(vupx,vupy,vupz);44. /构造n轴45. glm:vec3nvec(target-eye);46. nvec=glm:normalize(nvec);47. /构造u轴48. vup=glm:normalize(vup);49. glm:vec3uvec=glm:cross(nvec,vup);50. uvec=glm:normalize(uvec);51. /构造v轴52. glm:vec3vve
12、c=glm:cross(uvec,nvec);53. vvec=glm:normalize(vvec);54. /设置4x4矩阵55. memset(theMatrix,0,sizeof(GLdouble)*16);56. 57. theMatrix0=uvec.x;58. theMatrix4=uvec.y;59. theMatrix8=uvec.z;60. theMatrix12=-glm:dot(eye,uvec);61. 62. theMatrix1=vvec.x;63. theMatrix5=vvec.y;64. theMatrix9=vvec.z;65. theMatrix13=-
13、glm:dot(eye,vvec);66. 67. /注意这行数据68. theMatrix2=-nvec.x;69. theMatrix6=-nvec.y;70. theMatrix10=-nvec.z;71. theMatrix14=glm:dot(eye,nvec);72. 73. theMatrix15=1.0;74. 75. voidreshape(intw,inth)76. 77. glViewport(0,0,GLsizei(w),GLsizei(h);78. glMatrixMode(GL_PROJECTION);79. glLoadIdentity();80. gluPers
14、pective(60.0,(GLfloat)w/(GLfloat)h,1.0,10.0);81. glMatrixMode(GL_MODELVIEW);82. glLoadIdentity();83. 84. /gluLookAt(2.0,0.0,1.8,0.0,0.0,0.0,0.0,1.0,0.0);85. 86. /手动构造视变换矩阵87. GLdoubletheMatrix16;88. setViewMatrix(theMatrix,2.0,0.0,1.8,0.0,0.0,0.0,0.0,1.0,0.0);89. glMultMatrixd(theMatrix);90. 91. /打印
15、当前模视变换矩阵内容92. GLdoublemodelViewMat16;93. glGetDoublev(GL_MODELVIEW_MATRIX,modelViewMat);94. for(inti=0;i4;i+)95. for(intj=0;j4;j+)96. 97. fprintf(stdout,%-.4ft,modelViewMati+4*j);98. if(j+1)%4=0)fprintf(stdout,n);99. 100. 101. /绘制回调函数102. voiddisplay(void)103. 104. glClear(GL_COLOR_BUFFER_BIT);/清除颜色
16、缓存105. glLineWidth(2.0);106. glutWireCube(1.0);107. glutSwapBuffers();108. 109. /键盘按键回调函数110. voidkeyboardAction(unsignedcharkey,intx,inty)111. 112. switch(key)113. case033:/Escapekey114. caseq:caseQ:115. exit(EXIT_SUCCESS);116. break;117. 118. 使用gluLookAt如下图左右所示:手动计算视变换矩阵,效果如下图所示:可以看出两者是一样的,二者的视变换矩阵打印出来均为:0.6690 0.0000 -0.7433 0.00000.0000 1.0000 0.0000 0.00000.7433 0.0000 0.6690 -2.69070.0000 0.0000 0.0000 1.0000至此证明了上述推导的矩阵确实为OpenGL中使用的视变换矩阵。
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1