代码之家  ›  专栏  ›  技术社区  ›  deblocker

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

  •  3
  • deblocker  · 技术社区  · 7 年前

    为了理解与通用移动目标的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);
    

    顶点着色器:

    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;,
    }
    

    片段着色器:

    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;',
    }
    

    这就是我现在得到的:左:iPad PRO/Android/桌面Chrome-模拟着色器精度,中:桌面FF/Chrome(无标志),右:编码和解码(明显为256色调灰度)

    depth mediump

    我尝试了许多不同的包装方法,但似乎都不管用。 关于我做错了什么有什么建议吗?

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

    编辑:三个相同的问题。js公司: https://github.com/mrdoob/three.js/issues/9092

    有趣的是,如果我使用旧的mod方法来计算包装深度,我 获得更高的精度(至少多几个位)

    使用以下哪种方法存储和检索深度信息是正确的 mediump 精确

    1 回复  |  直到 4 年前
        1
  •  7
  •   Rabbid76    7 年前

    具有precision限定符的变量的浮点精度 mediump 保证为10位。
    看见 OpenGL ES Shading Language 1.00 Specification - 4.5.2 Precision Qualifiers, page 33

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

    enter image description here

    因此,只有编码深度的两个最高字节才有意义。该算法在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);
    }
    

    该算法可以扩展到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);
    }
    

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

    (function onLoad() {
    
    // shader program object
    var ShaderProgram = {};
    ShaderProgram.Create = function( shaderList, uniformNames ) {
        var shaderObjs = [];
        for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
            var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
            if ( shderObj == 0 )
                return 0;
            shaderObjs.push( shderObj );
        }
        var progObj = this.LinkProgram( shaderObjs )
        if ( progObj != 0 ) {
            progObj.unifomLocation = {};
            for ( var i_n = 0; i_n < uniformNames.length; ++ i_n ) {
                var name = uniformNames[i_n];
                progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
            }
        }
        return progObj;
    }
    ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); } 
    ShaderProgram.CompileShader = function( source, shaderStage ) {
        var shaderScript = document.getElementById(source);
        if (shaderScript) {
          source = "";
          var node = shaderScript.firstChild;
          while (node) {
            if (node.nodeType == 3) source += node.textContent;
            node = node.nextSibling;
          }
        }
        var shaderObj = gl.createShader( shaderStage );
        gl.shaderSource( shaderObj, source );
        gl.compileShader( shaderObj );
        var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
        if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
        return status ? shaderObj : 0;
    } 
    ShaderProgram.LinkProgram = function( shaderObjs ) {
        var prog = gl.createProgram();
        for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
            gl.attachShader( prog, shaderObjs[i_sh] );
        gl.linkProgram( prog );
        status = gl.getProgramParameter( prog, gl.LINK_STATUS );
        if ( !status ) alert("Could not initialise shaders");
        gl.useProgram( null );
        return status ? prog : 0;
    }
            
    function drawScene(){
    
        var canvas = document.getElementById( "ogl-canvas" );
        var vp = [canvas.width, canvas.height];
        
        gl.viewport( 0, 0, canvas.width, canvas.height );
        gl.enable( gl.DEPTH_TEST );
        gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
        gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
        ShaderProgram.Use( progDraw );
        gl.enableVertexAttribArray( progDraw.inPos );
        gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
        gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 ); 
        gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 );
        gl.disableVertexAttribArray( progDraw.pos );
    }  
    
    var gl;
    var prog;
    var bufObj = {};
    var canvas
    function sceneStart() {
    
      container = document.getElementById('container');
    
        canvas = document.getElementById( "ogl-canvas");
        resize();
    
        gl = canvas.getContext( "experimental-webgl" );
        if ( !gl )
          return;
    
        progDraw = ShaderProgram.Create( 
          [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
            { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
          ], [] );
        progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
        if ( prog == 0 )
            return;
    
        bufObj.pos = gl.createBuffer();
        gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
        gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( [ -1, -1, 1, -1, -1, 1, 1, 1 ] ), gl.STATIC_DRAW );
        
        window.onresize = resize;
        setInterval(drawScene, 50);
    }
    
    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight; 
    }
    
    sceneStart();
    })();
    <canvas id="ogl-canvas"></canvas>
    
    <script id="draw-shader-vs" type="x-shader/x-vertex">
    precision mediump float;
    
    attribute vec2 inPos;
    
    varying vec2 vertPos;
    
    void main()
    {
        vertPos = inPos;
        gl_Position = vec4( inPos.xy, 0.0, 1.0 );
    }
    </script>
    
    <script id="draw-shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    
    varying vec2 vertPos;
    
    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);
    }
    
    vec4 PackDepth32_orig(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 UnpackDepth32_orig( 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()
    {
        float depthTest = clamp(vertPos.x + 0.5, 0.0, 1.0);
        vec2  color1    = clamp(PackDepth16( depthTest ), 0.0, 1.0);
        float depth1    = UnpackDepth16( color1 );
        vec4  color2    = clamp(PackDepth32_orig( depthTest ), 0.0, 1.0);
        float depth2    = UnpackDepth32_orig( color2 );
    
        gl_FragColor = vec4( mix( vec3(depth1), vec3(depth2), step(vertPos.y, 0.0) ), 1.0 );
    }
    </script>