高质量bloom效果
高质量bloom效果
1、普通泛光(bloom)
参考LearnOpenGL上解释,泛光效果用于表达光源非常亮,以至于发出了光晕。旨在重现被称为Airy disc的光学效应,在现实世界里,当光线穿过人眼时会发生衍射,导致非常明亮的光源渗透进较暗的物体中。泛光效果通常使用屏幕后处理实现:首先渲染场景和光源到HDR颜色缓冲上;然后提取HDR中大于亮度阈值的像素;使用高斯模糊或其他模糊技术处理这些像素;最后将模糊后的像素和原本场景中的叠加到一起输出。

2、高质量bloom
普通泛光效果该亮的地方不够亮,不该亮的地方亮了,而且泛光的范围不够宽,像是高亮外围套了一圈。高质量泛光效果应该:光源溢出范围足够广;光源中心足够亮。Jorge Jimenez在2014年介绍了使命召唤采用的高质量bloom方案。
2.1 使用MRT渲染光源
多渲染(Multiple Render Targets)目标技术可以使用一个shader渲染颜色到不同的Framebuffer颜色缓冲上。具体做法为首先声明两张纹理,一张为普通RGBA、GL_UNSIGNED_BYTE 的LDR纹理,另一张为RGBA、GL_FLOAT的HDR纹理,用于接收光源颜色输出。然后绑定到Framebuffer不同的颜色纹理附件上。
// Set up floating point framebuffer to render scene to
GLuint hdrFBO;
glGenFramebuffers(1, &hdrFBO);
glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);
GLuint colorBuffers[2];
glGenTextures(2, colorBuffers);
for (GLuint i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, colorBuffers[i]);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGB16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_FLOAT, NULL
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// attach texture to framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, colorBuffers[i], 0);
}
最后在shader中使用标识符layout区分不同纹理的输出,添加亮度threshold,提取出亮色。
#version 330 core
layout (location = 0) out vec4 FragColor;
layout (location = 1) out vec4 BrightColor;
[...]
void main()
{
[...] // first do normal lighting calculations and output results
FragColor = vec4(lighting, 1.0f);
// Check whether fragment output is higher than threshold, if so output as brightness color
float brightness = dot(FragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
if(brightness > 1.0)
BrightColor = vec4(FragColor.rgb, 1.0);
}
2.2 下采样处理
想要得到高质量泛光中光源溢出范围足够广的效果,势必要进行大范围的模糊,这会极具增大采样的性能消耗。可以通过纹理双线性滤波实现快速大范围的模糊,不断新建颜色纹理,纹理大小为上一级的一半,调用glBlitFramebuffer命令将原始纹理中像素拷贝到目标纹理中,命令定义如下:
void glBlitFramebuffer(GLint srcX0, Glint srcY0,GLint srcX1, Glint srcY1,GLint dstX0, Glint dstY0,GLint dstX1, Glint dstY1,GLbitfield mask, GLenum filter);
其中mask参数可以是GL_DEPTH_BUFFER_BIT、GL_STENCIL_BUFFER_BIT、GL_COLOR_BUFFER_BIT中的一个。filter参数可以是GL_LINEAR或者是GL_NEAREST,如果你是拷贝深度数据或者蒙版数据或者整型的颜色数据的话,这个 参数必须是GL_NEAREST。当原始纹理尺寸和目标纹理不一致时,就会执行双线性插值。
for (int i = 1; i < kSimpleCount; i++)
{
m_downSimpleFbos[i]->copyFramebuffer(m_downSimpleFbos[i-1], GL_LINEAR);
}
最后得到的不同次数的下采样纹理如下:

2.3 上采样处理
下采样处理中随着缩放采样不断进行,得到了更加模糊但是范围更广的颜色纹理输出。但要保证光源中心足够亮,需要反着做上采样处理,另外为了保证不同模糊半径的纹理过渡自然,需要在上采样过程中使用高斯滤波shader进行平均。

类似下采样过程,我们声明一组上采样临时Framebuffer,他们的大小为上一级的一半。
reBindBloomQuadNode(m_downSimpleFbos[kSimpleCount - 2], m_downSimpleFbos[kSimpleCount - 1]);
renderSinglePass(m_bloomPostPass, m_upSimpleFbos[kSimpleCount - 2], {0, 0, 0, 0.0});
for (int i = kSimpleCount-3; i >=0; i--)
{
reBindBloomQuadNode(m_downSimpleFbos[i], m_upSimpleFbos[i+1]);
renderSinglePass(m_bloomPostPass, m_upSimpleFbos[i], { 0, 0, 0, 0.0 });
}
渲染过程中,输入上一级上采样纹理和同级的下采样纹理,输出此级的上采样纹理。使用的高斯模糊shader如下。
#version 330 core
out vec4 FragColor;
in vec2 vTexCoords;
uniform sampler2D mainMap;
uniform sampler2D preMap;
uniform float weight[25] = float[] (0.0038, 0.015, 0.0238, 0.015, 0.0038,
0.015,0.0599,0.0949,0.0599,0.015,
0.0238,0.0949,0.1503,0.0949,0.0238,
0.015,0.0599,0.0949,0.0599,0.015,
0.0038, 0.015, 0.0238, 0.015, 0.0038);
void main()
{
vec2 mainTexSize = 1.0 / textureSize(mainMap, 0);
vec2 preTexSize = mainTexSize*0.5;
vec3 mainCol = vec3(0.0);
vec3 preCol = vec3(0.0);
int count=0;
for(int i = -2; i < 3; ++i)
{
for(int j = -2; j < 3; ++j)
{
mainCol += texture(mainMap, vTexCoords + vec2(mainTexSize.x * i, mainTexSize.y * j)).rgb * weight[count];
preCol+=texture(preMap, vTexCoords + vec2(preTexSize.x * i, preTexSize.y * j)).rgb * weight[count];
count++;
}
}
FragColor = vec4(mainCol+preCol,1.0);
}
最后将输入纹理模糊后的颜色直接相加输出,效果如下:


浙公网安备 33010602011771号