状态量管理和绘制几何物体---3

来源:互联网 发布:淘宝中药央视 编辑:程序博客网 时间:2024/04/29 08:13

法线向量: 

顾名思义, 就是垂直与表面方向的向量。对与平面, 一个法线向量就可以标识出平面上所有点的法向。 对与曲面, 每一个点的法向是不同。因此OPENGL提供函数允许为每个多边形或者每个顶点指定法线的。程序员可以用glNormal3×来为指定的顶点赋以当前法线。 例如: glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz);

然后调用glVertex×() 函数为指定的顶点赋以当前法线。要为每一个物体寻找法线需要完成一些计算----求导,才能利用法线技术达到特定的效果。法线技术一般用在光照技术里面--根据光线与法线向量之间的位置来确定该顶点接收多少光照。

一些注意细节:

1.在一个表面给定处有两个方向相反的向量垂直于此表面。习惯上:法线是指向模型表面外侧的向量。

2. 法线向量仅仅只表示方向,其长度多少不重要。 但是在光照计算执行之前, 必须将其变换为长度1. 可以用x, y, z 都除以(x^2 + y^2 + z^2)^(1/2).  如果模型执行了旋转和平移, 不会改变法线长度,但是执行了不规则的变换, 则应该在变换之后让OPENGL 自动地将其法线向量归一化。 可以调用glEnable(GL_NORMALIZE)。如果支持单位长度法线, 并且只进行了均匀缩放, 就可以通过一个来自模型视图变换矩阵的常量因子, 使用glEnable(GL_RESCALE_NORMAL) 来缩放法线, 将其变换为一个单位长度。上述两种都需要额外的计算,致使程序的性能降低。

 

顶点数组:

当我们绘制一个20个顶点的多边形,我们要调用22个函数。两个是glBegin() , glEnd(), 其他的就是glVertex*(). 如果还要在顶点加上附加信息, 如前面的边界标志, 颜色, 顶点法线。那么函数调用的就更加频繁了。降低了性能。

还有相邻多边形之间所共有的顶点的冗余处理问题。 如一个立方体。 有6个面,8个共有顶点。如果向上面一样,就相当于为每个顶点指定了三次,总共处理了3×8个顶点。

基于上述问题:

opengl提供了顶点数组函数 :可以把20个顶点放入一个数组, 从而使用一个函数对其调用,如果每一个顶点也有一个颜色, 则可以将其所有的20个表面法线放入另外一个数组, 并且也可以用一个函数对其调用。

那么该如何做呢:

1. 最多可以激活6个数组, 它们中的每一个用于保存不同类型的数据: 顶点坐标, RGBA颜色、颜色索引, 表面法线、纹理坐标、多边形边界标志。

2.将数据放入一个数组或者几个数组中。 在cs模式下, 数据被保存在客户的地址空间中。

3. 使用数据来绘制几何体。 通过解除指针的参考,OPENGL从所有被激活的数组中获得数据。在cs模式下, 数据被传到服务器的地址空间中: 有3中解除指针参考的方法:

   a、 存取单个数组元素(随机地四处转发)

   b、 创建一个由单个数组元素组成的列表(顺序的四处转发)

   c、 处理顺序的数组元素。

 对上述第一步: 使用:glEnableClientState (GLenum array); 参数可以当然也有6种:

GL_VERTEX_ARRAY       -----顶点坐标
GL_NORMAL_ARRAY      -----法线向量
GL_COLOR_ARRAY        ------顶点颜色
GL_INDEX_ARRAY        -------颜色索引
GL_TEXTURE_COORD_ARRAY -----纹理坐标
GL_EDGE_FLAG_ARRAY    -----边界标志。

那么相应的关闭激活的函数: glDisableClientState (GLenum array);

 

第二步: 有6个不同的函数可以用于指定数组.  例如指定顶点坐标的:void glVertexPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);

size :每个顶点坐标数目,必须为2, 3,4中的一个。

type:数组中每个坐标的数据类型:GL_SHORT, GL_INT,  GL_FLOAT. GL_DOUBLE.

stride : 连续顶点之间的字节偏移。

