《OpenGL编程指南》 笔记三 OpenGL绘制方式

第三章 OpenGL绘制方式

3.1 OpenGL图元

3.1.1 点

  • glPointSize(GLfloat size) 设置固定的像素大小;如果没有开启GL_PROGRAM_POINT_SIZE,那么它将被用于设置点的大小
  • 如果使用OpenGL来渲染点,那么点的每个片元都会执行片元着色器
点精灵
  • 我们可以在片元着色器中,通过解析纹理图来实现点的着色
  • gl_PointCoord 当前片元在点区域内的坐标信息
  • 如果将gl_PointCoord做为输入纹理坐标使用,那么就可以使用位图和纹理替代简单的方块颜色

3.1.2 线、条带与循环线

  • 闭合的多段线叫做循环线(line loop)
  • 开放的多段线叫做条带线(line strip)

3.1.3 三角形、条带与扇面

  • OpenGL对于模型三角形共享边的光栅化过程不会产生任何裂缝,也不会产生重复的绘制
  • OpenGL图元的模式标识
图元类型OpenGL枚举
GL_POINTS
线GL_LINES
条带线GL_LINE_STRIP
循环线GL_LINE_LOOP
独立三角形GL_TRIANGLES
三角形条带GL_TRIANGLE_STRIP
三角形扇面GL_TRIANGLE_FAN
将多边形渲染为点集、轮廓线或者实体
  • void glPolygonMode(GLenum face, GLenum mode)
  • face: GL_FRONT_AND_BACK
  • mode: GL_POINT, GL_LINE, GL_FILL
多边形面的反转和裁剪
  • void glFrontFace(GLenum mode)
  • mode: GL_CCW 顶点逆时针排序的面做为正面
    GL_CW 顶点顺时针排序的面做为正面
  • void glCullFace(GLenum mode) 裁剪指定的面,mode的值可以是:GL_FRONT, GL_BACK, GL_FRONT_AND_BACK分别表示正面,反面或者所有多边形。要是命令生效,还要使用glEnable(GL_CULL_FACE)
高级技巧
  • 判断多边形的面是正面还是背面,需要依赖于这个多边形在窗口坐标系下的面积计算

3.2 OpenGL缓存数据

3.2.1 创建与分配缓存

  • void glGenBuffers(GLsizei n, GLuint* buffer) 返回n个当前未使用的缓存对象名称,并保存到buffers数组中
  • 可用的缓存目标
    在这里插入图片描述
  • void glBindBuffer(GLenum target, GLuint buffer) 将名称为buffer的缓存对象绑定到target所指的缓存结合点,如果buffer是第一次被指定,那么它所对应的缓存对象也将被创建

3.2.2 向缓存输入和输出数据

  • void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) 为绑定到target的缓存对象分配size大小(单位字节)的存储空间;如果data不为空,那么将使用data所在的内存区域的内容来初始化整个空间;usage允许应用程序向OpenGL端发出一个提示,指示缓存中的数据可能具备一些特定用途
  • 缓存用途表示符
    在这里插入图片描述
缓存的部分初始化
  • void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) 使用新的数据替换缓存对象中的部分数据;绑定到target的缓存对象要从offset字节处开始,使用地址为data、大小为size的数据块来进行更新
  • glClearBufferData(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void* data)
    glCearBufferSubData(GLenum target, GLenum internalformat, GLintptr offset, GLintptr size, GLenum format, GLenum type, const void* data)
    清除缓存对象中所有或者部分数据;绑定到target的缓存对象存储空间将使用data中存储的数据进行填充;format和type分别指定了data对应数据的格式和类型;将数据格式转换到internalformat所指定的格式,然后填充缓存数据的指定区域范围
  • 缓存对象中的数据也可以通过glCopyBufferSubData(…)函数互相进行拷贝
读取缓存的内容
  • glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid* data) 获取当前绑定到target的缓存对象中的部分或者全部数据

3.2.3 访问缓存的内容

  • 直接在应用程序中对OpenGL管理的内存进行访问。glMapBuffer(…)
  • void* glMapBuffer(GLenum target, GLenum access) 将当前绑定到target的缓存对象的整个数据区域映射到客户端的地址空间中
  • access可以设置的访问模式
标识符意义
GL_READ_ONLY应用程序仅对OpenGL映射的内存区域执行读操作
GL_WRITE_ONLY应用程序仅对OpenGL映射的内存区域执行写操作
GL_READ_WRITE应用程序仅对OpenGL映射的内存区域可能执行读或者写操作
  • 当结束数据的读取或写入到缓存对象的操作后,必须使用glUnmapBuffer()执行接触映射操作
异步和显示的映射
  • 为了避免glMapBuffer()可能造成的映射问题,glMapBufferRange()函数使用额外的标识符来更精确地设置访问模式
  • void* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) 将缓存对象数据的全部或者一部分映射到应用程序的地址空间中;access中必须包含GL_MAP_READ_BIT和GL_MAP_WRITE_BIT中的一个或者连个,以确认应用程序是否要对映射数据进行读操作、写操作或者两个都有
  • access中还可以包含的其他标识符
    在这里插入图片描述
  • glFlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) 通知OpenGL,绑定到target的映射缓存中由offset和length所划分的区域已经发生了修改,许哟啊立即更新到缓存对象的数据区域中

3.2.4 丢弃缓存数据

  • void glInvalidateBufferData(GLuint buffer)
    void glInvalidateBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr length)
    通知OpenGL,应用程序已经完成对缓存对象中给定范围内容的操作,因此可以随时根据实际情况抛弃数据

