如何为 GLSL / WebGL 以浮点数形式存储状态标志
How to store state flags in floating point numbers for GLSL / WebGL
我最近了解到 storing boolean flags in integers using bitmasks。我想知道如何执行此操作来存储和检索布尔标志 to/from JavaScript,以及如何存储和检索标志 to/from GLSL。我认为这需要 浮点位掩码 ,而不是整数位掩码。这样我就可以在纹理中编码 JavaScript 中的一些状态标志,然后在 GLSL 中解压缩它们。同样,如果我将数据写入 GLSL 中的像素作为状态标志,我可以在 JavaScript.
中读出它们
在 WebGL2 的 GLSL ES 3.0 中,和大多数语言一样有位操作
uint flags = ??;
...
bool flag1 = (flags & 0x1) > 0;
bool flag2 = (flags & 0x2) > 0;
bool flag3 = (flags & 0x4) > 0;
在 WebGL1 中,您可以 mod 输出达到某个限制的值
float flags = ??;
bool flag1 = mod(flags, 2.0) > 0.;
bool flag2 = mod(floor(flags / 2.0), 2.0) > 0.;
bool flag3 = mod(floor(flags / 4.0), 2.0) > 0.;
只要 flags
是一个 highp
值并且是正整数值,这应该适用于前 23 位。
当然要看flags
来自哪里。例如,如果您将标志作为 UNSIGNED_BYTE
存储在纹理或属性中,那么您会将它们作为每个通道(红色、绿色、蓝色、alpha)的 8 位值提取出来。 8 位小于 23 位限制,例如
vec4 flags = texture2D(textureWithFlags, someUV) * 255.0;
现在flags.r
或flags[0]
是前8位,flags.g
是接下来的8位等
或
attribute vec4 flags;
你用 UNSIGNED_BYTE 值和 normalize = false 设置属性然后就像上面每个标志通道一样是原始数据的 8 位
不鼓励在 GLSL 中使用分支。一般来说,如果你想做 2 个或更多不同的事情,而不是添加标志,为每个变体编写或生成着色器。这是大多数 3D 引擎所做的,包括 Unity、Unreal、Three.js 等...
另一方面,有时在适当的时候,您可以使用 step
和 mix
,而不是分支。例如
vec4 color1 = ??
vec4 color2 = ??
float useColor2 = mod(flags, 2.0); // will be 0.0 or 1.0
vec4 color = mix(color1, color2, useColor2);
上面的代码没有分支。
同理这个
vec4 color;
if (x < 100.0)
color = color1;
} else {
color = color2;
}
可以翻译成
vec4 color = mix(color1, color2, step(100.0, x));
我最近了解到 storing boolean flags in integers using bitmasks。我想知道如何执行此操作来存储和检索布尔标志 to/from JavaScript,以及如何存储和检索标志 to/from GLSL。我认为这需要 浮点位掩码 ,而不是整数位掩码。这样我就可以在纹理中编码 JavaScript 中的一些状态标志,然后在 GLSL 中解压缩它们。同样,如果我将数据写入 GLSL 中的像素作为状态标志,我可以在 JavaScript.
中读出它们在 WebGL2 的 GLSL ES 3.0 中,和大多数语言一样有位操作
uint flags = ??;
...
bool flag1 = (flags & 0x1) > 0;
bool flag2 = (flags & 0x2) > 0;
bool flag3 = (flags & 0x4) > 0;
在 WebGL1 中,您可以 mod 输出达到某个限制的值
float flags = ??;
bool flag1 = mod(flags, 2.0) > 0.;
bool flag2 = mod(floor(flags / 2.0), 2.0) > 0.;
bool flag3 = mod(floor(flags / 4.0), 2.0) > 0.;
只要 flags
是一个 highp
值并且是正整数值,这应该适用于前 23 位。
当然要看flags
来自哪里。例如,如果您将标志作为 UNSIGNED_BYTE
存储在纹理或属性中,那么您会将它们作为每个通道(红色、绿色、蓝色、alpha)的 8 位值提取出来。 8 位小于 23 位限制,例如
vec4 flags = texture2D(textureWithFlags, someUV) * 255.0;
现在flags.r
或flags[0]
是前8位,flags.g
是接下来的8位等
或
attribute vec4 flags;
你用 UNSIGNED_BYTE 值和 normalize = false 设置属性然后就像上面每个标志通道一样是原始数据的 8 位
不鼓励在 GLSL 中使用分支。一般来说,如果你想做 2 个或更多不同的事情,而不是添加标志,为每个变体编写或生成着色器。这是大多数 3D 引擎所做的,包括 Unity、Unreal、Three.js 等...
另一方面,有时在适当的时候,您可以使用 step
和 mix
,而不是分支。例如
vec4 color1 = ??
vec4 color2 = ??
float useColor2 = mod(flags, 2.0); // will be 0.0 or 1.0
vec4 color = mix(color1, color2, useColor2);
上面的代码没有分支。
同理这个
vec4 color;
if (x < 100.0)
color = color1;
} else {
color = color2;
}
可以翻译成
vec4 color = mix(color1, color2, step(100.0, x));