1. 引言
该例子讲解了一个由普通纹理格式GL_RGBA来模拟HDR效果。
2.关键技术
不使用浮点纹理,所以必须用一种方法让低精度数据代表高亮度颜色。那就用第四通道A来做为RGB的乘法因子。好象与RGBS编码方法类似。具体编码是,如果R、G或B大于1.0,则RGB除以它们的最大值,设最大值为MAX,则令
A=MAX/CONST
CONST是一常量,我们可以取2的幂,我这里取64,当然不能大于255。除以64.0是为了将A影射到[0,1]区间。所以我们编码和解码规则
If(max(color.r,color.g,color.b)>1.0)
{
color.rgb /= max(color.r,color.g,color.b);
color.a = max/64.0;
}
3.场景建立
3.1渲染天空盒
这是我们的天空盒。分两次渲染进显寸中,第一次将RGB颜色传递到CUBEMAP,第二次把A传递进去。我使用的天空盒不是HDR格式,但可以使用HDR格式,只要稍做改变即可使用。
程序代码:
uniform samplerCube cubeMap;
void main()
{
vec4 color = textureCube(cubeMap, gl_TexCoord[0].stp);
color.a = (color.a+1.0)/64.0;
gl_FragColor = color;
}
如果天空盒是HDR格式,代码如下
由RGBE格式为RGBA
fExp = Encoded.E - 128; color.r = color.r/255 * pow(2,fExp); color.g = color.g/255 * pow(2,fExp); color.b = color.b/255 * pow(2,fExp);
然后再用上面的代码转换为自定义格式。
3.2渲染场景的模型,主要是光照计算,并转换为自定义格式
uniform sampler2D tex;
uniform sampler2D average;
uniform float factor;
void main()
{
vec4 color = texture2D(tex, gl_TexCoord[0].st);
vec4 ave = texture2D(average, vec2(0.5,0.5));
float avelum = dot(ave.rgb,vec3(0.3,0.59,0.11))*factor;
vec3 final = vec3(0.0,0.0,0.0); color.rgb *=color.a*64.0 ;
float lum = dot(color.rgb, vec3(0.3,0.59,0.11));
color.rgb /= (vec3(1.0,1.0,1.0)+color.rgb);
if(lum>avelum) final = color.rgb;
gl_FragColor = vec4(final, 1.0);
}
4.渲染到离屏缓冲器FBO
我们这里一共用了6个FBO,10个纹理单元
GLuint FrameBufferObject::AddTexture(
GLenum attachment,
GLuint w,
GLuint h,
GLuint internalformat,
GLenum type,
GLuint format,
GLuint level,
GLuint target,
GLuint filter
)
{
GLuint texID;
glGenTextures(1,&texID);
glBindTexture(target, texID);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(target, 0,internalformat, w, h, 0, format, type, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, target, texID, 0);
return texID; }
4.1 提取高亮度部分并下采样到64*64,并模糊
步骤1将场景高亮部分提出,渲染到64*64纹理;
uniform sampler2D tex;
uniform sampler2D average;
uniform float factor;
void main()
{
vec4 color = texture2D(tex, gl_TexCoord[0].st);
vec4 ave = texture2D(average, vec2(0.5,0.5));
float avelum = dot(ave.rgb,vec3(0.3,0.59,0.11))*factor;
vec3 final = vec3(0.0,0.0,0.0); color.rgb *=color.a*64.0 ;
float lum = dot(color.rgb, vec3(0.3,0.59,0.11));
color.rgb /= (vec3(1.0,1.0,1.0)+color.rgb);
if(lum>avelum) final = color.rgb; gl_FragColor = vec4(final, 1.0);
}
步骤2 横向模糊渲染到另一纹理;
步骤3 纵向模糊渲染到另一纹理;
uniform sampler2D tex;
#define dx 0.015625
void main()
{
vec4 color = texture2D(tex, gl_TexCoord[0].st) * 0.375;
color += texture2D(tex, vec2(gl_TexCoord[0].s+dx, gl_TexCoord[0].t)) * 0.3125;
color += texture2D(tex, vec2(gl_TexCoord[0].s-dx, gl_TexCoord[0].t)) * 0.3125;
gl_FragColor =color;
}
4.3 与原场景混合,色调映射。
4.4 暴光控制
4.4.1 求亮度平均值
步骤1 将场景下采样到64*64纹理;
步骤2 再下采样到16*16纹理;
步骤3 再下采样到4*4纹理;
步骤4 再下采样到1*1纹理;
uniform sampler2D tex; uniform float dx;
void main()
{
vec4 color = texture2D(tex, gl_TexCoord[0].st+vec2(dx*1.5, dx*1.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2( dx*0.5, dx*1.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*0.5, dx*1.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*1.5, dx*1.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2( dx*1.5, dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2( dx*0.5, dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*0.5, dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*1.5, dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2( dx*1.5,-dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2( dx*0.5,-dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*0.5,-dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*1.5,-dx*0.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2( dx*1.5,-dx*1.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2( dx*0.5,-dx*1.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*0.5,-dx*1.5));
color += texture2D(tex,gl_TexCoord[0].st+vec2(-dx*1.5,-dx*1.5));
color /= 16.0;
gl_FragColor =color;
}
步骤5 采样该1*1纹理,其亮度便是平均亮度
4.4.2 利用乒乓技术求暴光度
利用两个渲染对象,分别绑定1*1纹理。渲染过程中两个纹理没次都轮流渲染到其中的一个。
公式如下
float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * ( 1 - pow( 0.98f , 30 * g_fElapsedTime ) );
另外为方便计算,我直接使用如下GLSL代码
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float time; void main()
{
vec4 average = texture2D(tex1, vec2(0.5,0.5));
float oldExp = texture2D(tex2, vec2(0.5,0.5)).r ;
float newExp = dot(average.rgb, vec3(0.3,0.59,0.11));
gl_FragColor.x = mix(oldExp, newExp, 0.001); }
如果麻烦,该技术去掉,直接在最后的图象生成阶段访问包含亮度平均值的纹理来计算暴光度。