本文档描述了实现自动检索OpenGL (ES) program所绑定shader的输入变量的思路。
1、背景
使用OpenGL ES 2.0时,编译shader并链接program后,我们会调用glGetAttribLocation
和glGetUniformLocation
获取shader定义的输入变量,或者使用glBindAttribLocation
在链接program前指定每个通用属性的位置。比如,GPUImage的各个子类需要注册自己的attribute、获取uniform位置,非常繁琐。
虽然,ES 3.0允许我们用layout(location = n)表示位置(这种做法类似Metal、Vulkan的风格),省去了动态获取属性位置的开销。但是,并不是每个GPU驱动都完全遵循KHR标准,为了让代码能在所有平台上正确运行,即便使用ES 3.0,我们还是避免使用layout布局语法,回归到ES 2.0这种动态获取或绑定变量位置的原始做法。
2、问题
如何在兼容ES 2.0、3.x shader的场景中动态获取输入变量信息?
3、解决方案
复杂想法:实现shader解析器
这是我的朴素想法。通过解析器,实现词法分析、语法分析、生成抽象语法树、类型检查等操作,得到合法的输入变量信息。为什么要这么做?我不止要解析shader的输入变量,还需要在此基础上实现shader语言的相互转换,当然这是个复杂的工程。-
取巧操作:利用OpenGL ES接口
其实,如果没有多shader语言转换需求,仅是单纯获取shader输入变量信息,那么OpenGL本身也提供了接口glGetProgramiv
、glGetActiveAttrib
和glGetActiveUniform
,非常方便。有几个值得注意的地方:
(1)glGetActiveAttrib
和glGetActiveUniform
的参数size对于普通变量为1,数组变量为数组长度。
(2)在shader中定义却没使用的输入,shader被编译后一般被优化掉,导致glGetActiveAttrib
和glGetActiveUniform
获取不到该变量。
(3)若使用了vertex shader内置输入变量,它也将成为激活的变量,例如gl_VertexID,示例如下:attribute index = 0, type = 1406, name = a_size, size = 1, location = 2 attribute index = 1, type = 1406, name = a_curtime, size = 1, location = 3 attribute index = 2, type = 8b50, name = a_position, size = 1, location = 0 attribute index = 3, type = 1404, name = gl_VertexID, size = 1, location = 5 attribute index = 4, type = 1406, name = a_lifetime, size = 1, location = 4 attribute index = 5, type = 8b50, name = a_velocity, size = 1, location = 1