3.3 顶点规范

3.3.1 深入讨论VertexAttribPointer

  • void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, cont GLvoid* pointer) 设置顶点属性在index位置可访问的数据值;输入的顶点数据 与 创建的顶点属性数组中的对象 的关联关系
  • 当type的类型是整型的一种时,opengl必须将这些数据转换为浮点数,才可以将他们读取到浮点数的顶点属性中;执行这一转换的过程可以通过normalize参数来控制;如果normalize为GL_FALSE,那么整数将直接被强制转化为浮点数的形式,然后再传入到顶点着色器中;如果normalize为GL_TRUE,那么将经过一个归一化计算后再传入顶点着色器
整型顶点属性
  • 大范围的整数值不能直接使用浮点型属性传入顶点着色器中,因为会造成精度丢失
  • glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)
双精度顶点属性
  • glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)
  • normalize 只是用来处理那些不适宜直接使用整型类型的情况
顶点属性的压缩数据格式
  • type的类型使用特殊的值:
    1. GL_INT_2_10_10_10_REV
    2. GL_UNSIGNED_2_10_10_10_REV

3.3.2 静态顶点属性的规范

  • 如果顶点属性对应的属性数组没有启用的话,opengl会使用静态顶点属性

3.4 OpenGL的绘制命令

  • 绘制命令大致分为

    1. 索引形式
    2. 非索引形式
  • void glDrawArrays(GLenum mode, GLint first, GLsizei count) 使用数组元素建立连续的几何图元序列

  • void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) 使用count个元素来定义一系列几何图元;元素的索引值保存在一个绑定到GL_ELEMENT_ARRAY_BUFFER的缓存中(元素数组缓存,element array buffer);indices定义了元素数组缓存中的偏移地址,也就是索引数据开始的位置;type必须是GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT中的一个,它给出了元素数组缓存中索引数据的类型

  • 上面两个函数都会从当前启用的顶点属性数组中读取顶点信息,然后使用他们来构建mode指定的图元类型顶点属性数组的启用通过glEnableVertexAttribArray()来完成

  • glDrawElementBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) 它的第i个元素在传入绘制命令时,实际上读取的是各个顶点属性数组中的第indices[i]+basevertex个元素

  • glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid* indices) 这是一种更严格的形式,它相当于规定:元素数组缓存中所包含的任何一个索引值(来自indices)都会落入到start和end所定义的范围当中

  • void glDrawRangeElementsBaseVertex(…) 相当于前边两个的组合

  • 间接绘制函数,他们的参数不是直接从程序中得到,而是从缓存对象中获取;如果要使用的话,必须先将一个缓存对象绑定到GL_DRAW_INDIRECT_BUFFER目标上

  • void glDrawArraysIndirect(GLenum mode, const GLvoid* indirect) 从绑定到GL_DRAW_INDIRECT_BUFFER的缓存(间接绘制缓存,draw indirect buffer)中获取结构体数据

  • glDrawElementsIndirect(…) 绘制命令的参数是从GL_DRAW_INDIRECT_BUFFER的缓存中获取的

  • void glMultiDrawArrays(GLenum mode, const GLint* first, const GLint* count, GLsizei primcount) 相当于执行下面的代码

void glMultiDrawArrays(GLenum mode, const GLint* first, const GLint* count, GLsizei primcount)
{
    for ( GLsizei i = 0; i < primcount; ++i)
    {
        glDrawArrays(mode, first[i], count[i];
    }
}
  • void glMultiDrawElements(…) 同上
  • void glMultiDrawElementsBaseVertex(…) 同上
  • void glMultiDrawArraysIndirect(…)
  • void glMultiDrawElementsIndirect(…)

3.4.1 图元的重启动

  • void glPrimitiveRestartIndex(GLuint index) 设置一个顶点数组元素的索引值,用来指定渲染过程中,从什么地方启动新的图元绘制。如果在处理顶点数组元素索引的过程中遇到了一个符合该索引的数值,那么系统不会处理它对应的顶点数据,而是终止当前的图元绘制,并从下一个顶点重新开始渲染同一类型的图元集合
  • glEnalble(GL_PRIMITIVE_RESTART) 启用图元重启动特性

3.5 多实例渲染

  • 实例化或者多实例渲染是一种连续执行多条相同的渲染命令的方法,并且每个渲染命令所产生的结果都会有轻微的差异。这是一种非常有效的,使用少量API调用来渲染大量几何体的方法
  • void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primCount)
  • void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei primCount)
  • void glDrawElementsInstancedBaseVertex(…)

3.5.1 多实例的顶点属性

  • void glVertexAttribDivisor(GLuint index, GLuint divisor) 启用多实例的顶点属性;如果divisor的值是0的话,那么该顶点属性的多实例特性将被禁用;而其他的值则表示顶点着色器,每divisor个实例都会分配一个新的属性值
  • glVertexAttribDivisor(…) 函数用于控制顶点属性跟新的频率

3.5.2 在着色器中使用实例计数器

  • 除了多实例的顶点属性外,当前实例的索引值可以在顶点着色器中通过内置gl_InstanceID变量获取

3.5.3 多实例方法的回顾

  • 如果要在程序中使用多实例的方法,应当:
    • 为准备实例化的内容创建顶点着色器输入
    • 使用glVertexAttribDivisor()设置顶点属性的分割频率
    • 在顶点着色器中使用内置gl_InstanceID变量
    • 使用渲染函数的多实例版本
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值