假设我正在研究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来解决。
答案 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纹理。