使用中精度在 RGBA 纹理中打包深度信息

deb*_*ker 2 javascript shader glsl depth-buffer webgl

试图了解与通用移动目标的 WebGL 开发相关的许多问题,现在我需要将深度信息存储在纹理附件中以供以后检索和后处理。

JavaScript:

var depthRB = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRB);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRB);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);

var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
Run Code Online (Sandbox Code Playgroud)

顶点着色器:

precision mediump float;
uniform mat4 u_transformMatrix;
attribute vec3 a_position;
varying float v_depth;
void main() {
    vec4 tcoords = u_transformMatrix * vec4(a_position, 1.0);
    v_depth = 0.5 * (tcoords.z + 1.0);
    gl_Position = tcoords;,
}
Run Code Online (Sandbox Code Playgroud)

片段着色器:

precision mediump float; 
varying float v_depth;
vec4 PackDepth(in float frag_depth) {
    vec4 bitSh = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
    vec4 bitMsk = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
    vec4 enc = fract(frag_depth * bitSh);
    enc -= enc.xxyz * bitMsk;
    return enc;
}
float UnpackDepth( const in vec4 enc ) {
    const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );
    float decoded = dot( enc, bit_shift );
    return decoded;
}
void main() {
    vec4 encoded_depth;
    float decoded_depth;
    encoded_depth = PackDepth(v_depth);
    decoded_depth = UnpackDepth(encoded_depth);
    //gl_FragColor = vec4(vec3(decoded_depth), 1.0);
    gl_FragColor = encoded_depth;',
}
Run Code Online (Sandbox Code Playgroud)

这就是我现在得到的:左:iPad PRO/Android/桌面 Chrome --emulate-shader-precision,中间:桌面 FF/Chrome(无标志),右:编码和解码(显然是 256 色调灰度)

深度中等p

我尝试了许多不同的打包/解包方法,但似乎都没有奏效。关于我做错了什么的任何建议?

此外,我还注意到使用 RGBA 纹理存储深度信息的最常见 WebGL 库的许多示例都被破坏了 - 我相信出于同样的原因,打包/解包函数中的某个地方存在问题。

编辑: Three.js 中的同样问题:https : //github.com/mrdoob/three.js/issues/9092

有趣的是,如果我使用旧的 mod 方法来打包深度,我会得到更高的精度(至少多一些)

使用mediump精度存储和检索深度信息的正确方法是什么?

Rab*_*d76 5

带有 precsion 限定符的变量的浮点精度mediump保证为 10 位。
请参阅OpenGL ES 着色语言 1.00 规范 - 4.5.2 精度限定符,第 33 页

精度限定符所需的最小范围和精度是:

在此处输入图片说明

为此,只有编码深度的最高两个字节才有意义。该算法将最高字节存储在 alpha 通道中,将第二高字节存储在蓝色通道中。这导致编码深度的 RGB 视图可能看起来很随意。

此外,该算法在深度为 1.0 时有溢出。这导致1的深度被编码为完全黑色,但黑色在解码时变为0.0。

将 [0.0, 1.0] 范围内的深度值编码为从 b00000000 到 b11111111 的 16 位的算法可能如下所示(RG 颜色通道):

vec2 PackDepth16( in float depth )
{
    float depthVal = depth * (256.0*256.0 - 1.0) / (256.0*256.0);
    vec3 encode = fract( depthVal * vec3(1.0, 256.0, 256.0*256.0) );
    return encode.xy - encode.yz / 256.0 + 1.0/512.0;
}

float UnpackDepth16( in vec2 pack )
{
    float depth = dot( pack, 1.0 / vec2(1.0, 256.0) );
    return depth * (256.0*256.0) / (256.0*256.0 - 1.0);
}
Run Code Online (Sandbox Code Playgroud)

这个算法可以扩展到 24 位或 32 位:

vec3 PackDepth24( in float depth )
{
    float depthVal = depth * (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
    vec4 encode = fract( depthVal * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return encode.xyz - encode.yzw / 256.0 + 1.0/512.0;
}

float UnpackDepth24( in vec3 pack )
{
  float depth = dot( pack, 1.0 / vec3(1.0, 256.0, 256.0*256.0) );
  return depth * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}

vec4 PackDepth32( in float depth )
{
    depth *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
    vec4 encode = fract( depth * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}

float UnpackDepth32( in vec4 pack )
{
    float depth = dot( pack, 1.0 / vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return depth * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}
Run Code Online (Sandbox Code Playgroud)

请参阅代码片段,其中比较了答案中的算法(顶部)和问题中的算法(底部):

vec2 PackDepth16( in float depth )
{
    float depthVal = depth * (256.0*256.0 - 1.0) / (256.0*256.0);
    vec3 encode = fract( depthVal * vec3(1.0, 256.0, 256.0*256.0) );
    return encode.xy - encode.yz / 256.0 + 1.0/512.0;
}

float UnpackDepth16( in vec2 pack )
{
    float depth = dot( pack, 1.0 / vec2(1.0, 256.0) );
    return depth * (256.0*256.0) / (256.0*256.0 - 1.0);
}
Run Code Online (Sandbox Code Playgroud)
vec3 PackDepth24( in float depth )
{
    float depthVal = depth * (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
    vec4 encode = fract( depthVal * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return encode.xyz - encode.yzw / 256.0 + 1.0/512.0;
}

float UnpackDepth24( in vec3 pack )
{
  float depth = dot( pack, 1.0 / vec3(1.0, 256.0, 256.0*256.0) );
  return depth * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}

vec4 PackDepth32( in float depth )
{
    depth *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
    vec4 encode = fract( depth * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}

float UnpackDepth32( in vec4 pack )
{
    float depth = dot( pack, 1.0 / vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return depth * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}
Run Code Online (Sandbox Code Playgroud)