假设我正在渲染2个样本,这些样本将合并为一个图像。第一个样本包含超出可显示像素范围的值(在这种情况下,大于1)。但是当被第二个样本减去时,它确实落在了范围内。
在合并之前,我将样本存储在帧缓冲纹理中。
我希望能够存储大于1的值,但这些值被钳制为1. GLSL片段着色器是否可以输出这样的值?纹理可以存储它们吗?如果没有,我还能怎样存储它们?
根据this page,有可能:
渲染到屏幕要求输出具有可显示格式,而在多通道管道中并非总是这样。有时,传递产生的纹理需要具有浮点格式,不能直接转换为颜色
但根据规范,纹理浮点数被限制在[0,1]范围内。
答案 0 :(得分:2)
片段着色器可以输出[0.0,1.0]范围之外的值,但前提是写入值的缓冲区格式支持该范围之外的值。启用此功能所需的是渲染目标(渲染缓冲区或附加到FBO的纹理),用于存储浮点值。
OpenGL ES 2.0及更低版本不需要支持浮点格式纹理。 OpenGL ES 3.0及更高版本。例如,在ES 3.0中,您可以将GL_RGBA16F
用于具有16位浮点(又称半浮点)组件的RGBA纹理,并将GL_RGBA32F
用于32位浮点组件。但是,ES 3.0和3.1仍然不需要支持将这些格式用作渲染目标,这就是这个用例所需要的。
ES 2.0实现可以通过支持OES_texture_half_float扩展来支持OES_texture_float和浮动纹理,从而提供半浮点纹理。为了支持渲染半浮动纹理,它们还需要EXT_color_buffer_half_float。 EXT_color_buffer_float定义渲染浮动纹理,但指定基于ES 3.0。
总结:
如果您想使用这些功能,则必须在设备上测试这些扩展程序的存在。
答案 1 :(得分:2)
最简单的方法是使用浮点纹理。
var gl = someCanvasElement.getContext("experimental-webgl");
var ext = gl.getExtension("OES_texture_float");
if (!ext) {
alert("no OES_texture_float");
return;
}
现在您可以使用浮点纹理创建和渲染。接下来要做的是看看你是否可以渲染到浮点纹理。
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);
gl.texParameteri(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_MAG_FILTER, gl.NEAREST);
var fb = gl.createFramebuffer();
gl.bindFrameBuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status != gl.FRAMEBUFFER_COMPLETE) {
alert("can not render to floating point textures");
return;
}
使用OES_texture_float
如果设备不支持渲染到浮点纹理,那么您必须以其他方式对结果进行编码,例如gil建议
注意:在WebGL2中,浮点纹理始终可用。另一方面,如果要过滤浮点纹理,仍需检查并启用OES_texture_float_linear
。同样在WebGL2中,您需要启用EXT_color_float_buffer
渲染到浮点纹理(并且您仍然需要调用gl.checkFramebufferStatus
,因为它取决于驱动器支持哪些附件组合。
答案 2 :(得分:0)
这里的关键思想是使用[0,1]范围内的2或4个固定点8位通道(颜色通道)在一些不受限制的范围内编码浮点数。此方法是通用的,适用于WebGL或任何其他GL系统。
假设您从浮点值开始:
float value;
假设您的机器支持mediump(16位浮点数),您可以使用编码值 2个8位通道:
float myNormalize(float val)
{
float min = -1.0;
float max = 1.0;
float norm = (val - min) / (max - min);
return norm;
}
vec2 encode_float_as_2bytes(float a)
{
a = myNormalize(a);
vec2 enc = vec2(1.0, 256.0);
enc *= a;
enc = fract(enc);
enc.x -= enc.y * (1.0 / 256.0);
return enc;
}
这里encode_float_as_2bytes(float a)接受要编码的值。首先使用一些边界值将值标准化为[0,1](在我的示例中,我的浮点数可以取[-1,1]中的值。标准化后,使用vec2对值进行编码。 现在,您可以将编码值写入颜色缓冲区:
float a = compute_something(...);
gl_FragColor.xy = encode_float_as_2bytes(a);
现在,当读取编码值(通过其他着色器或使用glReadPixels())时,您可以解码编码的float并获取值:
float denormalize(float val)
{
float min = -1.0;
float max = 1.0;
float den = val * (max - min) + min;
return den;
}
float decode_2_bytes(vec2 a)
{
float ret;
ret = a.x * 1.0 + a.y * 1.0/256.0;
ret = denormalize(ret);
return ret;
}
注意非规范化值必须与规范化值匹配(在此示例中为-1,1。
您可以在此处找到有关浮动编码的更多信息:http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/