OpenGL 状态管理和绘制几何体
虽然使用OpenGL可以绘制复杂、有趣的图形,但这些图形都是由几种基本图元构成的(达芬奇的作品都是使用画笔和画刷完成的)。
最抽象角度看,三种基本的绘图操作:清空窗口、绘制几何体和绘制光栅物体。光栅物体包括二维图像、位图和字符字体等。
OpenGL中,除非特别声明,否则调用绘图函数后,将立刻绘制指定的物体。这看似理所当然,但在有些系统中,首先建立一个要绘制的物体列表,然后命令图形硬件绘制列表中的物体。前一种方式被称为直接模式,OpenGL中默认采用这种模式。除直接模式外,还可以将命令存储在列表中,供以后使用。直接模式更易于编程,但显示列表的效率更高。
绘图补救工具箱(为什么叫这个名字)
1.清空窗口:在计算机屏幕上绘制不同于在纸上绘图。纸张最初是白色的,只需绘图即可;在计算机上,存储图像的内存中通常包含最后绘制的图像,因此在绘制新场景之前,需要使用某种背景颜色将其清除。为什么要清屏而不绘制一个足够大、颜色适当的矩形来覆盖整个窗口?首先,清屏函数的效率比绘图绘图函数高得多;其次OpenGL允许程序员随意地设置坐标系、观察位置和观察方向,因此要指定一个大小和位置合适的清屏矩形是极其困难的;最后,在很多计算机上,图形硬件中除了存储像素颜色的缓存外还包含多个其他的缓存。这些缓存也必须随时进行清楚,如果能用一条命令清楚所有缓存将会非常方便。
另外,还必须知道像素的颜色在图形硬件的位平面中是如何存储的。两种方法:1.可以直接将像素的红色、绿色、蓝色和alpha(RGBA)值存储到位平面中;2.存储一个应用颜色查询表的索引值。
下面代码将RGBA模式窗口的背景设置为黑色:
1 glClearColor(0.0, 0.0, 0.0, 0.0);
2 glClear(GL_COLOR_BUFFER_BIT);
第1行将清空颜色设置为黑色,第2行将窗口背景设置为当前清空颜色。glClear()接受一个参数,该参数指出要清楚哪个缓存。这里只清除了颜色缓存,显示在屏幕上的图像颜色存储在这个缓存中。通常,在应用程序中较前的位置设置一次清空颜色,然后根据需要清空缓存。OpenGL使用一个状态变量来记录当前清空颜色,而不要求程序在每次清空颜色缓存时都指定清空颜色。
下面代码将清空颜色缓存和深度缓存
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
函数glClearDepth指定了一个数值,深度缓存中的每个像素都将被设置为这个值;
颜色缓存 GL_COLOR_BUFFER_BIT
深度缓存 GL_COLOR_BUFFER_BIT
积累缓存 GL_ACCUM_BUFFER_BIT
模版缓存 GL_STENCIL_BUFFER_BIT
如果不想使用默认的RGBA颜色,深度值。积累颜色及模版索引来清空相应的缓存,则需要在发出清空缓存的命令之前设置清空值。glClearAccum() glClearStencil()来指定清空相应缓存的颜色索引。
2.指定颜色
在OpenGL中,描述物体形状的信息和颜色的信息是彼此独立的。任何时候绘制几何体时,都是用当前指定的着色方案。着色方案可以简单如“使用红色绘制所有物体”,也可复杂如“物体由海蓝色塑料制成,黄色聚光灯点以特定的方向对着某一点,其他地方为红褐色普通光。”一般而言,OpenGL程序员首先要设置颜色和着色方案,然后才绘制物体。OpenGL通过记录当前颜色,提高了绘图性能。
例如:使用红色绘制物体A和B,使用蓝色绘制物体C。
1 set_current_color(red);
2 draw_object(A);
3 draw_object(B);
4 set_current_color(green);
5 draw_object(C);
设置颜色,可使用函数glColor3f。接受三个参数,都是0.0~1.0的浮点数。这些参数依次为颜色中的红色、绿色和蓝色分量。可讲三个值视为颜色的成分:0.0表示不实用相应的成分,1.0表示使用成分的最大值。三个分量全为0时为黑色,三个分量全为1时为白色,三个分量全为0.5时为灰色。
3.强制完成绘图操作
现在图形系统视为装配线。CPU发出绘图命令,由其他硬件来完成几何变换,然后进行裁剪、着色或纹理映射,最后,将处理结果写入位平面,以便进行显示。在高端体系结构中,这些操作由不同的硬件执行,这些硬件是为了快速执行特定任务而专门设计的。在这样的体系结构中,CPU没有必要等到每个绘图命令完成后再发送下一条命令。CPU沿流水线传输定点数据的同时,变换硬件正在对前一个定点进行变换,而更前一个顶点正在被裁剪,等等。在这样的系统中,如果CPU等待每条命令完成后才发送下一条命令完成后才发送下一条命令,性能大幅下降。
OpenGL提供了函数glFlush,它强制客户机放松网络分组,即使分组未填满。然而,如果要编写无论是否涉及网络都能正确运行的程序。必须在绘制完每个帧或场景后函数调用glFlush。注意,函数glFlush并不等待绘制完成,而是强行开始执行绘图操作,从而确保之前的所有命令都将在有限的时间内执行,即使后而没有其他渲染命令。glFinish需要进行往返通信,因此过多的使用它降低应用程序的性能。
4.坐标系统修补工具箱
首次打开窗口、移动窗口和改变窗口大小时,窗口系统都将发送一个事件,通知程序员。如果使用的是GLUT,通知将自动完成,并调用向glutReshapeFunc注册的函数,必须注册用于完成下列工作的回调函数:
1.重新建立用作新渲染画布的矩形区域。
2.定义绘制物体时使用的坐标系。
例如glutReshapeFunc(reshape); 其中函数reshap()如下
void reshape(int w, int h) {
glviewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) w, 0.0, (GLdouble) h);
}
glViewport调整像素矩形,用于绘制整个新窗口。接下来的三个函数调整绘制坐标系,使左下角位置为(0, 0),右上角位置为(w, h);另一种解释方式是,它绘制一张图纸。函数reshape的参数w和h表示绘制图纸上正方形的行数和列数。gluOrtho2D将原点设置为左下角正方形的左下角,每个正方形代表一个单位。
表述点、直线和多边形
所有几何图最终都是用其顶点来描述的:顶点坐标定义点本身、线段的端点或多边形的角点。
1.什么是点、直线和多边形
OpenGL中与数学含义类似,但不完全相同。差别之一源自计算机在计算能力上的局限性。在任何OpenGL实现中,浮点数的计算精度都是有限的,存在舍入误差;OpenGL中的点、直线和多边形的坐标也存在这样的问题。另一个重要差别源于光栅图形显示器的局限性。在这种显示器中,最小的显示单位是像素,虽然像素的宽度可能小于0.01英寸,但相对数学中的无限小(点)和无限窄(线),这仍然太大了。OpenGL进行计算时,假设点是由浮点数向量表示的。然而实际绘制中点经常是一个像素,因此OpenGL将许多坐标值相近的点绘制在同一个像素上。
点:由一系列定义顶点的浮点数表示。在内部计算中,总是认为顶点是三维的。对于用户指定的二维顶点(只有x坐标和y坐标),OpenGL将其Z坐标设置为0。
OpenGL使用三维投影几何学中的齐次坐标,因此在内部计算中,所有顶点都由4个浮点坐标(x,y,z,w)表示。如果w不为0,这些坐标对应于欧式几何的三维点(x/w, y/w, z/w)。在OpenGL命令中,可以指定w坐标,但很少这样做。如果w坐标没有指定,则认为它为1.0。
直线:OpenGL中,术语直线指的是线段,面不是数学意义上沿两个方向延伸到无穷远的直线。有一些简单的方法可以定义一系列相连甚至闭合的线段。无论在何种情况下,相连的线段都是用其端点定义的。
多边形:多边形指的是由一系列闭合线段环绕的区域,这些线段由其端点处的顶点指定。绘制多边形时,通常填充其内部的像素,但也可以绘制成轮廓或一组点。一般而言,多变形很复杂,因此OpenGL对基本多边形图元的组成作了严格的限制。首先,OpenGL多边形的边不可以相交(数学中,满足这种条件的多边形被称为简单多边形);其次,OpenGL多边形必须是凸的,这意味着多边形不能有缺口。准确的说,给定多边形内部的任何两个点,这两个点之间的线段也位于多边形内部,则这个多变形是凸的(不会吧,受限很大啊)。
OpenGL之所以对合法多边形类型作出限制,是为了更方便地提供能够对符合条件的多边形进行快速渲染的硬件。简单多变形可被快速地渲染,而复杂多边形难以快速检测出来。为了最大限度地提高性能,OpenGL假定多边形是简单的。在现实世界中,很多表面都是由非简单多边形、非凸多边形形成或有洞的多边形组成的。所以有这些多边形都可以由简单的凸多边形组合而成,因此GLU库提供了一些建立更复杂物体的函数。这些函数接受复杂的描述信息,对多边形进行网格化——将它们分解成组简单的、能够进行渲染的OpenGL多边形。
OpenGL顶点总是三维的。构成多边形边界的点并不一定共面:当然,在很多情况下,他们确实共面的,例如,所有顶点的z坐标都为0或多边形为三角形。如果多边形的顶点不共面,则经过各种旋转、视点变化和到显示屏幕的投影后,这些点可能不再构成一个简单的凸多边形。
矩形:在图形应用程序中非常普遍,OpenGL提供了用于绘制填充矩形的函数:glRect*() 可以将矩形作为多边形来绘制。
曲线和曲面:所有光滑曲线和曲面都可以用短线段和小型多边形以任意精度进行逼近。因此,只要将曲线和曲面划分的足够细,然后用线段和平面多边形进行逼近,便可以使它们看起来是弯曲的。
2.指定顶点
在OpenGL中,所有几何体最终都被描述为一个有序的顶点集合,要指定顶点,可使用函数glVertex*()
该函数指定一个用于表述几何体的顶点。通过选择何时版本,可以用多至4个坐标(x, y, z, w),至少用到两个坐标(x, y)来指定顶点。使用没有明确指定z或w的版本时,OpenGL假定z为0,w为1。对函数glVertext*()的调用,只能在glBegin()和glEnd()之间。
3.OpenGL几何绘制图图元
知道如何指定顶点后,还需要知道如何命令OpenGL根据这些顶点创建一系列的点、直线或多边形。为此,需要在glBegin()调用和glEnd()调用之间指定每组顶点。传递给glBegin()的参数决定了根据这些顶点创建哪几种几何图元。
几何图元的名称和意义
GL_POINTS 独立的点
GL_LINES 将没对顶点视为一条线段
GL_LINE_STRIP 一些列相连的线段
GL_LINE_LOOP 同上,同时在第一个和最后一个顶点之间绘制一条线段
GL_TRIANGLES 将每3个顶点视为一个三角形
GL_TRIANGLES_STRIP 三角形条带
GL_TRIANGLE_FAN 三角形扇
GL_QUADS 将每4个顶点视为一个四边形
GLQUAD_STRIP 四边形条带
GL_POLYGON 简单的凸多边形
void glEnd(void);该函数标记顶点列表的结束。绘制同一种图元的办法有很多,选择哪种方法取决于顶点数据。
可用于glBegin()和glEnd()之间的函数
有关顶点最重要的信息是坐标,由函数glVertex*()指定。还可以使用相应的函数为每个顶点指定其他数据:颜色、法线向量、纹理坐标或这些数据的任意组合。
glVertex*() 设置顶点坐标
glColor*() 设置RGBA颜色
glIndex*() 设置颜色索引
glSecondaryColor*() 设置用于纹理映射后处理的辅助颜色
glNormal*() 设置法线向量坐标
glMatenal*() 设置材质属性
glFogCoord() 设置雾坐标
glTexCoord*() 设置纹理坐标
glMultiTextCoord*() 为多重纹理映射设置纹理坐标
glEdgeFlag*() 控制边的绘制
glArrayElement() 提取顶点数组数据
glEvalCoord*()、glEvalPoint*() 生成坐标
glCallList()、glCallLists() 执行显示列表
其他任何OpenGL函数都不能用于glBegin()和glEnd()之间,这样做大部分情况下将导致错误。
例子,绘制一个圆
1 glBegin(GL_LINE_LOOP);
2 for(int i = 0; i < 100; i++) {
3 float angle = 2 * 3.1415f * i/100;
4 glVertex2f(cos(angle), sin(angle));
5 }
6 glEnd();
本例并不是绘制园最有效的方法,尤其当需要反复绘制时。图形绘制函数的速度通常非常快,而上述代码对每个顶点计算角度并调用sin和cos;另外还有循环开销。另一种计算圆上顶点的方法是使用GLU函数。如果需要绘制许多圆,应一次性就算出顶点坐标,然后将其存储在数组中,并创建一个显示列表或使用顶点数组来渲染它们。
除非被加入到显示列表中,否则所有的glVertex*()函数都必须位于glBegin()和glEnd()之间,否则不会执行任何操作。在显示列表中,仅当它们位于glBegin() glEnd()之间时才会被执行。虽然很多函数都能用于glBegin()和glEnd()之间,但只有glVertex*()能生成顶点。函数glVertex*()被调用时,OpenGL将当前颜色、纹理坐标法线向量信息等分配给生成的顶点。
在glBegin和glEnd之间,可以使用glVertex*()的24种版本的任意组合:但是在实际的应用程序中,通常使用同一种版本。如果顶点数据的定义是前后一致和重复的,则可以使用顶点数组来提高应用程序的性能。
3.基本状态管理
OpenGL维护了众多的状态和状态变量。对物体渲染时,可使用光照、纹理、隐藏面消除、雾以及其他影响物体外观的状态。默认情况,这些状态大多数未被启动。启动这些状态的代价可能很大,例如,启动纹理映射将降低渲染图元的速度;但图形的质量将更高,看起来更真实。
要打开和关闭这些状态,可使用下面两个简单的函数。
void glEnalble(GLenum cap); void glDisable(GLenum cap);
glEnable()用于启用功能,glDisable()用于关闭。有60多个枚举值可用作glEnalbe()或glDisable()的参数,其中包括GL_BLEND、GL_DEPTH_TEST等
还可以检查状态当前处于启动还是禁用状态。
GLboolean glIsEnabled(GLenum capability);
该函数根据被查询的功能是否被启动,返回GL_TRUE或GL_FALSE。
上述状态只有两种,开和关。大多数OpenGL函数为更复杂的状态变量设置值。例如,函数glColor3f()设置三个值,它们是状态GL_CURRENT_COLOR的组成部分。
void glGetBooleanv(GLenum pname, GLboolean *params);
void glGetIntegerv(GLenum pname, GLint *params);
void glGetFloastv(GLenum pname, GLfloat *params);
void glGetDoublev(GLenum pname, GLdouble *params);
void glGetPointerv(GLenum pname, GLvoid **params);
这些函数分别获得布尔值、整型、浮点型、双精度型和指针型变量的值。
显示点、直线和多边形
默认情况下,点在屏幕上被绘制为单个像素;直线被绘制为实线,宽度为一个像素;多边形用纯色(solid)填充。
1.点的细节
为控制渲染后点的大小,可使用函数glPointSize(),并将期望大小(单位像素)作为参数。
void glPointSize(GLfloat size);
该函数设置点的宽度(单位为像素);默认值为1.0。
对于各种大小的点,根据是否启用了反走样功能,将在屏幕上绘制一些列像素。反走样是一种使点和直线更平滑的技术。如果不使用(默认设置),用小树表示的宽度将舍入为整数,然后绘制一个与屏幕平行的方形像素区域。因此,如果宽度为1.0,则方形区域为1*1像素;如果宽度为2.0,则方形区域为2*2像素,依次类推。
如果启动了反走样或多重采样,将绘制一个圆形像素区域,并将边界上的像素画的暗些,使边缘显得较为光滑。在这种模式下,对非整数宽度进行舍入。glGetFloatv(),查看支持点的最大和最小尺寸。
2.直线的细节
在OpenGL中,可以指定线宽和各种点画方式:点线(dotted)、短划线(dashed)以及交替使用点和短划线等。
线宽:void glLineWidth(GLfloat width);该函数以像素为单位设置线宽;参数width的值必须大雨0.0,默认为1.0。
未完待续,非常期待看到,加载位图那章,慢慢来。
如果走样和多重采样被启用,渲染直线时将受影响。反走样没有被启动时,如果宽度为1、2、3,将绘制宽度分别为1个、2个和3个像素的直线;反走样被启动时,可支持非整数线宽,同时边缘像素通常暗些。同点尺寸一样,OpenGL实现可能将非反走样直线的最大宽度限制为最大的反走样线宽。要获悉的非反走样线宽范围,GL_LINES,则会址每条线段之前都将重置pattern。
1 #define drawOneLine(x1, y1, x2, y2) glBegin(GL_LINES); glVertex2f((x1), (y1));glVertex2f((x2), (y2)); glEnd();
2 void init() {
3 glClearColor(0.0, 0.0, 0.0, 0.0);
4 glShadeModel(GL_FLAT);
5 }
6
7 void display(void) {
8 int i;
9 glClear(GL_COLOR_BUFFER_BIT);
10 glColor3f(1.0, 1.0, 1.0);
11 glEnable(GL_LINE_STIPPLE);
12
13 glLineStipple(1, 0x0101);
14 drawOneLine(50.0, 125.0, 150.0, 125.0);
15 glLineStipple(1, 0x00FF);
16 drawOneLine(150.0, 125.0, 250.0, 125.0);
17 glLineStipple(1, 0x1C47);
18 drawOneLine(250.0, 125.0, 350.0, 125.0);
19
20 glLineWidth(5.0);
21 glLineStipple(1, 0x0101);
22 drawOneLine(50.0, 100.0, 150.0, 100.0);
23 glLineStipple(1, 0x00FF);
24 drawOneLine(150.0, 100.0, 250.0, 100.0);
25 glLineStipple(1, 0x1C47);
26 drawOneLine(250.0, 100.0, 350.0, 100.0);
27 glLineWidth(1.0);
28
29 glLineStipple(1, 0x1C47);
30 glBegin(GL_LINE_STRIP);
31 for(i = 0; i < 7;i++) {
32 glVertex2f(50.0 + (GLfloat)i * 50.0, 75.0);
33 }
34 glEnd();
35
36 for(i = 0; i < 6;i++) {
37 drawOneLine(50.0 + (GLfloat)i * 50.0, 50.0, 50.0 + (GLfloat)(i+1) * 50.0, 50.0);
38 }
39
40 glLineStipple(5, 0x1C47);
41 drawOneLine(50.0, 25.0, 350.0, 25.0);
42 glDisable(GL_LINE_STIPPLE);
43 glFlush();
44 }
45
46 void reshape(int w, int h) {
47 glViewport(0, 0, (GLsizei)w, (GLsizei)h);
48 glMatrixMode(GL_PROJECTION);
49 glLoadIdentity();
50 gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
51 }
52 int main(int argc, char* argv[])
53 {
54 glutInit(&argc, argv);
55 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
56 glutInitWindowSize(400, 150);
57 glutInitWindowPosition(100, 100);
58 glutCreateWindow("hello");
59 init();
60 glutDisplayFunc(display);
61 glutReshapeFunc(reshape);
62 glutMainLoop();
63 return 0;
64 }
3.多边行的细节
(唉~又少了一页!重新下载一本效果现在看,比前一本好多了)一般情况下,多边形是按填充模式绘制的,边界之内的像素均被绘制。但是,也可以把它们画成轮廓形式,甚至只绘制它们的顶点。填充多边形可以是实心填充,也可以用某种模式进行点画填充。尽管这里省略了具体的细节,但我们还是要说明一点,相邻的填充多边形如果共享一条边或一个顶点,组成这条边或这个顶点的像素只绘制一次,它们只包含在其中一个多边形中。这样,部分透明的多边形的边不会绘制两次,它们的边缘看上去不会更暗。为了对填充多边形进行抗锯齿处理,强烈推荐使用多重采样。
点、轮廓或实心形式的多边形
多边形具有正面和背面两个面。取决于哪一面朝向观察者,多边形可能会被渲染成不同的样子。这样,就可以获得实心物体正反两面截然不同的剖视图。在默认情况下,多边形的正面和背面是按照相同的方式绘制的。为了更改这个行为,或者只绘制它的轮廓或顶点,可使用glPloygonMode(GLenum face, GLenum mode)函数。
例如,可以通过下面两个调用,把多边形的正面画成填充形式,把背面画成轮廓形式:
glPolygonMode(GL_FRONT, GL_FILL);glPolygonMode(GL_BACK, GL_LINE);
反转和剔除多边形表面
按照约定,如果多边形的顶点以逆时针顺序出现在屏幕上,它便称为正面。可以根据方向一致的多边形构建任何合理实心表面。按照数学的术语,这种表面称为可定向簇。为了创建可定向的表面,可以使用全部是逆时针方向的多边形,也可以使用全部是顺时针方向的多边形,这正是可定向的数学定义。顶点的方向(顺时针或逆时针)又称为环绕。
在一个完全闭合的表面(由方向一致的不透明多边形所组成)上,所有的背面多边形都是不可见的,因为它们总是被多边形的正面所遮挡。如果观察者位于这个表面的外侧,可以启用剔除功能,丢弃哪些被OpenGL认为是背面的多边形。类似的,如果观察者位于物体的内侧,只有背面的多边形才是可见的。告诉OpenGL丢弃哪些不可见的正面或背面多边形,可以使用glCullFace()函数。当然,在此之前必须调用glEnable()函数启动剔除功能。
点画多边形
在默认情况下,填充多边形是用实心模式绘制的。此外,它们还可以使用之中用32*32位的窗口对齐的点画模式。glPolygonStipple()函数用于指定多边形的点画模式;启用多边形点画功能;glEnalbe(GL_POLYGON_STIPPLE);用同一个参数调用glDisalbe()函数可以禁用多边形点画功能。
标记多边形的边界边
在默认情况下,所有的顶点都标记为边界边的起点,但是可以使用glEdgeFlag*()函数手工控制边界标志(edge flag)的设置。这个函数在glBegin()和glEnd()之间调用,它将影响在之后所制定的所有顶点,直到再次调用glEdgeFlag()函数。它只用作用于那些为多边形、三角形和四边形所指定的顶点,对那些为三角形带或四边行带所指定的顶点无效。
void glEdgeFlag(GLboolean flag); void glEdgeFlagv(const GLboolean *flag);
5.法线向量
法线向量是垂直于某个表面的方向向量。对于平表面而言,它上面每个点的垂直方向都是相同的。但是,对于普通的曲面而言,表面山给每个点的法线方向可能不相同。在OpenGL中,即可以为每个多边形指定一条法线,也可以为多边形的每个顶点分别指定一条法线。同一个多边行的顶点可能共享同一条法线(平表面),也可能具有不同的法线(曲面)。除了顶点之外,不能为多边形的其他地方分配法线。
物体的法线向量定义了它的表面在空间中的方向。具体地说,定义了它相对于光源的方向。OpenGL使用法线向量确定了这个物体的各个顶点所接收的光照。光照本省是一个非常庞大的主题。在这里,我们只是简单地讨论法线向量,因为在定义物体的几何形状时,同时也定义了它的法线向量。
可使用glNormal*()函数,把当前的法线向量设置为这个函数的参数所表示的值。以后调用glVertex*()时,就会把当前法线向量分配给它所制定的顶点。每个顶点常常有不同的法线,因此需要交替调用这两个函数。
void glNormal3(TYPE nx, TYPE ny, TYPE nz); void glNormal3(const TYPE *v);
在表面的一个特定点上,有两条向量垂直于这个表面,它们指向相反的方向。按照约定,指向表面外侧的那条向量就是它的法线。如果想反转内侧和外侧,只要把所有的法线向量从(x,y,z)修改为(-x,-y,-z)就可以了。法线向量只表示方向,可以指定为任意长度,但在执行光照计算时,它的长度会转换为1(长度为1的向量成为单位向量或规范化的向量)。一般而言,我们应该提供规范化的法线向量。为使一条法线向量具有单位长度,只要把它的每个x、y和z成分除以法线的长度就可以了。如果提供了单位长度的法线,并且只执行均匀的缩放,就可以使用glEnable(G_RESCALE_NORMAL),用一个常量因子(从模型视图变换矩阵得出)对法线进行缩放,使他们在变换后恢复为单位长度。
6.顶点数组
OpenGL需要进行大量的函数调用才能完成对几何图元的渲染。绘制一个20条边的多边形至少需要22个函数调用。首先调用1次glBegin(),然后为每个顶点调用1次函数,最后调用1次glEnd()。
OpenGL提供了一些顶点数组函数,允许只用少数几个数组指定大量的与顶点相关的数据,并且少量函数调用访问这些数据。使用顶点数组,一个拥有20条边的多边形的20个顶点可以放在1个数组中,并且只通过1个函数进行调用。如果每个顶点还有一条法线向量,所有20条法线向量可以放在另一个数组中,也可以只通过1个函数进行调用。
把数据放在顶点数组中可以提高应用程序的性能。使用顶点数组可以减少函数调用的次数,从而提高性能。另外,使用顶点数组还可以避免共享顶点的冗余处理。
使用顶点数组对几何图形进行渲染的3个步骤:
1.激活最大可达8个数组,每个数组用于存储不同类型的数据:顶点坐标、表面法线、RGBA颜色、辅助颜色、颜色索引、雾坐标、纹理坐标以及多边形的边界标志。
2.把数据放入数组中。这些数组是通过它们的内存位置的地址进行访问的。在客户机-服务器模型中,这些数据存储在客户机的地址空间中,除非选择使用缓存对象,这时候,数组存储在服务器内存中。
3.用这些数据绘制几何图形。OpenGL通过指针从所有的被激活数组中国获取数据。在客户机-服务器模型中,数据被传输到服务器的地址空间中。有3种凡是完成这个任务
访问单独的数组元素(随记存取);创建一个单独数组元素的列表(系统存取);线性地处理数组元素。
另一种常用的数据方式是混合顶点数组数据。和普通的使用多个不同的数组、让每个数组保存一种不同类型的数据(颜色、表面法线、坐标等)不同,我们可以把不同类型的数据混合放在同一数组中。
跨距(stride)
gl*Pointer()函数的stride参数告诉OpenGL如何访问指针数组中的数据。它的值应该是两个连续的指针元素之间的字节数量(或者是0,这是一种特殊情况)。如果stride参数的值为0,每种类型的顶点数组(RGB颜色、颜色索引、顶点坐标等)必须紧密相邻。数组中的数据必须是一致的。
看到这里,有点发虚了,没有一个整体概念了,先停停,回头看看。笔记做的这么详细,基本都没经过自己过滤,发现很多文字都没有价值;如果自己看完后总结到博客中,这样我想这样价值更大些,也省的自己打这么多字;不过我打字的目的是为了更好的看书,敲(书写)的时候有思考时间,更仔细更深入,但是这个没有长久价值(直接看书多好),关于opengl概念的总论这个除外;好好取舍一下。