OpenGL学习笔记(一):状态管理与绘制
来源:互联网 发布:2djgame 邀请码 淘宝 编辑:程序博客网 时间:2024/06/06 03:06
作者: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_BUFFERGL_ELEMENT_ARRAY_BUFFERGL_PIXEL_PACK_BUFFERGL_PIXEL_UNPACK_BUFFERGL_COPY_READ_BUFFERGL_COPY_WRITE_BUFFERGL_TRANSFORM_FEEDBACK_BUFFERGL_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 2GLuint buffers[NUM_BUFFERS];GLfloat vertices[][3] = {...}GLubyte indices[][4] = {...};glGenBuffers(NUM_BUFFERS, buffers); // step 1glBindBuffer(GL_ARRAY_BUFFER, buffers[VERTICES]); // step 2glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // step 3glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0)); // step 4glEnableClientState(GL_VERTEX_ARRAY);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[INDICES]);// step 2glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);// step3glDrawElements(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
- OpenGL学习笔记(一):状态管理与绘制
- OpenGL学习(二) 状态管理和绘制几何物体
- opengl学习笔记——状态管理和绘制几何物体
- OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译
- OpenGL学习二:状态管理和绘制几何物体
- OpenGL学习日记1状态管理和绘制几何物体
- opengl编程指南笔记(三)第二章 状态管理和绘制几何物体
- 状态管理和绘制几何体(一)
- ASP.NET 2.0 状态管理 学习笔记(一)
- 初识openGL---openGL学习笔记(一)
- OpenGL编程指南之阅读笔记 第二章 状态管理和绘制几何物体
- Opengl学习笔记(一)
- OpenGL学习笔记(一)
- OpenGL学习笔记(一)
- OpenGL 学习笔记(一)
- opengl学习笔记(一)
- OpenGL学习笔记(一)
- OpenGL学习笔记(一)
- Cocos2d-x 粒子系统----实现下雪效果
- 节点事件相应
- poj2060 Taxi Cab Scheme
- 6月7日,福建省厦门市一公交车在行驶过程中突然起火
- Linux2.6 内核进程调度分析 .
- OpenGL学习笔记(一):状态管理与绘制
- 13:java并发编程总结
- UcHome二次开发调试技巧
- linux shell编程控制结构:expr、let、for、while、until、shift、if、case、break、continue、函数、select 学习笔记
- Cocos2d-x Tiled Map Editor(一)
- UCHome二次开发 模板基础语法
- 三维凸包
- Cocos2d-x Tiled Map Editor(二)
- Ext中左侧tree与右侧grid,grid分页问题