目前使用VC6.0+OpenGL图形库已经逐渐成为开发3D应用程序的主流,但是在使用OpenGL图形库中会涉及到很多2D或者3D坐标,正确理解这 些坐标的含义是使用OpenGL开发应用程序的前提,另外对于一个复杂的3D场景如果使用程序的手段来进行有效的渲染也是至关重要的。本文主要探讨这两个 方面的问题。
width="360" scrolling="no" height="300" frameborder="0" align="right" src="http://images.chinabyte.com/adjs/iframe-pip/y-software-pip.html" marginheight="0" marginwidth="0"> 1. 相关基础知识
要渲染真实世界的3D物体,首先必须了解OpenGL里面的坐标系统。OpenGL是使用顶点(Vertex)来描述几何物体的,最开始几何物体处于世 界坐标之下,然后所有的顶点都通过模型视图变换、投影变换以及视口变换来转换为屏幕坐标。通过这种流水线似的作业,3D场景就可以在屏幕上展现出来了。
世界坐标系统遵循右手定则,即X轴是从左至右,Y轴由下至上,Z轴是垂直于屏幕由里到外。这样在OpenGL里面使用的坐标值都是在这样的一个座标系统之下定位的,然后通过一系列的变换转换为人眼坐标,进而最后转换为屏幕坐标(2D坐标)。
2. 场景的层次化渲染
对于一个复杂的场景,如果才能有效的进行渲染呢?这里就借助一个示例来进行说明。
例子是这样的,需要在一个SDI窗口下绘制如下的一个2D的货车(实际上在进行场景层次化渲染这方面,2D渲染的思路与3D渲染思路是一样的)
实现的思路首先应该是对这个货车分解几个独立的子实体:货车主体(前面那个大矩形)、货车主体车轮、拖车主体(后面的矩形)、拖车主体车轮。那么整个货 车在运动的时候应该是以主货车为运动的主体,其他的子实体都对主货车做相对运动,这就好比是爸爸牵着儿子走路一样,整体上爸爸和儿子是一起往前走的,但是 就局部来说,儿子可以绕爸爸做四周环绕运动。所以在利用OpenGL渲染比较复杂的3D物体时,首先要分清楚此3D物体的自由度的个数,然后分离出一个主 体部分,其他部分只是对这个主体部分做相对运动即可。这样的思路在渲染复杂的机器人的时候也应用到了。
那么在OpenGL里面如果实现各个实体之间的相对运动呢?事实上OpenGL里面提供了这样的一对函数:glPushMatrix()和glPopMatrix()。其功能是前者把当前所有的矩阵栈都压入堆栈,后者刚好相反。先看下面的例子:
上面代码看出来,在第一对glPushMatrix()和glPopMatrix()之间绘制的货车主体不会影响到第二对glPushMatrix() 和glPopMatrix()里面绘制的拖车主体,也就是说,平行的两对glPushMatrix()和glPopMatrix()互相不影响。但是变换 A却可以影响货车主体和拖车主体,因为它们是上下级的关系,而不是平行的关系。
所以从上面的分析中可以看出,可以利用glPushMatrix()和glPopMatrix()来实现主体和子体之间的相对运动,而且子体之间的绘制也不会相互影响,基于这样的思想,可以编制如下代码:(这里只列出主体代码DrawLorry和DrawWheel)
3.小结:
前面的分析看出,无论是对复杂的场景还是对多DOFs(自由度的简称)的物体来说,使用分解的方式将复杂物体分解为相对独立的子实体(对于更加复杂的场 景需要建立场景的层次树),并结合glPushMatrix()和glPopMatrix()来实现对各个子实体的绘制,进而实现整个3D物体的绘制工 作。这种思路对于大型的复杂场景的绘制尤为有用。
width="360" scrolling="no" height="300" frameborder="0" align="right" src="http://images.chinabyte.com/adjs/iframe-pip/y-software-pip.html" marginheight="0" marginwidth="0"> 1. 相关基础知识
要渲染真实世界的3D物体,首先必须了解OpenGL里面的坐标系统。OpenGL是使用顶点(Vertex)来描述几何物体的,最开始几何物体处于世 界坐标之下,然后所有的顶点都通过模型视图变换、投影变换以及视口变换来转换为屏幕坐标。通过这种流水线似的作业,3D场景就可以在屏幕上展现出来了。
世界坐标系统遵循右手定则,即X轴是从左至右,Y轴由下至上,Z轴是垂直于屏幕由里到外。这样在OpenGL里面使用的坐标值都是在这样的一个座标系统之下定位的,然后通过一系列的变换转换为人眼坐标,进而最后转换为屏幕坐标(2D坐标)。
2. 场景的层次化渲染
对于一个复杂的场景,如果才能有效的进行渲染呢?这里就借助一个示例来进行说明。
例子是这样的,需要在一个SDI窗口下绘制如下的一个2D的货车(实际上在进行场景层次化渲染这方面,2D渲染的思路与3D渲染思路是一样的)
实现的思路首先应该是对这个货车分解几个独立的子实体:货车主体(前面那个大矩形)、货车主体车轮、拖车主体(后面的矩形)、拖车主体车轮。那么整个货 车在运动的时候应该是以主货车为运动的主体,其他的子实体都对主货车做相对运动,这就好比是爸爸牵着儿子走路一样,整体上爸爸和儿子是一起往前走的,但是 就局部来说,儿子可以绕爸爸做四周环绕运动。所以在利用OpenGL渲染比较复杂的3D物体时,首先要分清楚此3D物体的自由度的个数,然后分离出一个主 体部分,其他部分只是对这个主体部分做相对运动即可。这样的思路在渲染复杂的机器人的时候也应用到了。
那么在OpenGL里面如果实现各个实体之间的相对运动呢?事实上OpenGL里面提供了这样的一对函数:glPushMatrix()和glPopMatrix()。其功能是前者把当前所有的矩阵栈都压入堆栈,后者刚好相反。先看下面的例子:
//对整个货车做运动变换A 变换A glPushMatrix(); //绘制货车主体 glPopMatrix(); glPushMatrix(); //绘制拖车主体 glPopMatrix(); |
上面代码看出来,在第一对glPushMatrix()和glPopMatrix()之间绘制的货车主体不会影响到第二对glPushMatrix() 和glPopMatrix()里面绘制的拖车主体,也就是说,平行的两对glPushMatrix()和glPopMatrix()互相不影响。但是变换 A却可以影响货车主体和拖车主体,因为它们是上下级的关系,而不是平行的关系。
所以从上面的分析中可以看出,可以利用glPushMatrix()和glPopMatrix()来实现主体和子体之间的相对运动,而且子体之间的绘制也不会相互影响,基于这样的思想,可以编制如下代码:(这里只列出主体代码DrawLorry和DrawWheel)
//m_rot 和m_trans分别表示轮胎的旋转量和整个货车在X轴上的平移量,已经在有关//函数里面进行了初始化 void CTerraintestView::DrawLorry() {//控制货车的运动 //这里施加的几何变换将影响到所有的下面绘制的物体 glTranslatef(m_trans,0,0); glColor3f(1.0,0.5,0); //绘制货车 glBegin(GL_POLYGON ); glVertex2f(-0.3f,0.2f); glVertex2f(-0.3f,-0.2f); glVertex2f(0.3f,-0.2f); glVertex2f(0.3f,0.2f); glEnd(); //绘制拉绳 glBegin(GL_LINES ); glVertex2f(-0.3,0); glVertex2f(-0.6,0); glEnd(); //绘制货车轮胎 glPushMatrix(); glTranslatef(0,-0.3f,0); glTranslatef(-0.2,0,0); glPushMatrix(); glRotatef(m_rot,0,0,1); DrawWheel(0.1); glPopMatrix(); glTranslatef(0.4,0,0); glRotatef(m_rot,0,0,1); DrawWheel(0.1); glPopMatrix(); glPushMatrix(); //总比货车滞后0.3 glTranslatef(-0.6,0,0); //绘制后面的拖车 glBegin(GL_POLYGON); glVertex2f(-0.2f,0.2f); glVertex2f(-0.2f,-0.2f); glVertex2f(0.2f,-0.2f); glVertex2f(0.2f,0.2f); glEnd(); // 绘制拖车的轮胎 glPushMatrix(); glTranslatef(0,-0.3f,0); glTranslatef(-0.1,0,0); DrawWheel(0.1); glTranslatef(0.2,0,0); DrawWheel(0.1); glPopMatrix(); glPopMatrix(); } void CTerraintestView::DrawWheel(float radius) {//绘制轮胎 float step=0.05; float i; float x,y; glBegin(GL_LINE_STRIP ); glVertex2f(radius,0); for (i=step;i<360.0;i+=step) { //计算两个坐标 x=radius*cos(i*3.1415926/180.0); y=radius*sin(i*3.1415926/180.0); glVertex2f(x,y); } glEnd(); } |
3.小结:
前面的分析看出,无论是对复杂的场景还是对多DOFs(自由度的简称)的物体来说,使用分解的方式将复杂物体分解为相对独立的子实体(对于更加复杂的场 景需要建立场景的层次树),并结合glPushMatrix()和glPopMatrix()来实现对各个子实体的绘制,进而实现整个3D物体的绘制工 作。这种思路对于大型的复杂场景的绘制尤为有用。