第二章 状态管理和绘制几何物体 总结

目标 1. 清除窗口

  2.强制完成所有尚未执行的绘图操作

  3.在2d或3d空间绘制图元

  4.打开、关闭、查询状态

  5.控制图元显示

  6.在实心物体表面适当位置指定法线向量

  7.用顶点数组和缓冲区对象存储和访问几何数据。

  8.同时保存和恢复几个状态变量。

1.1 3种基本操作:清除窗口、绘制几何图形、绘制光栅对象。

2. 绘图工具箱:

  2.1 清除RGBA模式的窗口

glClearColor(R, G, B, A);    //将当前清除颜色设置成为一个状态变量
glClearDepth(1.0);        //指定深度缓冲区中每个像素要设置的值
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);  // 表示要清除的缓冲区为颜色缓冲区和深度缓冲区

  2.2 指定颜色

glColor3f(R,G,B);

  2.3 强制完成绘图操作

void glFlush(void);
void glFinish(void);

  2.4 坐标系统工具箱

  当窗口发生改变的时候,会发送一个时间作为通知。在glutReshapeFunc()中注册的那个函数会被调用。并且必须注册一个回调函数完成下面任务:

  1)重新建立一个矩形区域,把它作为新的渲染画布。

  2)定义一个用于绘制物体的坐标系统。

//一个2d的glutReshapeFunc(reshape)回调函数
void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei) h);//调整用于绘图的像素矩形,使它占据整个窗口
    glMatrixMode(GL_PROJECTION);        //接下来的三行代码表示让坐标系的左下角为原点(0,0)
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
}

3 描述图元:点、直线、多边形

  3.1 矩形:

    void glRect{sifd}(x1, y1, x2, y2);

  3.2 指定顶点:

    void glVertex[234]{sifd}();   //注意只有在glBegin 和glEnd之间时才有效

    void glVertex[234]{sifd}v();

  3.3 几何图元:

    将一组顶点放在glBegin 和glEnd之间, 传递给glBegin的参数决定了利用这些顶点所构成的几何图元的类型;

    有GL_POINTGL_LINESGL_LINE_STRIPGL_LINE_LOOPGL_TRIANGLESGL_TRIANGLES_STRIPGL_TRIANGLE_FANGL_QUADSGL_QUAD_STRIPGL_POLYGON

glBegin(GL_POLYGON);
    glVertex2f(0, 0);
    glVertex2f(0, 3);
    glVertex2f(3, 4);
glEnd();

    在glBegin和glEnd之间还可以用另外的一些函数来指定顶点的额外的属性数据, 例如:颜色glColor*(); 法线向量glNormal*(); 纹理坐标glTexCoord*();等

4. 基本状态管理

   物体在渲染的时候可能会用到光照、纹理、隐藏表面消除、雾或者其他的状态

  这都是由函数 void glEnable(); 和 void glDisalbe();来控制 , 例如有GL_BLEND, GL_DEPTH_TEST, GL_FOG, GL_LINE_STIPPE, GL_LIGHTING等。

   查询GLboolean glIsEnable(GLenum capability); 它返回GL_TRUEGL_FALSE,  还可以查询其他类型的。

5. 显示点、直线和多边形

  5.1 点

    void glPointSize();

  5.2 直线

    线宽: void glLineWidth(GLfloat width);

    点画线: void glLineStipple(GLint factor, GLushort pattern) ; 必须掉用glEnable(GL_LINE_STIPPLE)来启用点画线功能。

  5.3 多边形细节

    点、轮廓、实心:void glPolygonMode(GLenum face, GLenum mode);  参数face可以是GL_FRONT_AND_BACK, GL_FRONT, GL_BACK. 参数mode可以是GL_POINT, GL_LINE,GL_FILL

   反转和剔除多边形表面:void glFrontFace(GLenum mode), mode表示哪面为正面 GL_CCW表示逆时针方向为正面, GL_CW表示顺时针方向为正面。

   剔除:glEnable(GL_CULL_FACE); void glCullFace(GLenum mode);

   点画多边形:glEnable(GL_POLYGON_STIPPLE); void glPolygonStipple();

  5.4 法线

    void glNormal*()

  5.5 标记多边形的边界边

    void glEdgeFlag(GL_TRUE) 表示其后的顶点为边界边的起点。

6. 顶点数组(我认为是顶点相关的属性数组) 

  使用顶点数组对几何图形进行渲染需要三个步骤:

  1)激活顶点数组:顶点坐标(数组)、表面法线、RGBA颜色,辅助颜色、颜色索引、雾坐标、纹理坐标、多边形边界标志 

    void glEnableClientState(GLenum array);//GL_VERTEX_ARRAY, etc.

    void glDisableClientState();

  2)把数据放入数组中。

    void glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer);//size是每个顶点的坐标数量2/3/4,stride是连续顶点之间的字节偏移量。type为GL_INT等,pointer是第一个顶点的内存位置

    void glColorPointer()void glSecondaryColorPointerglIndexPointerglNormalPointerglFogCoordPointerglTexCoordPointerglEdgeFlagPointer

static GLint vertics[] = {
    25, 25,
    100, 325,
     175,25  
}   

static GLfloat colors[] = {
1.0, 0.2, 0.2,
0.2, 0.2, 1.0,
0.8, 1.0, 0.2  
}

glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

glColorPointer(3, GL_FLOAT, 0, colors);
glVertexPointer(2, GL_INT, 0, vertices);

 3)用这些数据绘制几何图形:解引用和渲染。

  解引用:客户端的数组中数据被提取发送到服务器,然后发送到图形处理管线进行渲染。

  a. void glArrayElement(GLint ith); 获取当前已启用数组(可以是多个)的第ith个顶点数据。

glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRYA);
glColorPointer(3, GL_FLOAT, 0, colors);
glVertexPointer(2, GL_INT, 0, vertices);

glBegin(GL_TRIANGLES)
glArrayElement(2);
glArrayElement(3);
glArrayElement(5);
glEnd();

  b. void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);   // mode表示要创建那种图元GL_POLYGON/GL_LINE_LOOP/GL_LINES/GL_POINTS, count表示解引用数组元素的个数,type表示类型为GL_UNSIGNED_BYTE等, indices表示元素的内存索引位置。

   c. void glMultiDrawElements(GLenum mode, GLsizei *count,  GLenum type, const GLvoid **indices, GLsizei primcount); //调用primcount个glDrawElements()函数

static GLubyte oneIndices[] = {0, 1, 2, 3, 4, 5, 6};
static GLubyte twoIndices[] = {7, 1, 8, 9, 10, 11}
static GLsizei count[] = {7, 6};
static GLvoid * indices[2] = {oneIndices, twoIndices};
glMultiDrawElements(GL_LINE_STRIP, count, GL_UNSIGNED_BYTE, indices, 2);

  d. void glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);//

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

  f. void glMultiDrawArrays(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);

  4)图元重启索引:

    void glPrimitiveRestartIndex(GLuint index);

    指定一个顶点数组元素索引,用来表示一个新的图元在渲染时的开始位置。当顶点数组元素索引的处理中遇到和index匹配的一个值的时候,就没有顶点数据要处理了,当前的图形图元就终止了,相同类型的一个新的图元开始了。通过调用glEnable()或者glDisable来控制图元重启GL_PRIMITIVE_RESTART。

   5)实例化绘制

    void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount); 相当于以下效果:

for(i = 0; i < primcount; i++)
{
    gl_instanceID = i;
    glDrawArrays(mode, first, count);
}
gl_InstanceID = 0;

    void glDrawElementsInstanced();

  6) 混合数组                                  

    void glInterleavedArray(GLenum format, GLsizei stride, const GLvoid * pointer);

7. 缓冲区对象

  1)创建缓冲区对象:利用opengl分配缓冲区对象标识符,调用void glGenBuffers(GLsizei n, GLuint *buffers);//buffers 数组返回n个当前未使用的名称,表示缓冲区对象

    调用GLboolean glIsBuffer(GLuint buffer);来判断一个标志符是否是当前被使用的缓冲区对象标识符

  2) 激活缓冲区对象:为了激活缓冲区对象,首先需要将它绑定,使用void glBindBuffer(GLenum target, GLuint buffer); //target必须要设置成GL_ARRYA_BUFFERGL_ELEMENT_ARRAY_BUFFER... , buffer指定了要绑定的缓冲区对象。

  3) 数据分配和初始化缓冲区对象: 一旦绑定了缓冲区对象就需要保留空间以存储数据,通过调用void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage); //将客户机中data指示的数据size个复制到服务器缓冲区。

  4)更新缓冲区对象的数据值:void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);//用data指向的数据更新与target相关联的当前绑定缓冲区对象中offset开始的size个字节数据。

   GLvoid *glMapBuffer(GLenum target, Glenum access); 这个函数返回一个指向缓冲区对象的指针。

  在完成了对数据存储的访问以后可以调用glUnmapBuffer()来取消对这个缓冲区的映射。

GLfloat*data;

data = (GLfloat*) glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);

if(data!=(GLfloat*)NULL)
{
  for(i = 0; i<8 ; i++)
{
  data[3*i + 2]*=2.0;//modify Z value
  glUnmapBuffer(GL_ARRAY_BUFFER);    
}  
}
else{}

  GLvoid* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);如果在access标志中使用了GL_MAP_FLUSH_EXPLICIT_BIT的话,还需要调用glFlushMappedBufferRange()向opengl表明映射缓冲区中的范围需要修改。

  5)在缓冲区对象之间复制数据

    void glCopyBufferSubData(GLenum readbuffer,GLenum writebuffer, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);

   6) 清除缓冲区对象 glDeleteBuffers();

#define VERTICES    0
#define INDICES    1
#define NUM_BUFFERS     2
GLuint buffer[NUM_BUFFERS];

GLfloat vertices[][3] = {
{-1.0, -1.0, -1.0},
{1.0, -1.0, -1.0},
{1.0, 1.0, -1.0},
...
{-1.0, 1, 1}
}

GLubyte indices[][4] = {
{0, 1, 2, 3}
...
{1, 5, 6, 2}
}

glGenBuffers(NUM_BUFFERS, buffers);

glBindBuffer(GL_ARRAY_BUFFER, buffers[VERTICES]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));  
glEnableClientState(GL_VERTEX_ARRAY);

glbindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[INDICES]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));

8、顶点数组对象

   1)创建顶点数组对象,调用void glGenVertexArrays(GLsizei n, GLuint *array); //返回n个当前未使用的名字,用作数组arrays中的顶点数组对象。

  2)使用GLvoid glBindVertexArray(GLuint array);

  3)glDeleteVertexArrays();

9. 属性组

  使用glPushAttrib()和glPopClientAttrib()来操作属性组。例如:GL_LINE_BIT包括了GL_LINE_SMOOTH在内的5个状态。

  OPENGL有两个属性堆栈,除了原来的属性堆栈,还有一个客户堆栈,是通过glPushClientAttrib()和glPopClientAttrib()来函数访问的。

原文地址:https://www.cnblogs.com/bubbler/p/3697737.html