如何在webgl中的渲染过程之间传达高精度的uv坐标?

时间:2013-12-30 21:04:39

标签: opengl-es three.js glsl webgl shader

假设我正在研究WebGL中的一个问题,该问题需要从大纹理(例如,2048x2048)中检索值,并且像素完美精确。

到目前为止,一切对我来说都很有用。我可以将大纹理传递给片段着色器,片段着色器会将其转换为渲染目标,我甚至可以将渲染目标作为另一个渲染过程的输入,始终检索我需要的精确像素。

但现在让我们说我想混淆一下。我想创建一个渲染过程,它返回一个纹理,为我开始使用的大纹理中的每个像素存储一对uv坐标。作为最简单的用例,让我们说:

precision highp float;
precision highp sampler2D;
varying vec2 vUv;

void main() {

    gl_FragColor = vec4(vUv, 0, 1);

}

使用第一个渲染过程返回的uv坐标,我想从我开始使用的大纹理中访问一个像素:

precision highp float;
precision highp sampler2D;
uniform sampler2D firstPass;
uniform sampler2D largeTexture;
varying vec2 vUv;

void main() {

    vec2 uv = texture2D(firstPass, vUv);
    gl_FragColor = texture2D(largeTexture, uv);

}

然而,这并不能以足够的精度工作。我经常从相邻像素获得颜色,而不是我想要解决的像素。从一些修补我发现这只适用于尺寸高达~512x512的纹理。

您将注意到我已在这些示例中指定使用高精度浮点数和采样器2D。这是关于唯一容易想到的解决方案,但这仍然没有解决我的问题。我知道我总是可以依靠寻找具有较低精度的相对坐标系的像素,但我希望为了简单起见,我仍然可以用uv来解决。

1 个答案:

答案 0 :(得分:7)

  • 让你的UV纹理成为浮点纹理?您的纹理目前可能只有每个通道8位,这意味着它只能处理256个唯一位置。浮点纹理不会出现这个问题

    不幸的是,到处都不支持渲染到浮点纹理,并且浏览器没有统一实现所需的扩展来检查它是否可行。如果你在现代桌面上,它可能会工作。

    要确定它是否有效,请尝试获取浮点纹理扩展,如果存在则创建浮点纹理并将其附加到帧缓冲区,然后检查帧缓冲区是否完整。如果是,你可以渲染它。

    var floatTextures = gl.getExtension("OES_texture_float");
    if (!floatTextures) {
       alert("floating point textures are not supported on your system");
       return;
    }
    
    // If you need linear filtering then...
    var floatLinearTextures = gl.getExtension("OES_texture_float_linear");
    if (!floatLinearTextures) {
       alert("linear filtering of floating point textures is not supported on your system");
    }
    
    // check if we can render to floating point textures.
    var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, null);
    
    // some drivers have a bug that requires you turn off filtering before
    // rendering to a texture. 
    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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 
    
    // make a framebuffer
    var fb = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
    
    // attach the texture
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 
                            gl.TEXTURE_2D, tex, 0);
    
    // check if we can render
    var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    if (status != gl.FRAMEBUFFER_COMPLETE) {
        alert("can't render to floating point textures");
        return;
    }
    
    // You should be good to go.
    
  • 通过将数据合并到多个渠道来提高分辨率

    当写入UV纹理时,将UV从0-1范围转换为0到65535范围,然后将modf(uv, 256) / 255写入一个通道,将floor(uv / 256) / 256写入另一个通道。阅读时,将频道重新组合为uv = (lowChannels * 256.0 + highChannels * 65535.0) / 65535.0

    这应该适用于所有地方并为您提供足够的分辨率来解决65536x65536纹理。