我有来自 Threejs 示例的代码示例,其中浮点值转换为
vec4
。我在其他几个论坛上看到过这种逻辑,但没有解释。
我也看过这个链接将 float 打包到 vec4 - 这段代码是如何工作的?
它表示
vec4
最终将存储在32位RGBA8缓冲区中。此外,由于
vec4
有 4 个分量,每个分量 4 个字节,使其成为 16 个字节,从而使其成为 16 * 8 位。这如何适合 32 位 RGBA8?
vec4 pack_depth( const in float depth ) {
const vec4 bit_shift = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
const vec4 bit_mask = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
vec4 res = fract(depth * bit_shift);
res -= res.xxyz * bit_mask;
return res;
}
void main() {
vec4 pixel = texture2D(texture, vUV);
if (pixel.a < 0.5) discard;
gl_FragData[0] = pack_depth(gl_FragCoord.z);
}
连同 @WaclawJasper 的答案,此代码将“深度”(浮点值(32 位))打包为 4 个 8 位值。
gl_FragData[ 0 ]
表示纹理中的单个像素,在本例中是每个通道 8 位的纹理(总共 32 位)。 如果我写
gl_FragData[0] = vec4(0.25, 0.5, 0.75, 1.0);
正在写入的纹理(假设它是 8 位纹理)实际上会获取值
r = Math.floor(0.25 * 255) = 63;
g = Math.floor(0.5 * 255) = 127;
b = Math.floor(0.75 * 255) = 191;
a = Math.floor(1.0 * 255) = 255;
规范中的实际公式是有效的
unsignedIntValue = floor(clamp(floatValue, 0., 1.) * (pow(2., numberOfBits) - 1.));
因此,即使
pack_depth
返回一个浮点型 vec4,并且 gl_FragData
被定义为 vec4
,当写入到当前正在写入的任何 WebGL(画布、渲染缓冲区、纹理)时,它最终也会被转换。
如果写入浮点纹理
pack_depth
将是不必要的。由于 pack_depth
正在执行的操作,我们可以推断它正在写入 8 位 RGBA 纹理。
为什么有这个需求?因为在 WebGL 中,对浮点纹理的支持是可选的。因此,如果用户的机器不支持浮点纹理,并且您需要类似浮点的数据(如阴影贴图的深度缓冲区),那么将数据打包到 8 位纹理中是一种解决方案。
256是8位数据可以表示的不同值的数量。让我们暂时停止使用二进制,转而使用熟悉的十进制。如果我们有 2 个通道,每个通道只能存储 1 位数字 (0-9),那么我们如何打包 45 这样的数字?显然,我们将 5 打包为一位数字,将 40/10 或 4 打包为另一位数字。然后在我们的 unpack 函数中,我们只需执行相反的操作:4*10 + 5 = 45。256 类似于十进制示例中的 10。
OpenGL 没有,但您自己的应用程序代码可以理解它。
我不确定我是否正确理解这部分,但您显示的代码是将一个浮点数打包成8*4位RGBA。