中文版 红宝书----->>>>>>OPENGL编程指南!!! <<<<<<中文 第七版>>>>>>>>>共5部分 每部分60多MB 这是 2/5中文版 红宝书----->>>>>>OPENGL编程指南!!! <<<<<<中文 第七版>>>>>>>>>共5部分 每部分60多MB 这是 2/5中文版 红宝书----->>>>>>OPENGL编程指南!!! <<<<<<中文 第七版>>>>>>>>>共5部分 每部分60多MB 这是 2/5中文版 红宝书----->>>>>>OPENGL编程指南!!! <<<<<<中文 第七版>>>>>>>>>共5部分 每部分60多MB 这是 2/5视图这个过程。6)进行透视投影变换时,确信近侧裁剪平面不要太靠近观察者(照相机),不然会对深度缓冲区的准确性产生不利影响。即使已经让照相机对准了正确的方向,并且可以看到物体,但是它们仍然可能显得太大或太小。如果使用的是 gluPerspectiveO,可能需要修改用于定义视野的角度。这可以通过修大小改这个函数的第一个参数来实现。可以使用三角函数,根据物体的大小以及它与观察点的距离来计算所需的视野:视野角度的一半的正切距离就是这个物体的大小除以它与观察点距离的一半(如图319所示)。因此,我们可以使用个反正切函数来计算所需角度的一半。示例程序3-3使用了一个三角函数atan20,它根据一图3-19使用三角函数计算视野个直角三角形的对边和邻边的长度计算反正切值。随后,这个计算结果需要从弧度变换为角度。示例程序33计算视野# define pi3.1415926535double calculateAngle(double size, double distancedouble radtheta, degthetaradtheta =2.0 *atan2 (size/2.0, distance)idegtheta =(180.0 *radtheta)/PIreturn degtheta:当然,在一般情况下,我们并不知道物体的准确大小,而只知道场景中的一个点和观察点之间的距离。为了获得一个相对合理的近似值,可以通过确定场景中所有物体的最大和最小x、y和z坐标值,判断包含整个场景的边界长方体。然后,计算这个长方体的外接球体的半径,并根据这个球体的中心来判断物体和观察点的距离,根据这个球体的半径来确定物体的大小。例如,假设物体的所有坐标都满足方程式-1≤x≤3、5≤y≤7和-5≤z≤5,这个边界长方体的中心便位于(1,6,0),外接球体的半径就是长方体的中心到任意一个角的距离。假设这个角为(3,7,5),那么距离就是1)2+(7-6)2+(5-0)2=√30=54如果观察点位于(8,9,10),它和中心的距离就是√(8-1)2+(9-6)2+(10-0)2=√158=12570半角的正切就是5477除以12570,也就是04357。因此,半角的度数为2354(即视角大约为47度)。为了实现逼真的图像,需要记住视野的角度将会影响观察点的最佳位置。例如,经过计算得到的视野角度为179度,为了获得逼真感,观察点和屏幕的距离必须小于1英寸(254cm)。如果计算出来的视野角度太大,可能需要移动观察点,使之远离物体。第3章3.6操纵矩阵堆栈模型视图矩阵和投影矩阵的创建、加载和乘法只是“冰山露出水面的一角”。当我们对这些矩阵执行操作时,每一个矩阵实际上是各自矩阵堆栈最顶部的那个元素(如图3-20所示)。模型视图矩阵堆栈(32个4x投影矩阵堆栈(2个4×4的矩阵)4的矩阵)图3-20模型视图和投影矩阵堆栈矩阵堆栈适用于创建层次式的模型,也就是通过简单的模型构建复杂的模型。例如,假如绘制的是一辆具有4个轮子的汽车,每个轮子用5颗螺钉固定到汽车上。由于所有的轮子都是相同的,所有的螺钉看上去也没什么区别,因此可以用一个函数绘制轮子,用另一个函数绘制螺钉。这两个函数在适当的位置和方向绘制一个轮子或一颗螺钉,例如,它们的中心在原点,并且它们的轴与z轴对齐。在绘制这辆包括了轮子和螺钉的汽车时,需要4次调用画轮子的函数,每次都使用不同的变换,使每个轮子处于正确的位置。在绘制每个轮子时,需要5次调用画螺钉的函数,每次都要相对于轮子的位置进行适当的变换。假设只需要绘制车身和轮子,下面这段话描述了需要做的事情绘制车身。记住自己的位置,并移动到右前轮的位置。绘制轮子,并丢弃上一次所进行的变换(即移动到右前轮的位置),使自己回到车身的原点位置。记住自己的位置,然后移动到左前轮……类似地,对于每个轮子,我们需要绘制轮子,记住自己的位置,然后移动到绘制螺钉的每个位置,在画完每个螺钉之后丢弃上一次进行的变换。由于变换是以矩阵的形式存储的,因此矩阵堆栈就是一种理想的机制,可以用来完成这种类型的记忆、移动和丢弃操作。到目前为止所描述的所有矩阵操作( gILoadMatrix()、 glloadTrans当前矩阵拷贝pose Matrix(、 gIMultMatrix()、 gIMultTran-sposeMatrixo)、 glLoadIndentity(以及用于创建特定的变换矩阵的函数)都是对当前矩阵(堆添加栈顶部的那个矩阵)进行处理。可以用执行堆栈操作的函数 glPushMatrix(和 glPopMatrix(来控制堆栈顶部的矩阵。 glPushMatrix0复制一份当前矩阵,并把这份复制添加到堆栈的顶部。glPop Matrix(丟弃堆栈顶部的那个矩阵。图图3-21在矩阵堆栈中进行压入和弹出3-21显示了这两种操作。记住,当前矩阵就是位于堆栈顶部的矩阵。事实上, glPush Matrix表示“记住自己的位置”, glPopMatrixO表示“回到原来的位置”。枧图701void glpushMatrix(void兼容性扩展把当前堆栈中的所有矩阵都下压一级。当前矩阵堆栈是由 glMatrixMode(函glPushMatrix数指定的。这个函数复制当前的顶部矩阵,并把它压到堆栈中。因此,堆栈最glPopMatrix顶部的两个矩阵的内容相同。如果压入的矩阵太多,这个函数会导致错误。void glPopMatrix(void);把堆栈顶部的那个矩阵弹出堆栈,销毁被弹出矩阵的内容。堆栈原先的笫二个矩阵成为顶部矩阵。当前堆栈是由 glMatrixModeo函数指定的。如果堆栈只包含了一个矩阵,调用 glPopMatrixo将会导致错误。示例程序3-4绘制了一辆汽车,假设已经存在用于绘制车身、轮子和螺钉的相应函数。示例程序3-4压入和弹出矩阵draw wheel and bolts(int i;draw wheel()for(立=0;1<5:i++){gIPushMatrix();g1R。 tatef(72.0*i,0.0,0.0,1.0);gITranslatef(3.0,0.0,0.0)draw bolt()g1P。 pMatriX()draw body and wheel and bolts(rdraw car body()igIPushMatrix();gITranslatef(40,,30):/*move to first wheel position*draw wheel and bolts()gIPopMatrix()gIPushMatrix()?OglTranslatef(40,0,-30); /*move to 2nd wheel position*/draw wheel and bolts();g1P° pMatrix()/*draw last two wheels similarly*/这段代码假定轮了和螺钉的轴都与z轴对齐,固定毎个轮子的5颗螺钉均匀地分布,每隔72度一颗,并且与轮子中心的距离都为3个单位(例如英寸)。两个前轮的位置是车身原点向前40个单位,向左右两侧分别距离30个单位。使用矩阵堆栈的效率要高于使用单独的堆栈,尤其是堆栈是用硬件实现时。压入一个矩阵时,并不需要把当前矩阵复制到主进程,并且硬件有可能一次能够复制多个矩阵元素。有时候,我们可能想在矩阵底部保存一个单位矩阵,以避免重复调用 gILoadIdentity)。3.6.↑模型枧图矩阵堆栈在前面第3.2节中,我们解释了模型视图矩阵是视图变换矩阵与模型变换矩阵相乘的结果。每个视102第3幸图或模型变换都创建了一个新的矩阵,并与当前的模型视图矩阵相乘,其结果成为新的当前矩阵,表示组合后的变换。模型视图矩阵堆栈至少可以包含32个4×4的矩阵。模型视图矩阵一开始的顶部矩阵是单位矩阵。有些 OpenGL实现支持在矩阵堆栈上保存超过32个的矩阵。为了确定矩阵堆栈的最大允许矩阵数,可以使用查询函数 glGetIntegerv( GL MAX MODELVIEW STACK_ DEPTH, GLint* params)。362投影矩阵堆栈投影矩阵包含了一个表示投影变换的矩阵,它描述了视景体。一般情况下,并不需要对投影矩阵进行组合,因此在执行投影变换之前需要调用 gILoadidentity o函数。另外,由于这个原因,投影堆栈的深度一般只需要两层。有些 OpenGL实现可能允许在投影矩阵堆栈中存储超过2个的4×4矩阵。为了获取投影堆栈的深度,可以调用 glGetIntegerv( GL MAX PROJECTION STACK DEPTH, GLint* params)。在投影矩阵堆栈中保存第二个矩阵有什么用途呢?有些应用程序除了显示三维场景的主窗口之外,还需要显示一个包含文本的帮助窗口。由于文本在正投影模式下比较容易定位,因此当我们需要显示帮助窗口时,可以暂时把投影模型设置为正投影,显示这个帮助窗口,然后再返回到原来的透视投影模式。glMatrixMode(GL PROTECTIONglPushMatrix();/* save the current projection*/glLoadIdentity()ig1otho(∴);*set up for displaying help*display the help()iglPopMatrix();注意,当修改投影模式时,很可能需要对模型视图矩阵进行相应的修改。高级话题如果读者的数学知识足够丰富,可以创建自定义的投影矩阵,执行任意的授影变換。例妇,OpenGL和它的工具函数库并没有提供内置的机制,对两点透视(two- point perspective)提供支持。如果想模拟手写文本的绘图方式,就可能需要一个像这样的投影矩阵。3.7其他裁剪平面除了视景体的6个裁剪平面(左、右、底、顶、近和远)之外,还可以另外再指定最多可达6个的其他裁剪平面,对视景体施加进一步的限制,如图3-22所示。这些裁剪平面可以用于删除场景中的无关物体。例如,我们可能只想显示一个物体的剖面视图。每个平面都是由它的方程式Ax+By+Cz+D=0的系数所指定的。裁剪平面会根据模型和视图矩阵自动执行适当的变换。最终的裁剪区域将是视景体与其他裁剪平面定义的所有半空间的交集。记住, OpenGL会自动对部分被裁剪的多边形的边进行正确的重构。图3-22共他裁剪平面和视景体void glClip Plane( GLenum plane, const GLdouble *equation定义一个裁剪平面。 equation参数指向平面方程Ax+By+Cz+D=0的4个系数。满足(ABCD)视图103M(xy2zw)7≥0的所有视觉坐标(xy2znw)点都位于这个平面定义□兼容性扩展的半空间中,其中M是在调用 gIclipPlaneo时的当前模型视图矩阵。所有「 gIClipPlane不是位于这个半空间内的点都将裁剪摔。pane参数是 GL CLIP_ PLANE, GL CLIP DISTANCEi其中i是一个整数,表示需要定义哪个有效裁剪平面。i的值位于0和最大GL CLIP PLANE其他裁剪平面数减1之间。GL MAX CLIP PLANES我们需要启用每个被定义的裁剪平面:glEnable(GL CLIP PLANEi);也可以用下面这个函数禁用一个裁剪平面;glDisable(GL CLIP PLANEi);所有的 OpenGL实现都必须支持至少6个其他裁剪平面,有些实现可能允许超过6个的其他裁剪平面。可以用 GL MAX CLIP_ PLANES为参数调用 glGctIntegervo函数,查询自己使用的pen丑实现所支持的其他裁剪平面的最大数量注意:调用g1 ClipPlane()西数所执行的裁剪是在视觉坐标中完成的,而不是在裁剪坐标中进行的。如果投影矩阵为奇异矩降(也就是把三錐坐标压平到二维坐标的真正投影矩阵),这个区别就非常大。在视觉坐标中进行裁剪时,即使投影矩阵是奇异矩阵,裁剪仍然是在三维空间中进行的。裁剪平面的代码例子示例3-5是经过两个截剪平面裁剪的线框球体,裁去了34体积如图3-23所示。图3-23裁剪后的线框球体示例程序3-5经过两个裁剪平面裁剪的线框球体;clip,cvoid init(void)rlclearcolor(0·0,00,0.0,0.0);gIshadeModel(GL FLATvoid display(v。id)ocin. comGldouble eqn[4]={0.0,1.0,0.0,0,0}GLdouble egn2[4]=(1.0,0.0,0·0,0.0};g1CLea(G二 COLOR BUFFER_B工T)g1 Color3f(1·0;1.0,1.0)glPushMatrix()?g1ras1ate£(0.0,0,0,-5.0)cLip1 ower ha1f-¥<0*glclipPlane(GL CLIP PLANEO eqn)A glEnable(GL CLIP PLANEO )iclip left half --x