pointer : 数组中第一个顶点的第一坐标的内存地址。

其他三个参数都好理解,就是stride这个参数不好理解, 是吧?那举个例子:

    GLfloat intertwined[] = {        1.0, 0.2, 1.0, 100.0, 100.0, 0.0,        1.0, 0.2, 0.2, 0.0, 200.0, 0.0,        1.0, 1.0, 0.2, 100.0, 300.0, 0.0,        0.2, 1.0, 0.2, 200.0, 300.0, 0.0 };    glColorPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), &intertwined[0]);    glVertexPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), &intertwined[3]);

前三个是一个颜色坐标值, 后三个是顶点坐标值,然后这样交替。

其他5个类似的函数:

void glColorPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);

void glIndexPointer (GLenum type, GLsizei stride, const GLvoid *pointer); 尺寸为1

void glNormalPointer (GLenum type, GLsizei stride, const GLvoid *pointer); 尺寸为3

void glTexCoordPointer (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);

void glEdgeFlagPointer (GLsizei stride, const GLvoid *pointer); 尺寸为1

 

第三步:解除参考和渲染。

1.解除一个数组元素的参考:

利用glArrayElement(GLint  ith); 如下代码:

GLfloat intertwined[] = {        1.0, 0.2, 1.0, 10.0, 10.0, 0.0,        1.0, 0.2, 0.2, 0.0, 20.0, 0.0,        1.0, 1.0, 0.2, 10.0, 30.0, 0.0,        0.2, 1.0, 0.2, 20.0, 30.0, 0.0 };    glEnableClientState(GL_COLOR_ARRAY);    glEnableClientState(GL_VERTEX_ARRAY);    glColorPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), &intertwined[0]);    glVertexPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), &intertwined[3]);    glClearColor(0, 0, 0, 0);    glClear(GL_COLOR_BUFFER_BIT);    glBegin(GL_TRIANGLES);    glArrayElement(0); // 第 0个顶点    glArrayElement(2);    glArrayElement(3);    glEnd();    glFlush();

注意在完成图元之前,不要在glBegin 和 glEnd 之间改变任何可能被存取的数组的内容。

2. 解除数组元素的列表:

利用函数: glDrawElements,

上面glBegin 到最底下的代码可以也可这样实现:

GLushort indices[] = { 0, 2, 3 };    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, indices);    glFlush();

第一个参数指定创建什么类型的图元, 第二个参数指定数组的个数,第三个参数指定这个数组的数据类型, 最后一个是数组的指针。

上面的两个函数“四处转发” 数据数组的时, 函数 glDrawArrays() 则直接在数据数组中进行。

void  glDrawArrays (GLenum mode, GLint first, GLsizei count);

该函数使用每一个激活数组的起始于first, 结束与first + count -1 的数组元素, 来构造一系列的几何图元。mode 和glBegin一样。

 

交叉数组, 它一次实现了上面的第一步和第二步, 函数为:

glInterleavedArrays (GLenum format, GLsizei stride, const GLvoid *pointer); 具体使用可以google。

 

属性组

OPENGL 把相关的状态变量归纳在一个属性组中。

例如: GL_LINE_BIT 属性包括了五个状态变量: 线宽、 GL_LINE_STIPPLE激活状态、线段的点画线模式、线段的点画线计数器和GL_LINE_SMOOTH激活状态。

使用glPushAttrib() 和 glPopAttrib(), 可以立刻保存 和恢复所有的这 5个状态变量。。

在opengl1.1中, 有两个不同的属性堆栈:原始的矩阵堆栈, 客户属性堆栈。 客户属性堆栈可以用函数 glPushClientAttrib() 和 glPopClientAttrib() 。。因为一些值被保存在硬件当中,所以如果自己编写函数来保存,存取的开销比较大。

大约总共有20个属性组和2个客户组。因为属性都保存在堆栈上, 若向一个满栈中压入数据和从空栈中弹出数据,会发生错误。我们可以用

void glGetIntegerv (GLenum pname, GLint *params);

传递: GL_MAX_ATTRIB_STACK_DEPTH  和GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 获得堆栈深度。

 

0 0
原创粉丝点击