C语言中如何实现ShaderLanguage的swizzle操作?
摘要:swizzle 语法 经常编写着色器的同学应该对 swizzle(重排)语法非常熟悉,方便又灵活,可以说是用过一次便回味无穷。 代码 vec4 color = vec4(1.0, 0.5, 0.0, 1.0); vec3 rgb = col
swizzle 语法
经常编写着色器的同学应该对 swizzle(重排)语法非常熟悉,方便又灵活,可以说是用过一次便回味无穷。
代码
vec4 color = vec4(1.0, 0.5, 0.0, 1.0);
vec3 rgb = color.rgb; // { 1.0, 0.5, 0.0 }
vec2 xy = color.xy; // { 1.0, 0.5 }
vec4 bgra = color.bgra; // { 0.0, 0.5, 1.0, 1.0 }
可惜的是,C++ 中并不存在这样的语法,但是可以利用语法特性来模拟它,基本的思路是使用一个代理类来存储被操作点的引用以及需要操作的位置信息。
知名的 swizzle 实现
GLM
作为图形编程中的常客,GLM 提供了一套和 GLSL 相似的 swizzle 语法,只需要在使用前定义宏 GLM_FORCE_SWIZZLE 即可在向量类中使用了:
代码
#define GLM_FORCE_SWIZZLE
#include <glm/glm.hpp>
glm::vec3 v{1.0f, 2.0f, 3.0f};
v.xy = v.yz;
glm::vec3 reverse = v.zyx;
GLM 的实现方式是在类的未命名 union 内部定义一系列预定义的 swizzle 组合代理类,这些类只存储一个标记 vec 类内存起始位置的 char _buffer[1],而需要操作的位置信息则以模板参数形式编译进类型信息本身。
当一个 vec 类被构造时,这些代理类的 _buffer 即被初始化为 vec 实例的内存起始位置,当需要访问代理类的数据时,将 _buffer 转换为 vec 实例化时的数值类型指针,再取出位置信息作为索引即可实现对 vec 数据进行特定模式的访问。
GLM 的 swizzle 实现可以说是非常优雅,在形式和作用上是最还原 GLSL swizzle 语法的。
然而这种实现方式有一个缺点:所有的 swizzle 组合都是预定义的。GLM 的 vec 支持 2,3,4 维度的 swizzle,以 glm::vec3 来举例,它有 3 个元素,则能够组成的 swizzle 组合的总数为:
$$
\begin{aligned}
N=\sum_{i=2}^{4} 3^i=117 \\
\end{aligned}
$$
也就是说在 glm::vec3 的类定义中会有 117 个类似于 xx, xy, xxx, xyz, xxxx, xyzw 这样的成员(位于未命名 union 内)。
