在C++中使用GLSL语法
我编写了自定义向量类,例如vec2
,vec3
等,它们模仿GLSL类型并且大致如下:
struct vec3
{
inline vec3(float x, float y, float z)
: x(x), y(y), z(z) {}
union { float x, r, s; };
union { float y, g, t; };
union { float z, b, p; };
};
向量操作的实现方式如下:
inline vec3 operator +(vec3 a, vec3 b)
{
return vec3(a.x + b.x, a.y + b.y, a.z + b.z);
}
这使我能够使用类似GLSL的语法创建向量并访问其组件,并且几乎可以像处理数字类型一样对它们执行操作。联合体允许我无差别地将第一个坐标称为x
或r
,就像在GLSL中一样。例如:
vec3 point = vec3(1.f, 2.f, 3.f);
vec3 other = point + point;
point.x = other.b;
Swizzling的问题
GLSL也允许使用Swizzling操作,即使在组件之间有空隙。例如,p.yx
的行为类似于具有p
的x
和y
交换的vec2
。当没有重复的组件时,它也是一个lvalue。以下是一些例子:
other = point.xyy; /* Note: xyy, not xyz */
other.xz = point.xz;
point.xy = other.xx + vec2(1.0f, 2.0f);
现在可以使用标准的getter和setter来完成这个操作,例如
vec2 xy()
和void xy(vec2 val)
。这就是GLM库所做的事情。透明的getter和setter 然而,我设计了这种模式,让我在C++中完全实现相同的功能。由于所有内容都是POD-struct,因此我可以添加更多联合体:
template<int I, int J> struct MagicVec2
{
friend struct vec2;
inline vec2 operator =(vec2 that);
private:
float ptr[1 + (I > J ? I : J)];
};
template<int I, int J>
inline vec2 MagicVec2<I, J>::operator =(vec2 that)
{
ptr[I] = that.x; ptr[J] = that.y;
return *this;
}
例如,vec3
类变成了这样(我稍微简化了一下,例如在这里没有阻止xx
作为左值使用):
struct vec3
{
inline vec3(float x, float y, float z)
: x(x), y(y), z(z) {}
template<int I, int J, int K>
inline vec3(MagicVec3<I, J, K> const &v)
: x(v.ptr[I]), y(v.ptr[J]), z(v.ptr[K]) {}
union
{
struct { float x, y, z; };
struct { float r, g, b; };
struct { float s, t, p; };
MagicVec2<0,0> xx, rr, ss;
MagicVec2<0,1> xy, rg, st;
MagicVec2<0,2> xz, rb, sp;
MagicVec2<1,0> yx, gr, ts;
MagicVec2<1,1> yy, gg, tt;
MagicVec2<1,2> yz, gb, tp;
MagicVec2<2,0> zx, br, ps;
MagicVec2<2,1> zy, bg, pt;
MagicVec2<2,2> zz, bb, pp;
/* Also MagicVec3 and MagicVec4, of course */
};
};
基本上:我使用联合将向量的浮点分量与一个神奇的对象混合在一起,该对象实际上不是一个
vec2
,但可以隐式地转换为vec2
(因为有一个允许它的vec2
构造函数),并且可以赋值给vec2
(因为它的重载赋值运算符)。我对结果非常满意。上面的GLSL代码可以工作,我相信我得到了不错的类型安全性。而且我可以在我的C++代码中
#include
一个GLSL着色器。限制:
当然有限制。我知道以下限制:
-
sizeof(point.xz)
将是3*sizeof(float)
而不是预期的2*sizeof(float)
。这是设计上的问题,我不知道这是否会有问题。
- &foo.xz
不能用作vec2*
。这应该没问题,因为我只通过值传递这些对象。所以我的问题是:我可能忽略了什么,这会使我的生活难以忍受吗?此外,我还没有在其他地方找到这种模式,所以如果有人知道它的名称,我很感兴趣。
注意:我希望坚持使用C++98,但我确实依赖于编译器允许通过联合进行类型戏弄。我不想使用C++11的原因是我的目标平台中有几个缺乏编译器支持;所有对我有兴趣的编译器都支持类型戏弄。
vec3 v1
,似乎没有办法将v1.xz
正确地分配给实际的vec2 v2
,因为它看起来像是没有重新排序元素(v1.z
必须进入v2.y
,这是向量中的不同偏移)。你在帖子中漏掉了吗,还是我错过了什么? - Michael Madsenvec3::vec3(MagicVec3<>const&)
构造函数中的重新排序方式。 为了简洁起见,我省略了vec2
的相应构造函数,但它当然也存在。由于此构造函数不是explicit
,因此任何接受vec2
的方法也将隐式接受MagicVec2
。 因此,转换被延迟,直到实际需要其内容。 - sam hocevar