应用步骤
- 着色器和程序对象的概述
- 创建和编译着色器
- 创建和链接程序 【上接OpenGL ES 3.0 | 着色器源码、实例 与 管线程序源码、实例 的联系与细节 以及 各自的应用流程和相关API】
- 获取和设置统一变量
- 获取和设置属性
- 着色器编译器和程序二进制代码
统一变量和属性
- 一旦链接了
程序对象
,就可以在对象
上进行许多查询
; - 首先,需要找出
程序
中的活动统一变量
; 统一变量(uniform)
是存储应用程序
通过OpenGL ES 3.0 API
传递给着色器
的只读 常数值
的变量
;-
统一变量
被组合成两类统一变量块
; 第一类是命名统一变量块
,统一变量
的值 由统一变量缓冲区对象
支持;命名统一变量块
被分配一个统一变量块索引
;
- 第二类是默认的
统一变量块
,用于在命名统一变量块
之外声明的统一变量
; 和命名统一变量块
不同,默认统一变量块
没有名称
或者统一变量块索引
;
- 如果
统一变量
在顶点着色器
和片段着色器
中均有声明
, 则声明的类型
必须相同,且在两个着色器
中的值也需相同
; - 在
链接
阶段,链接程序
将为程序中 与默认统一变量块
相关的活动统一变量
指定位置
; - 这些
位置
是 应用程序 用于 加载 统一变量的标志符; 链接程序
还将为与命名统一变量块
相关的活动统一变量
分配偏移
和跨距
(对于数组和矩阵类型的统一变量)
获取统一变量
- 查询程序中
活动统一变量
的列表(/ 数量)
; 获取 程序中 最大统一变量名称的字符数量(最大长度)
:
- 找出每个
统一变量
的细节
:- 拿到
类型
和名称
:
- 拿到
- 拿到其他指定的属性(
pname
指定的):
- 用
名称
拿到位置
:
- 有了
统一变量
的位置
及其类型
和数组大小
, 即可加载统一变量
的值
;
例程(查询活动统一变量的流程复盘)
统一变量缓冲区对象
- 可以使用
缓冲区对象
存储统一变量数据
, 从而在管线程序中
的着色器之间
甚至管线程序之间
共享统一变量
; - 这种
缓冲区对象
称作统一变量缓冲区对象
; - 使用
统一变量缓冲区对象
, 可以在更新大的统一变量块
时降低API开销
; 此外, 这种方法增加了统一变量
的可用存储
, 因为可以不受默认统一变量块大小
的限制;
- 可以使用
glBufferData
、glBufferSubData
、glMapBufferRange
和glUnmapBuffer
等函数 修改缓冲区对象
中的统一变量数据
;
统一变量缓冲区对象
中,统一变量
在内存中以如下的形式出现:
- 类型为
bool、int、uint
和float
的成员 保存在 内存的特定偏移
, 分别作为单个uint、int、uint
和float
类型的分量
; - 基本数据类型
bool、int、uint
和float
的向量
保存在始于特定偏移
的连续内存位置
中,(类似数组) 第一个分量在最低偏移处
; - (行优先、列优先的意义)
C列R行 的
列优先矩阵
被 当成 C浮点列向量
的一个数组
对待, 每个向量包含R个分量。(一个列有R行) 相类似, R行C列的行优先矩阵
被 当成 R浮点行向量
的一个数组对待, 每个向量包含C个分量。(一个行有C列) 列向量 或者 行向量 连续存储,但是有些实现的存储中可能有缺口; 矩阵中两个向量之间的偏移量
被称作列跨距
或者行跨距
(GL_UNIFORM_MATRIX_STRIDE
), 可以在链接的程序中 用glGetActiveUniformsiv
查询; 标量、向量
和矩阵的数组
按照元素的顺序
存储于内存
中, 成员0 放在最低偏移处
;数组
中每对元素
之间的偏移量
是一个常数
,称作数组跨距
(GL_UNIFORM_ARRAY_STRIDE
), 可以在 链接的程序中 用glGetActiveUniformsiv
查询;
- 除非使用
std140统一变量块布局(默认)
, 否则需要查询程序对象
得到字节偏移
和跨距
, 以在统一变量缓冲区对象
中设置统一变量数据
。std140布局
保证使用 由OpenGL ES 3.0规范
定义的明确布局规范
进行特定包装
; 因此,使用std140, 即可在不同的OpenGL ES 3.0实现之间
共享统一变量块
; 【其他包装格式(如下)可能使 某些OpenGL ES 3.0实现 以比std140布局
更紧凑
的方式 打包数据】
std140例程与规范
- 与
统一变量位置值
用于引用统一变量
类似 【有了统一变量
的位置
及其类型
和数组大小
, 即可加载统一变量
的值
】,统一变量块索引
用于引用统一变量块
, 用glGetUniformBlockIndex
检索统一变量块索引
:
- 【】用
程序句柄
、统一变量块名
,【】 【】拿到统一变量块索引
;【】 - 有了
统一变量块索引
, 可以用glGetActiveUniformBlockName
获取块名
, 用glGetActiveUniformBlockiv
获取统一变量块
的各种属性
【要获取什么属性, 由pname
指定, 在params
返回】;
- 有了
统一变量块索引
, 还可以用glUniformBlockBinding
将该索引
和程序实例
中的统一变量缓冲区绑定点【自定义的一个(点)序号】
关联;【bindingPoint】
- 可以用
glGenBuffers(bindingPoint, &bufferId)
最后, 可以用glBindBufferRange
或者glBindBufferBase
将统一变量缓冲区对象
绑定到GL_UNIFORM_BUFFER目标
和 程序实例中的统一变量块绑定点
【bindingPoint |glBindBufferBase
的 GLuint index】
编程统一变量块时,应该注意如下的限制:
顶点
或者片段着色器
使用的最大活动统一变量块
的数量 可以分别用带GL_MAX_VERTEX_UNIFORM_BLOCKS
或GL_MAX_FRAGMENT_UNIFORM_BLOCKS
参数的glGetIntegerv
查询, 所有实现中最小的支持数量为12;- 程序中所有着色器 使用的
最大活动统一变量块
的数量 可以用带GL_MAX_COMBINED_UNIFORM_BLOCKS
参数的glGetIntegerv
查询, 所有实现中最小的支持数量为24; - 每个
统一变量缓冲区
的最大可用存储量
可以 用带GL_MAX_UNIFORM_BLOCK_SIZE
参数的glGetInteger64v
查询, 返回的大小以字节数表示。 所有实现中最小的支持数量为16KB;
如果违反了这些限制,程序就无法链接;
程序示例,
说明如何用前面描述的命名统一变量块LightTransform
【std140例程处】
建立一个统一变量缓冲区对象
:
【思路:
块
与自定义绑定点
关联,
创建缓冲区实例对象
,
缓冲区实例对象
绑定到与块
关联的绑定点
,即用块
建立了一个统一变量缓冲区对象
】
【!!!!!!
注意注释,关于代码的功能,注释写的很清楚
!!!!!!】
可以看到glBindBufferBase
的API 二参
要传入的是
【GLuint index
| (准备要跟 程序实例中的统一变量缓冲区绑定点 进行绑定的)绑定索引
】,
而实际上 代码运用中,
传入的数值
跟传给glUniformBlockBinding
的API 第三个参数
一样
【GLunit blockBinding
| 统一变量缓冲区对象绑定点
】,
都是【bindingPoint | 绑定点】,
也就是它们两个形参位置,其实是传入的是一个东西;
因为
glUniformBlockBinding
的API 第三个参数
是自定义
的一个与索引
相关联的统一变量缓冲区绑定点
, 这个uniform block【统一变量块】
跟它的索引
、跟这个统一变量缓冲区绑定点
!三者!是【相互关联】的;
glBindBufferBase
的二参
即是这个uniform block【统一变量块】
对应的(统一变量缓冲区)绑定点
,
glBindBufferBase
便是
将buffer实例(的id)【三参】
绑定到绑定点(bindingPoint)【二参】
上来
Bind the 【buffer object】 to the 【uniform block binding point】
获取和设置属性
- 除了查询
程序对象
上的统一变量信息
之外, 还需要使用程序对象
设置顶点属性
; - 对
顶点属性
的查询和统一变量查询
非常相似; 可以用GL_ACTIVE_ATTRIBUTES
查询找到活动属性列表
, 可以用glGetActiveAttrib
找到某个属性的特性
。 然后,有一组例程可用于设置顶点数组,以加载顶点属性值。
参考自:
- 《OPENGL ES 3.0编程指南(第2版)》