着色器对象
要创建着色器对象,你可以使用以下命令:
cpp
uint glCreateShader( enum type );
当创建着色器对象时,它最初是空的。type
参数指定要创建的着色器对象的类型,必须是指示相应着色器阶段的值之一。以下是该命令的简要概述:
- 参数 :
type
:指定要创建的着色器对象的类型。它必须是指示相应着色器阶段的值之一。- 顶点着色器:
GL_VERTEX_SHADER
- 片段着色器:
GL_FRAGMENT_SHADER
- 几何着色器:
GL_GEOMETRY_SHADER
- 细分控制着色器:
GL_TESS_CONTROL_SHADER
- 细分评估着色器:
GL_TESS_EVALUATION_SHADER
- 计算着色器:
GL_COMPUTE_SHADER
- 顶点着色器:
- 返回值 :
- 返回一个非零的无符号整数名称,可用于引用着色器对象。
该命令用于将源代码加载到指定的着色器对象中:
cpp
void glShaderSource( uint shader, sizei count, const char * const *string, const int *length );
shader
:是要加载源代码的着色器对象的名称。count
:表示字符串数组中的字符串数量。string
:是一个指向可选空终止字符字符串数组的指针数组,这些字符串共同构成了着色器的源代码。length
:是一个整数数组,包含每个字符串的字符数量。如果length
数组中的某个元素为负数,则对应的字符串以空字符终止。若length
参数为NULL,则认为string
参数中的所有字符串都是以空字符终止。
此命令将着色器对象的源代码设置为string
数组中的文本字符串。如果着色器先前已加载了源代码,则现有的源代码会被完全替换。任何传递给length
的值都不包括其计数内的空终止符。
加载到着色器对象中的字符串期望构成符合OpenGL着色语言规范的有效着色器源代码。如果之前通过ShaderBinary
命令将SPIR-V模块与shader
关联起来,执行此命令后会解除这种关联。在成功完成此命令后,shader
对象的SPIR-V_BINARY状态将被设置为FALSE。
加载着色器对象的源代码后,可以使用以下命令编译着色器对象:
cpp
void glCompileShader(uint shader);
glCompileShader
命令用于编译着色器对象shader
中的源代码。- 每个着色器对象都有一个布尔状态
COMPILE_STATUS
,它在编译过程中被修改。可以使用GetShaderiv
命令查询此状态。如果着色器编译成功且准备就绪,则此状态将设置为TRUE;否则设置为FALSE。编译可能因多种原因而失败,详细列在OpenGL着色语言规范中。 - 如果
glCompileShader
失败,则任何关于先前编译的信息都会丢失。因此,编译失败不会恢复着色器的旧状态。 - 使用
ShaderSource
更改着色器对象的源代码不会更改其编译状态或已编译的着色器代码。 - 每个着色器对象都有一个信息日志,它是一个文本字符串,作为编译过程的结果而被覆盖。可以使用
GetShaderInfoLog
查询此信息日志,以获取有关编译尝试的更多信息。
通过着色器编译器分配的资源可以通过以下命令释放,然而,实际应用中该函数并不常用,因为现代OpenGL实现通常会自动管理这些内部资源。
cpp
void glReleaseShaderCompiler(void);
- 这是应用程序提供的提示,不会阻止后续使用着色器编译器。
- 如果在调用
glReleaseShaderCompiler
之后加载和编译着色器源代码,只要着色器源代码没有错误,CompileShader
必须成功。 - 可以使用命令
GetShaderPrecisionFormat
确定着色器编译器支持的不同数字格式的范围和精度。
着色器对象可以使用以下命令删除:
cpp
void glDeleteShader(uint shader);
- 如果着色器对象未附加到任何程序对象,则立即删除。否则,着色器对象被标记为删除,并将在不再附加到任何程序对象时删除。
- 如果对象被标记为删除,其布尔状态位
DELETE_STATUS
将设置为true
。可以使用GetShaderiv
查询DELETE_STATUS
的值。 glDeleteShader
将静默忽略值为零的情况。
cpp
boolean glIsShader(uint shader);
- 如果
shader
是着色器对象的名称,则返回 TRUE。 - 如果
shader
是零,或者是一个不是着色器对象名称的非零值,则返回 FALSE。
着色器的二进制文件
glShaderBinary
命令用于加载预编译的着色器二进制文件,具体如下:
cpp
void glShaderBinary(GLsizei count, const GLuint *shaders,
GLenum binaryformat, const void *binary, GLsizei length);
- count:表示要加载二进制数据的着色器对象数量。
- shaders:指向包含着色器对象句柄(ID)数组的指针。每个句柄代表一种唯一的着色器类型,可以对应表7.1中列出的任何着色器阶段。
- binaryformat :指定预编译着色器代码的格式。若要加载SPIR-V模块,则应设置为
SHADER_BINARY_FORMAT_SPIR_V
。 - binary:指向客户端内存中预编译着色器二进制代码的起始位置。对于SPIR-V,这里应该是指向有效SPIR-V模块二进制的指针。
- length:表示二进制代码的长度(以字节为单位)。
成功加载SPIR-V模块后:
shaders
列表中的每个着色器对象都将与提供的SPIR-V模块关联起来。- 每个着色器的
SPIR_V_BINARY
状态将被设置为TRUE。 - 这些着色器的
COMPILE_STATUS
将被设为FALSE,因为它们不是通过OpenGL实现编译的;而是从预编译形式加载的。 - 从着色器对象中移除所有已存在的源代码字符串(通过
glShaderSource
指定的)。 - 关于先前编译的信息将丢失。
与SPIR-V模块关联的着色器必须通过调用glSpecializeShader
进行最终确定。
此外,OpenGL还提供了获取由扩展提供的此类格式令牌值的机制:
- 通过查询
NUM_SHADER_BINARY_FORMATS
的值可以获得支持的二进制格式总数。 - 通过查询
SHADER_BINARY_FORMATS
的值可以获得GL实现所支持的具体二进制格式列表。
根据着色器对象的类型,glShaderBinary
会分别加载单个二进制着色器,或者加载包含一组优化过的、存储在同一个二进制文件中的着色器的可执行二进制文件。
如果glShaderBinary
加载二进制文件失败,那么该二进制文件正被加载到的着色器对象的状态不会恢复。需要注意的是,如果着色器二进制接口得到支持,OpenGL实现可能要求在调用LinkProgram
时指定一组一起编译并优化过的着色器二进制。如果不提供这样的优化集合,可能会导致LinkProgram
操作失败。
着色器专业化(Shader Specialization)
与SPIR-V模块关联的着色器在被链接到程序对象之前必须先进行专业化处理。不过,无需在将着色器附加到程序对象前就对其进行专业化。
专业化过程主要完成两件事:
- 从SPIR-V模块中选择该着色器阶段的入口点名称。
- 设置SPIR-V模块中所有或部分特殊化常量的值。
要对由SPIR-V模块创建的着色器进行专业化,请调用以下函数:
cpp
void glSpecializeShader( uint shader, const char *pEntryPoint, uint umSpecializationConstants, const uint *pConstantIndex, const uint *pConstantValue );
shader
:指向包含未特殊化的SPIR-V的着色器对象名称,该对象是通过成功调用ShaderBinary
并将SPIR-V模块传递给它而创建的。pEntryPoint
:指向一个以空字符终止的UTF-8字符串指针,用于指定SPIR-V模块中用于此着色器的入口点名称。numSpecializationConstants
:指示在此调用中设置值的特殊化常量数量。pConstantIndex
:指向一个包含numSpecializationConstants
个无符号整数的数组,每个整数表示SPIR-V模块中要设置其值的一个特殊化常量的索引。pConstantValue
中的相应条目用于设置由pConstantIndex
中条目索引的特殊化常量的值。pConstantValue
:指向一个无符号整数数组,虽然该数组为整型,但每个元素都会转换为其在SPIR-V模块中对应的适当类型。因此,可以通过在pConstantValue
数组中包含IEEE-754位表示来设置浮点常量的值。未被pConstantIndex
引用的特殊化常量将保留其在SPIR-V模块中指定的默认值。
若着色器成功实现专业化,则着色器的编译状态会被设置为TRUE。如果失败,着色器的编译状态会设为FALSE,并且关于失败原因的更多信息可能可在着色器编译日志中获取。如果SPIR-V模块未能满足附录C中列出的要求,则可能会导致专业化失败。