opengl-es - 如何在 webgl 渲染 channel 之间传递高精度 uv 坐标?

标签 opengl-es three.js glsl webgl shader

假设我正在解决 WebGL 中的一个问题,该问题需要以像素完美精度从大型纹理(例如 2048x2048)中检索值。

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

但现在假设我想把事情搞混。我想创建一个渲染 channel ,它返回一个纹理,为我开始使用的大纹理中的每个像素存储一对 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 的纹理。

您会注意到我在这些示例中指定了高精度 float 和 sampler2Ds 的使用。这是唯一容易想到的解决方案,但这仍然没有解决我的问题。我知道我总是可以使用需要较低精度的相对坐标系来处理像素,但为了简单起见,我希望我仍然能够使用 uv 进行处理。

最佳答案

想法

  • 让您的 UV 纹理成为浮点纹理?您的纹理目前可能只有每个 channel 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.
    
  • 通过将数据组合到多个 channel 来提高您的分辨率

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

    之类的内容重新组合

    这应该适用于任何地方,并为您提供足够的分辨率来处理 65536x65536 纹理。

关于opengl-es - 如何在 webgl 渲染 channel 之间传递高精度 uv 坐标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20847546/

相关文章:

three.js - 如何将gltf素材变成卡通素材?

javascript - GLTF 加载程序试用版返回类型错误 : Failed to resolve module specifier "three". 相对引用必须以 "/"、 "./"或 "../"开头

android - OpenGL ES glCheckFramebufferStatus 返回 0

android - GLES10.glGetIntegerv 仅在 Lollipop 中返回 0

3d - 三js : optimize vertices and faces

c++ - OpenGL 中的 Oren-Nayar 光照(如何在片段着色器中计算 View 方向)

c++ - OpenGL:如何根据相机距离对点进行排序?

ios - 前两个片段着色器输出不同

opengl-es - 多边形的分割和三角剖分

java - 按下后退按钮后 libGDX And​​roid 白屏