OpenGL下的场景层次化渲染

目前使用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()。其功能是前者把当前所有的矩阵栈都压入堆栈,后者刚好相反。先看下面的例子:

//对整个货车做运动变换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物体的绘制工 作。这种思路对于大型的复杂场景的绘制尤为有用。







你可能感兴趣的:(OpenGL资料)