假设我正在解决 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/