高質量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號