OGL glViewport glFrustum gluPerspective gluLookAt glTranslatef glRotatef glScalef感悟

来源:互联网 发布:360防蹭网软件 编辑:程序博客网 时间:2024/05/16 14:07

通过分析代码可以得到如下感悟:
1.变换顺序:OGL中因为是列主序矩阵,需要左乘下一步变换才得到矩阵变换结果。所以视口变换,投影变换,视图变换,模型变换的顺序进行设置的;所以变换顺序一定要和D3D区分好。
2.变换设置:OGL中是用视图模型矩阵堆栈,投影矩阵堆栈进行变换的,每个矩阵的设置其实包含了生成一个矩阵,以及和当前栈顶矩阵相乘,得到当前变换;所以变换设置glLoadIdentify,glPushMatrix,glPopMatrix很重要。
3.变换细节:Ogl变换和glu变换函数的细节区分,见下面。gluLookAt只是封装了平移旋转变换(OGL可以用本地坐标系思考)封装了下。glFrustum是透视投影,用了近裁剪面的参数。其它细节见代码。变换矩阵函数都是先生成一个矩阵,然后和当前矩阵堆栈栈顶矩阵相乘,作为当前矩阵如果有物体绘制网格就要和当前矩阵相乘作为顶点变换结果。

4.裁剪阶段:裁剪是在4D 坐标系中进行,4D是为了表达更多形式和统一x,y,z方便裁剪透视投影矩阵也是到这里,后面硬件进行透视除法到NDC坐标系(设备坐标系)OGL x在[-1,1], y在[-1,1], z在[-1,1];D3D x在[-1,1], y在[-1,1], z在[0,1]);,虽然硬件裁剪掉了但是也浪费了提交到GPU Vertex, Shader进行的变换消耗和裁剪消耗;所以对于大型场景场景管理渲染技术是很重要的。gluPerspective只是变换到4D空间,硬件会进行裁剪和透视除法到DNC,正交投影直接变换到DNC坐标系中

5.深度缓存存储阶段:在视口变换期间会将NDC坐标中的z坐标存入深度缓存中,OGL中在设备坐标[-1,1]中的z值也要经过glDepthRange(near,far),near默认是0,far默认是1,将其变换到[0,1]放入深度缓存中,用于后面的深度检测剔除背面。

代码实例:


