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

168*_*807 1 shader opengl-es glsl webgl three.js

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

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

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

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

void main() {

    gl_FragColor = vec4(vUv, 0, 1);

}
Run Code Online (Sandbox Code Playgroud)

使用第一个渲染过程返回的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);

}
Run Code Online (Sandbox Code Playgroud)

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

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

gma*_*man 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.
    
    Run Code Online (Sandbox Code Playgroud)
  • 通过将数据组合到多个通道中来提高分辨率

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

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