OpenGL学习笔记(九)

龙云尧个人博客,转载请注明出处。

CSDN地址:http://blog.csdn.net/michael753951/article/details/71750809

个人blog地址:http://yaoyl.cn/nehexue-xi-bi-ji-jiu/


前言

本次笔记为nehe课程第11课的学习内容,通过实验,我们可以利用正弦函数绘制一个舞动的旗帜。在本次课程中,我们也将学习OpenGL中glPolygonMode函数对一个模型的正面和反面进行不同模式的绘图。

glPolygonMode 函数介绍

本部分内容请先查看前人整理的博客【OpenGL学习笔记(7)多边形绘制】。请先仔细阅读该博客,理解如何区分模型中的正面反面(依然遵守右手定则,由描点的方向决定),以及3种填充方式(fill,line point)。

代码分析

好了,到这里新的基本知识就已经讲述完毕了,我们看看作者是如何实现一个舞动的旗帜的。(基本的代码我就不分析了,只挑重点)

InitGL中,在设置完纹理和透视的init之后,我们将插入glPolygonMode方法进行正反不同方式填充(以此从直观上区分正反面)。然后完成45*45个点阵的绘制。

绘制中,作者将45*45个点阵均匀分布到一个长为9个单位,宽为9个单位,并且中点放置在原点上的正方形中(通过-4.5将中点移动到原点)。Z轴上,作者绘制了一个以x为自变量的正弦函数(刚好一个周期,至于为什么需要一个周期,在看完本篇博客之后你将会有答案)。

接下来DrawGLScene开始进行图像旋转。这里我注释掉了作者的沿y轴和z轴旋转,仅仅保留沿x轴旋转,以方便观察。模型绘制和纹理绑定部分我不分析了,已经是很经典的代码了,就是利用45*45个点阵绘制44*44个正方形而已。

接下来就是本此实验的重点了。一个if判断,这个判断实现了旗帜的舞动。

1
2
3
4
5
6
7
8
9
10
11
12
13
if( wiggle_count == 2 )
{
for( y = 0; y < 45; y++ )
{
hold=points[0][y][2];
for( x = 0; x < 44; x++)
{
points[x][y][2] = points[x+1][y][2];
}
points[44][y][2]=hold;
}
wiggle_count = 0;
}

仔细阅读代码我们可以发现,作者通过wiggle_count计数,每两帧画面,就将45*45点阵中的z值向左移动一位,最左边的z值放到最右边,实现循环。所以整个if实现的就是将一个正弦波向左移动,这也就是为什么我们在实验中旗帜的挥动,波纹向左走的原因。

剩下的代码就不用怎么解释了,都是和以前一样的代码了。

回到一开始的问题,为什么在设置45*45点阵的z值的时候,要使用2*pi将正弦波控制在一个周期。可不可以乘以n*pi,或者2*n*pi,或者其他值。(提示,if条件中左移的时候,左端点会被移动到右端,此时这个端点有可能是一个突变点造成波形尖锐或者畸形)。

最后附上效果图,首先是作者源代码的运行结果。

section9.1

然后是经过我注释掉y轴旋转,z轴旋转以及注释掉了整个if判断之后的图像。我们发现这个时候图像已经不能飘动了,而且上下两个边界呈现一种标准的正弦波的形式。

section9.2

最后是没有注释if,单单注释掉y轴旋转和z轴旋转的图像。喜事已经能够正常的飘动了。

section9.3

显示列表

nehe教程第12课显示列表。

该课程主要讲解了glGenLists的使用样例。在使用之前,我们可以了解一下使用glGenLists能给你程序性能所带来的好处。这部分只是我们可以参考【OpenGL(八) 显示列表】以及【OpenGL显示列表】

本次课程需要使用的光、材质和光照模型,就比较符合glGenLists的使用环境。

我们来看看课程代码,作者首先定义了两个二位数组,boxcol用来存放立方体除了top之外,其他所有面的5种取,topcol用来存放立方体的顶部颜色。

接下来作者定义了BuildLists方法,在这个方法中,作者使用了本次课程的核心——显示列表。使用方法大致如下。

  • 首先调用glGenListst生成一个显示列表(连续地址的数组形式),本次实验中作者生成了包含2个元素的数组,返回数组的首地址给box(不过定义的时候并没有定义成指针,有些奇怪)
  • 接着作者使用glNewList和glEndList定义显示列表中的每一个元素。
    • 在GLNewList方法中,作者传入了box指向的第一个显示元素,同时传入了一个参数GL_COMPILE。(注意:按照【OpenGL显示列表】中所告诉我们的知识,glNewList的函数原型为void glNewList(GLuint list,GLenum mode);说明一个显示列表的开始,其后的OpenGL函数存入显示列表中,直至调用结束表的函数(见下面)。参数list是一个正整数,它标志唯一的显示列表。参数mode的可能值有GL_COMPILE和GL_COMPILE_AND_EXECUTE。若要使后面的函数语句只存入而不执行,则用GL_COMPILE;若要使后面的函数语句存入表中且按瞬时方式执行一次,则用GL_COMPILE_AND_EXECUTE。)
    • glNewList和glEndList中间定义矩阵的语句我这里就不再分析,和之前一样。
  • 定义完显示列表中第一个元素之后,定义第二个元素时,需要将数组头指针向后移一位,所以代码中会有top=box+1;一行。

接着本次实验中作者修改了InitGL方法,他在Init的最后几行中,调用了3个glEnable方法,打开了GPU默认的灯光(GL_LIGHT0),自定义的灯光(GL_LIGHTING),以及颜色纹理(GL_COLOR_MATERIAL),注意,如果不激活颜色纹理的话,在我们DrawGLScene中,将只能够贴上图片纹理,但是无法再往上添加颜色纹理(即之后的glColor3fv失效)。

DrawGLScene方法中,作者或者了6层,逐渐减少的立方体。使用过程中,作者在定位完画笔,并且旋转坐标轴完成之后,使用glColor3fv将一开始宏定义的颜色作为纹理,然后调用glCallList快速构建矩形。本次实验使用显示列表之后,DrawGLScene代码量明显比以往少了很多。

其他代码就不解释了。本次课程主要掌握显示列表的原理以及使用即可。

最终的显示图像如下所示:

section9.4

稍微旋转一下图片可以看到如下效果:

section9.6

我们注释掉glEnable(GL_COLOR_MATERIAL);或者DrawGLScene中的glColor3fv方法,就发现显示的图片已经没有颜色纹理了。

section9.5

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

请我喝杯咖啡吧~

支付宝
微信