#include <GL/glut.h>#include <stdlib.h>void init(void) {   glClearColor (0.0, 0.0, 0.0, 0.0);   glShadeModel (GL_FLAT);   GLint modelViewMatrixStacks;
// 32 都是4X4的列式矩阵,视图矩阵可以不用glLoadIdentity ();但一般使用避免窗口变化导致异常。
glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &modelViewMatrixStacks);
// 4 一定需要glLoadIdentity (),当需要绘制2D文本时候,可以Push当前Load单位,然后使用2D投影。当投影变换时,很可能需要调整模型视图矩阵得到想要结果。
GLint perspectiveMatrixStacks; glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, &perspectiveMatrixStacks);
}void display(void){   glClear (GL_COLOR_BUFFER_BIT);   glColor3f (1.0, 1.0, 1.0);   //OGL中都是和当前矩阵相乘作为变换矩阵,所以这里要清理下;   //虽然模型视图矩阵栈中调用gluLookAt会初始化单位矩阵但是屏幕大小改变时候,还是会出现错乱所以视图变换前glLoadIdentity是好习惯。   //glLoadMatrix指定的矩阵,glMultMatrix是乘以当前矩阵作为结果矩阵;   //glLoadTransposeMatrix,glMultTransposeMatrix是行主序矩阵类似D3D,转置矩阵刚好把列主序矩阵变换为行主序矩阵。   glLoadIdentity ();             /* clear the matrix */           /* viewing transformation  */   // gluLookAt是将摄像机的平移旋转的逆进行了封装,可以定义自己的视图转换矩阵。   // 因为OGL是列式矩阵,所以矩阵乘法顺序变反,所以投影变换,视图转换矩阵要在前面设置。左乘下一步变换才得到结果。   gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);   //glTranslatef( .0, .0f, -5.0f); // 变换坐标系等于相反的方向变换物体,故这里模型和视图变换放到一起   // glTranslate glRotate glScale生成恰当的矩阵,且调用了glMulteMatrix作为结果;   // 这些变换矩阵放置到display list中比每帧计算更快   glRotatef(45, 0, 0, 1); // 绕原点到指定点的直线(指定轴),逆时针旋转45度   glPushMatrix();   // 若当前物体点用局部坐标系统刻画那么在局部坐标系中缩放   // 使用缩放会降低光照计算效率,因为要重新计算物体表面的法向量; 一般不缩放为0而是使用投影矩阵而不是模型视图矩阵(例如平面阴影)   glScalef (1.0, 2.0, 1.0);      /* modeling transformation */    // OGL的矩阵乘法,默认是列主序矩阵,M[i][j]是i列j行,矩阵乘法是左乘当前矩阵或向量   glutWireTeapot(1);   glPopMatrix();   glTranslatef(-2.0, -2.0f, 0);   glScalef(-1.0, 2.0, 1.0);   glutWireTeapot(1);   glFlush ();}void reshape (int w, int h){// 视口变换,将[-1,-1,-1]->[1,1,1]空间变换到OGL左下角[0,0]右上角[1,1]中   glViewport (0, 0, (GLsizei) w, (GLsizei) h);    // 透视投影变换到裁剪4D空间(裁剪掉视景体外的),变换到视口变换前硬件会进行透视除法/w,建筑师需要实际大小用gluOrtho2D   glMatrixMode (GL_PROJECTION);   glLoadIdentity ();   // glFrustum上的left, right, bottom, top是近裁剪面的坐标参数,zNear,zFar是距离坐标的相反数;该函数会生成透视矩阵且和当前矩阵相乘。   //glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);   // gluPerspective是glu提供的函数沿x,y轴均是对称的,没有glFrustum灵活,但是更好用。fovy在[0,180]内VR却要360度,aspect是宽比高。   // zNear,zFar是近截面,远截面距离(视图坐标中的距离);创建透视投影矩阵和乘以当前矩阵,变换到4D裁剪空间中,硬件除以w变换到NDC坐标中。   gluPerspective(90.0, w / h, 1, 1000.0);      // 在建筑绘图或CAD中需要的2D投影,是一个长方体的左右,上下,前后边界线值;zNear,zFar不需要一定是正数,负数,0都可以但是不要相同。   // 因为正交投影中生成的正交矩阵,将物体直接变换到NDC 3D坐标系中,不需要到4D裁剪空间中和进行透视除法到3D。   // gluOrtho2D是glu提供的版本,和glOrtho相比,只是zNear默认指定为-1,zFar默认指定为1.   //glOrtho(-10.0, 10.0, -10.0, 10.0, 1.5, 20.0);   // 所有物体变换到NDC坐标后(无论是透视还是正交投影),在OGL中x属于[-1,1],y属于[-1,1],z属于[-1,1]   // 不在此区间内的物体都会被硬件快速裁剪掉(而不是视锥体中裁剪),D3D中也类似的只是z属于[0,1]。   // 所以变换和裁剪都是消耗性能的,大场景中需要场景管理技术。      // 切换到模型视图变换   glMatrixMode (GL_MODELVIEW);}void keyboard(unsigned char key, int x, int y){   switch (key) {      case 27:         exit(0);         break;   }}int main(int argc, char** argv){   glutInit(&argc, argv);   glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);   glutInitWindowSize (500, 500);    glutInitWindowPosition (100, 100);   glutCreateWindow (argv[0]);   init ();   glutDisplayFunc(display);    glutReshapeFunc(reshape);   glutKeyboardFunc(keyboard);   glutMainLoop();   return 0;}

0 0
原创粉丝点击