作者:yurunsun@gmail.com 新浪微博@孙雨润 新浪博客 CSDN博客
日期: 2013-6-11
本章内容:
- 用任意一种颜色清除窗口
- 在二维或三维空间绘制几何图元:点、直线、多边形
- 打开/关闭/查询状态
- 控制几何图元的显示:线、多边形
- 在实心物体表面适当位置指定法线向量
- 用顶点数组和缓冲区对象存储和访问几何数据
- 同时保存和恢复几个状态变量
- 显示列表
1. 绘图工具箱
-
清除窗口
glClearColor(0.0, 0.0, 0.0, 0.0); glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
指定颜色
glColor3f(0.0, 1.0, 0.0);
2. 绘制点、直线、多边形
-
指定顶点
glVertex3f(1.0, 1.0, 1.0);
-
几何图元
把一组顶点放在一对glBegin()和glEnd()之间用来绘制几何图元:
glBegin(GL_POLYGON) glVertex3f(-1.0, -1.0, 0.0); glVertex3f(1.0, -1.0, 0.0); glVertex3f(1.0, 1.0, 0.0); glVertex3f(-1.0, 1.0, 0.0); glEnd();
除
GL_POLYGON
以外的图元名称参见手册。
3. 打开/关闭/查询状态
glEnable(GL_LIGHTING);
glDisable(GL_FOG);
glIsEnabled(GL_DEPTH_TEST);
glGetBooleanv(GL_BLEND, bBlend);
glGetIntegerv(GL_CURRENT_COLOR, colorArr);
4. 控制几何图元的显示
-
点的大小
glPointSize(2.0);
-
直线的宽度、点画线
glLineWidth(3.0); glLineStipple(1, 0x3F07); glEnable(GL_LINE_STIPPLE);
-
多边形的点、线、填充模式,正面与丢弃
glPolygonMode(GL_FRONT, GL_FILL); glPolygonMode(GL_BACK, GL_LINE); glFrontFace(GL_CCW); //逆时针顶点方向为正面 glCullFace(GL_BACK); //转换到屏幕坐标前丢弃背面 glEnable(GL_CULL_FACE);
5. 法线
-
概念
- 法线是一条垂直于某表面的方向向量
- 平表面上每个点的垂直方向都相同,曲面上每个点的法线可能不同
- OpenGL中既可以为每个多边形指定一条法线,也可以为多边形的每个顶点分别指定一条法线
- 只能在顶点处分配法线
-
实现
glBegin (GL_POLYGON); glNormal3fv(n0); glVertex3fv(v0); glNormal3fv(n1); glVertex3fv(v1); glNormal3fv(n3); glVertex3fv(v3); glEnd();
开启自动对法线归一化的功能:
glEnable(GL_NORMALIZE);
6. 顶点数组(VA)
采用之前办法绘制20条边的多边形需要22个函数调用:指定20次顶点、一对glBegin/glEnd
.绘制立方体需要为每个顶点重复指定3次。通过使用OpenGL的顶点数组,只需要一次调用。步骤如下:
- 启用数组(同时最多8个)
- 放入数据
- 绘制图形:随机/系统/线性 访问
6.1. 启用数组
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
例如关掉光照后禁用改变法线状态的值,则调用
glDisableClientState(GL_NORMAL_ARRAY);
之所以使用新的函数名而不是`glEnable`,是因为顶点数组存储在client端,不能放入显示列表中。而`glEnable`需要能够放入server端显示列表
6.2. 放入数据
glColorPointer(3, GL_FLOAT, 0, colorArr);
glVertexPointer(3, GL_FLOAT, 0, vertexArr);
6.3. 解引用并绘制图形
在解引用前数据一直保存在client端,内容很容易被修改。在此步骤中数组数据被发送到server端进行绘制。
glArrayElement(GLint ith);
表示获取当前所有已开启数组的第ith顶点的数据。分别调用:
glEdgeFlag3fv();
glTexCoord3fv();
glColor3fv();
glSecondaryColor3fv();
glIndexfv();
glNormal3fv();
glFogCoordfv();
glVertex3fv();
例如:
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(3, GL_FLOAT, 0, colorArr);
glVertexPointer(3, GL_FLOAT, 0, vertexArr);
glBegin(GL_TRIANGLES);
glArrayElement(2);
glArrayELement(3);
glArrayELement(5);
glEnd();
与下面效果相同:
glBegin(GL_TRIANGLES);
glColor3fv(colorArr + 2*3);
glVertex3fv(vertexArr + 2*3);
glColor3fv(colorArr + 2*3);
glVertex3fv(vertexArr + 2*3);
glColor3fv(colorArr + 2*5);
glVertex3fv(vertexArr + 2*5);
glEnd();
6.4 解引用数组元素的一个list
与glArrayElement()
类似的函数还有:
glDrawElements();
glMultiDrawElements();
glDrawRangeElements();
-
glDrawElements()
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices);
相当于
glBegin(mode); for (int i = 0; i < count; ++i) { glArrayElement(indices[i]); }
注意:不要把
glDrawElements()
放在glBegin()/glEnd()
之间 -
glMultiDrawElements()
void glMultiDrawElements(GLenum mode, GLsizei* count, GLenum type, const GLvoid** indices, GLsizei primcount);
相当于
for (int i = 0; i < premcount; ++i) { if (count[i] > 0) { glDrawElements(mode, count[i], type, indices[i]); } }
-
glDrawRangElements()
在
glDrawElements()基础上加入
start/end`的限制,其外的顶点会被丢弃。
6.5 解引用数组元素的一个sequence
上述函数均能随机存储,glDrawArrays()
只能按顺序访问:
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
相当于:
glBegin (mode);
for (int i = 0; i < count; ++i) {
glArrayElement(first + i);
}
glEnd();
而`glMultiDrawArrays()`则提供类似的组合调用功能。
下面两章VBO VAO的内容不被opengl 1.1支持 仅供了解
7. 缓存对象(BO)
我们向OpenGL发送大量顶点、向量等数据,这种传输可能是简单的从内存copy到显卡;但OpenGL是按照C/S模式设计的,在OpenGL需要数据的任何时候都必须把数据从client端内存send到server端显卡。如果分布式渲染,数据传输效率很低。因此引入buffer object, 允许程序显示指定哪些数据缓存在server端。
- v1.5支持顶点数据缓存
- v2.1支持像素数据缓存
- v3.1支持统一缓存对象(uniform buffer object),存储成块的用于着色器的统一变量数据
7.1 创建缓存对象
void glGenBuffers(GLsizei n, GLuint* buffers);
在buffer数组中返回n个当前未使用过的名称,表示缓存对象。
7.2 激活缓冲区对象
void glBindBuffer(GLenum target, GLuint buffer);
为激活缓存对象首先需要将其绑定,表示选择未来的操作将影响哪个缓存对象,可选值有:
GL_ARRAY_BUFFER
GL_ELEMENT_ARRAY_BUFFER
GL_PIXEL_PACK_BUFFER
GL_PIXEL_UNPACK_BUFFER
GL_COPY_READ_BUFFER
GL_COPY_WRITE_BUFFER
GL_TRANSFORM_FEEDBACK_BUFFER
GL_UNIFORM_BUFFER
7.3 赋值/初始化缓存对象
void glBufferData(GLenum target, GLsizeiptre size, const GLvoid* data, GLenum usage);
分配size各字节的server端内存用于存储顶点数据或索引。如果data是NULL则纯粹分配内存,如果是指向client端内存的指针则会讲数据copy到server端。
7.4 更新缓存对象
两种更新方法:
-
直接替换内存
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
-
获取内存指针,灵活修改
GLvoid *glMapBuffer(GLenum target, GLenum access); GLboolean glUnmapBuffer(GLenum target);
7.5 缓存对象之间copy data
V3.1之前分两步:
- Data从缓存对象copy到client端内存
- 绑定到新的缓存对象
V3.1使用新函数:
void glCopyBufferSubData(GLenum readbuffer, GLenum writebuffer, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);
7.6 清除缓存对象
void glDeleteBuffers(GLsizei n, const GLuint *buffers);
7.7 使用缓存对象存储顶点数组
步骤如下:
- 生成缓存对象id
- 绑定缓存对象,确定用于存储顶点数据还是索引
- 请求数据内存
- 指定相对于缓存起始位置的偏移
- 绑定缓存对象用于渲染
- 使用顶点数组渲染函数
完整示例如下:
#define VERTICES 0
#define INDICES 1
#define NUM_BUFFERS 2
GLuint buffers[NUM_BUFFERS];
GLfloat vertices[][3] = {...}
GLubyte indices[][4] = {...};
glGenBuffers(NUM_BUFFERS, buffers); // step 1
glBindBuffer(GL_ARRAY_BUFFER, buffers[VERTICES]); // step 2
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // step 3
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0)); // step 4
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[INDICES]);// step 2
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);// step3
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0)); // step6
8. 顶点数组对象(VAO)
大数据量可能要求在每个帧的多组顶点数组间切换,glVertexPointer()
这样的函数调用次数会随之增大。顶点数组对象则绑定了调用的集合,用来设置顶点数组的状态。
void glGenVertexArrays(GLsizei n, GLuint *arrays);
void gBindVertexArray(GLuint array);
void glDeleteVertexArrays(GLsizei n, GLuint *arrays);
9. 显示列表
opengl采用CS模式,每次绘制需要将顶点等数据从client发送到server。使用显示列表将绘图命令保存在server端,同时还能避免重复计算。
但是显示列表中的值不能在以后进行修改,需要删除显示列表并创建一个新的列表。
显示列表在以下领域中优化效果最明显:
- 矩阵操作
- 对位图和图像的光栅化
- 光源、材料属性和光照模型
9.1 命名、创建、删除
GLuint glGenLists(GLsizei range);
void glNewList(GLuint list, GLenum mode);
void glEndList(void);
GLboolean glIsList(GLuint list);
void glDeleteLists(GLuint list, GLsizei range);
GL_COMPILE; // 不立即执行
GL_COMPILE_AND_EXECUTE // 立即执行
设置client端的函数以及用于提取状态值的函数无法存储在显示列表中。
9.2 执行
-
一般形式
void glCallList(GLuint list);
-
层次式显示列表
glNewList(listIndex,GL_COMPILE); glCallList(handlebars); glCallList(frame); glTranslatef(1.0, 0.0, 0.0); glCallList(wheel); glTranslatef(3.0, 0.0, 0.0); glCallList(wheel); glEndList();
-
执行多个
void glListBase(GLuint base);
这个函数指定一个偏移量,将与glCallLists()函数中显示列表索引相加,以获得最终的显示列表索引。默认为0.此函数对于
glCallList()/glNewList()
函数没有效果void glCallLists(GLsizein, GLenum type, const GLvoid *lists);
9.3 用显示列表管理状态变量
如果将渲染命令中包含状态改变的命令,这些状态会在执行时被修改并继续保持,但有时候我们希望执行显示列表之后恢复原来的状态。可以使用glPushAttrib()
函数保存一组状态变量,并用glPopAttrib()
函数在需要时恢复这些值。
OpenGL将相关的状态变量进行归组,称为属性组,例如GL_LINE_BIT
属性包括宽度、点画、抗锯齿等共5个状态,使用glPushAttrib()/glPopAttrib()
可以同时保存和恢复全部这5个状态。
- 如果这篇文章对您有帮助,请到CSDN博客留言;
- 转载请注明:来自雨润的技术博客 http://blog.csdn.net/sunyurun