|
楼主 |
发表于 2005-7-10 16:11:18
|
显示全部楼层
四、OpenGL编程小结 1、如果要响应WM_SIZE消息,则一定要设置视口和矩阵模式。
2、尽量把你全部的画图工作在响应WM_PAINT消息时完成。
3、产生一个绘制环境要耗费大量的CPU时间,所以最好在程序中只产生一次,直到程序结束。
4、尽量把你的画图命令封装在文档类中,这样你就可以在不同的视类中使用相同的文档,节省你编程的工作量。
5、glBegin和glEnd一定要成对出现,这之间是对图元的绘制语句。
glPushMatrix()和glPopMatrix()也一定要成对出现。glPushMatrix()把当前的矩阵拷贝到栈中。当我们调用glPopMatrix时,最后压入栈的矩阵恢复为当前矩阵。使用glPushMatrix()可以精确地把当前矩阵保存下来,并用glPopMatrix把它恢复出来。这样我们就可以使用这个技术相对某个物体放置其他物体。例如下列语句只使用一个矩阵,就能产生两个矩形,并将它们成一定角度摆放。
glPushMatrix(); glTranslated( m_transX, m_transY, 0); glRotated( m_angle1, 0, 0, 1); glPushMatrix(); glTranslated( 90, 0, 0); glRotated( m_angle2, 0, 0, 1); glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glCallList(ArmPart);//ArmPart 且桓鼍卣竺? glPopMatrix(); glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glCallList(ArmPart); glPopMatrix(); | 6、 解决屏幕的闪烁问题。我们知道,在窗口中拖动一个图形的时候,由于边画边显示,会出现闪烁的现象。在GDI中解决这个问题较为复杂,通过在内存中生成一个内存DC,绘画时让画笔在内存DC中画,画完后一次用Bitblt将内存DC“贴”到显示器上,就可解决闪烁的问题。在OpenGL中,我们是通过双缓存来解决这个问题的。一般来说,双缓存在图形工作软件中是很普遍的。双缓存是两个缓存,一个前台缓存、一个后台缓存。绘图先在后台缓存中画,画完后,交换到前台缓存,这样就不会有闪烁现象了。通过以下步骤可以很容易地解决这个问题:
1) 要注意,GDI命令是没有设计双缓存的。我们首先把使用InvalidateRect(null)的地方改成InvalidateRect(NULL,FALSE)。这样做是使GDI的重画命令失效,由OpenGL的命令进行重画;
2) 将像素格式定义成支持双缓存的(注:PFD_DOUBLEBUFFER和PFD_SUPPORT_GDI只能取一个,两者相互冲突)。
pixelDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_STEREO_DONTCARE; | 3) 我们得告诉OpenGL在后台缓存中画图,在视类的OnSize()的最后一行加入:glDrawBuffer (GL_BACK);
4) 最后我们得把后台缓存的内容换到前台缓存中,在视类的OnPaint()的最后一行加入:SwapBuffers(dc.m_ ps.hdc)。
7、生成简单的三维图形。我们知道,三维和二维的坐标系统不同,三维的图形比二维的图形多一个z坐标。我们在生成简单的二维图形时,用的是gluOrtho2D;我们在生成三维图形时,需要两个远近裁剪平面,以生成透视效果。实际上,二维图形只是视线的近裁剪平面z= -1,远裁剪平面z=1;这样z坐标始终当作0,两者没有本质的差别。
在上述基础之上,我们只做简单的变化,就可以生成三维物体。
1) 首先,在OnSize()中,把gluOrtho2D(0.0, 500.0*aspect,0.0, 500.0)换成gluPerspective(60, aspect, 1, 10.0);这样就实现了三维透视坐标系的设置。该语句说明了视点在原点,透视角是60度,近裁剪面在z=1处,远裁剪面在z=10.0处。
2) 在RenderScene()中生成三维图形;实际上,它是由多边形组成的。下面就是一个三维多边形的例子:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, RedSurface) glBegin(GL_POLYGON); glNormal3d( 1.0, 0.0, 0.0); glVertex3d( 1.0, 1.0, 1.0); glVertex3d( 1.0, -1.0, 1.0); glVertex3d( 1.0, -1.0, -1.0); glVertex3d( 1.0, 1.0, -1.0); glEnd(); | 3) 我们使用glMaterialfv(GL_ FRONT_AND_BACK, GL_AMBIENT, RedSurface)这个函数来定义多边形的表面属性,为每一个平面的前后面设置环境颜色。当然,我们得定义光照模型,这只需在OnSize()的最后加上glEnable(GL_LIGHTING);RedSufFace是一个颜色分量数组,例如:RedSufFace[] ={1.0f,0.0f,0.0f};要定义某个平面的环境颜色,只需把glMaterialfv加在平面的定义前面即可,如上例所示。
4) Z缓冲区的问题:要使三维物体显得更流畅,前后各面的空间关系正确,一定得使用Z缓冲技术;否则,前后各面的位置就会相互重叠,不能正确显示。Z缓冲区存储物体每一个点的值,这个值表明此点离人眼的距离。Z缓冲需要占用大量的内存和CPU时间。启用Z缓冲只需在OnSize()的最后加上glEnable(GL_DEPTH_TEST);要记住:在每次重绘之前,应使用glClear(GL_DEPTH_BUFFER_BIT)语句清空Z缓冲区。
5) 现在已经可以正确地生成三维物体了,但还需要美化,可以使物体显得更明亮一些。我们用glLightfv函数定义光源的属性值。下例就定义了一个光源:
glLightfv(GL_LIGHT0, GL_AMBIENT,LightAmbient); glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular); glLightfv(GL_LIGHT0, GL_POSITION, LightPosition); glEnable(GL_LIGHT0); | GL_LIGHT0是光源的标识号,标识号由GL_LIGHTi组成(i从0到GL_MAX_LIGHTS)。 GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR、GL_POSITION分别定义光源的周围颜色强度、光源的散射强度、光源的镜面反射强度和光源的位置。
本文例子较简单,Visual C++6.0中还有很多例子。参照本文的设置,你一定能体会到OpenGL强大的图形、图像绘制功能。
void CGLSample2View::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); GLsizei width, height; GLdouble aspect; width = cx; height = cy; if (cy==0) aspect = (GLdouble)width; else aspect = (GLdouble)width/(GLdouble)height; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, 500.0*aspect, 0.0, 500.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void CGLSample2View::OnPaint() { CPaintDC DC(this); // device context for painting (added by ClassWizard) glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glVertex2f(100.0f, 50.0f); glColor4f(0.0f, 1.0f, 0.0f, 1.0f); glVertex2f(450.0f, 400.0f); glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glVertex2f(450.0f, 50.0f); glEnd(); glFlush(); } |
<!-- Script 代码开始 --> zmbbs=1; <!-- Script 代码结束 -->
|
|