<

这里主要讨论 Bloom效果引发的各种问题,及其解决方案。

记录几个高斯模糊的做法:

https://www.shadertoy.com/view/XdfGDH

https://www.shadertoy.com/view/ltScRG

https://gamedev.stackexchange.com/questions/166182/my-single-pass-gaussian-blur-looks-awful

https://github.com/Jam3/glsl-fast-gaussian-blur

原作者的做法这样的:

注意原作者的贴图从头到尾都不是抗锯齿(MSAA),所以操作起来一路顺手。而且场景也简单就几个贴图。

梁大大已经告诉我MSAA是废品,让我考虑FXAA。

而我的做法是:

hdrFBO里的纹理是多重采样纹理。我一开始想的是直接用FBO一步传递到一个postprocessing的buffer中处理。

也就是渲染只分2部分,一部分渲染场景,第二部分把这个渲染的2个buffer传递到postprocessing先测试。

遇到的问题就是:

1,hdrFBO多重采样纹理FBO渲染场景完成之后必须拷贝到post processing 的FBO.

2,hdrFBO多重采样FBO的第一个场景纹理复制过去到post processing没问题。

3,hdrFBO多重采样的第二个Texture(也就是第二个color attachment)是个多重采样纹理,要传递到post的材质,需要这么传:

4,顺便认识到MSAA的这种设计,直接限制了代码,让代码可以写的更烂

vec4 textureMSAA(sampler2DMS tex,vec2 TexCoords){

    ivec2 texSize = textureSize(tex);
    vec4 mtex=vec4(1);
    for(int i = 0; i < 32; ++i)
    {
        mtex += texelFetch(tex, ivec2(TexCoords*texSize ), i);
    }
    mtex = mtex / 32.0f;
    return mtex;
}
textureMSAA函数
#version 450 core
#extension GL_ARB_shading_language_include : require
#include "/shaders/common/utils.glsl"
#include "/shaders/common/postprocess.glsl"
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D sceneImage;
uniform sampler2DMS brightImage;
uniform float exposure;
void main()
{
    vec4 scene = texture(sceneImage,TexCoords);
    vec4 mtex = textureMSAA(brightImage,TexCoords);
    vec3 result = toneMapping(scene.rgb, exposure);
    FragColor = vec4(result,1.0f);

}

到这里顺便复习下以前做过的相关的Framebuffer和这个Quad post processing的关系。为什么有时候postprocessing需要个framebuffer?为什么有时候不要:

接下来现在用2中方案实现:思路如下:

实际中别用第一个方案,比较垃圾,直接第二个,把multisample color attachment 转换成普通的GL_TEXTURE_2D。然后就各种后期效果就行。 

多采样代码,注意里面的32 samplers,是在一开始设置framebuffer,renderbuffer就要提前设置好的,平常应该用unifrom int samplers从C++传入:

vec4 textureMSAA(sampler2DMS tex,vec2 TexCoords){

    ivec2 texSize = textureSize(tex);
    vec4 mtex=vec4(1);
    for(int i = 0; i < 32; ++i)
    {
        mtex += texelFetch(tex, ivec2(TexCoords*texSize ), i);
    }
    mtex = mtex / 32.0f;
    return mtex;
}

转换的伪代码就是:

// 2020 4 6
// This shader for convert multisampled buffer with multi attachements
// to basis GL_TEXTURE_2D
// frag file export two attachents, and input sampler2dMS texture


#version 450 core
#extension GL_ARB_shading_language_include : require
#include "/shaders/common/utils.glsl"


in vec2 TexCoords;

layout (location = 0 ) out vec4 colorAttament0;
layout (location = 1 ) out vec4 colorAttament1;
layout (location = 2 ) out vec4 colorAttament2;
layout (location = 3 ) out vec4 colorAttament3;
layout (location = 4 ) out vec4 colorAttament4;
layout (location = 5 ) out vec4 colorAttament5;
layout (location = 6 ) out vec4 colorAttament6;

// ... and you can export more texture , C++ code need:
// static FrameBufferAttachments<GL_RGBA16F,6> convertFBO;




// for study opengl, writing with hard code
// for best should use a uniform texture count : uniform int texCount;
// and: uniform sampler2dMS mtexs[texCount]

uniform sampler2DMS mtex0;
uniform sampler2DMS mtex1;
uniform sampler2DMS mtex2;
uniform sampler2DMS mtex3;
uniform sampler2DMS mtex4;
uniform sampler2DMS mtex5;
uniform sampler2DMS mtex6;

void main()
{

    colorAttament0 = textureMSAA(mtex0, TexCoords);
    colorAttament1 = textureMSAA(mtex1, TexCoords);
    colorAttament2 = textureMSAA(mtex2, TexCoords);
    colorAttament3 = textureMSAA(mtex3, TexCoords);
    colorAttament4 = textureMSAA(mtex4, TexCoords);
    colorAttament5 = textureMSAA(mtex5, TexCoords);
    colorAttament6 = textureMSAA(mtex6, TexCoords);


}

CP36:

逻辑:

   int display_w, display_h;
    glfwGetFramebufferSize(frameWindow->getWindow(), &display_w, &display_h);

    glm::mat4 view = camera->GetViewMatrix();
    glm::mat4 projection = glm::perspective(glm::radians(camera->fov),float(SRC_WIDTH) / float(SRC_HEIGHT),0.1f,  1000.0f);
    // object world transformation
    glm::mat4 model = glm::mat4(1.0f);

    // per-frame time logic
            // --------------------
    float currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;



    ImGui::Begin("Distance Light Params");
    ImGui::ColorEdit3("color", color);
    ImGui::SliderFloat("lightIntensity",&intensity,0,10.0f);
    ImGui::SliderFloat("shadowNear",&shadowNear,1,10.0f);
    ImGui::SliderFloat("shadowFar",&shadowFar,100,800.0f);
    distanceLight.setShadowNearFarPlane(shadowNear,shadowFar);
    // ----------------------------------- RENDER SHADOW MAP -----------------------------------
    // 1. render depth of scene to texture (from light's perspective)

    glViewport(0, 0, distanceLight.shadowPtr->width, distanceLight.shadowPtr->height);
    glBindFramebuffer(GL_FRAMEBUFFER, distanceLight.shadowPtr->fbo);
    glClear(GL_DEPTH_BUFFER_BIT);
    //distanceLight.setDir(glm::vec3())
    distanceLight.renderUseDepthShader();

    // Render SceneAssembly in depth shadow shader
    RenderScene(model,distanceLight.shadowPtr->lightView,distanceLight.shadowPtr->lightProjection,&distanceLight.shadowPtr->shader);
    // ----------------------------------- RENDER SHADOW MAP -----------------------------------
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    ImGui::End();


    // 2.---------------render scene as normal ,draw scene as normal in multisampled buffers---------------
    glViewport(0, 0, display_w, display_w/2);
    hdrMultiSamplerFBO.update(display_w,display_w/2);
    // update render buffer
    UpdateMultiSampledRenderBufferObject(display_w, display_w/2,multiRBO,samplers);
    glBindFramebuffer(GL_FRAMEBUFFER, hdrMultiSamplerFBO.fbo);       // BIND TO MULTI sampled fbo
    hdrMultiSamplerFBO.drawBuffers();
    ClearAllBufferColor();
    glEnable(GL_DEPTH_TEST);
    RenderScene(model,view,projection);
    RenderGridAndGnomon(view,projection);
    // render distance light in multiFBO
    distanceLight.setLightIntensity(intensity);
    distanceLight.shader.use();
    distanceLight.shader.setMatrix(view,projection);
    distanceLight.setLightColor(glm::vec3(color[0] ,color[1],color[2] ));
    distanceLight.draw();
     // 2.---------------render scene as normal ,draw scene as normal in multisampled buffers---------------





    // 3----------------------------- Render Scene to ConvertFBO ------------------------

    glViewport(0, 0, display_w, display_w/2);
    convertFBO.update(display_w, display_w/2);
    convertPostProcess.update(display_w,display_w/2);
    convertFBO.drawBuffers();
    glBindFramebuffer(GL_FRAMEBUFFER, convertFBO.fbo);; // render quad in convert FBO
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    convertPostProcess.shader.use();

    glActiveTexture(hdrMultiSamplerFBO.texChannels[0]);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, hdrMultiSamplerFBO.textures[0]);
    glActiveTexture(hdrMultiSamplerFBO.texChannels[1]);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, hdrMultiSamplerFBO.textures[1]);
    convertPostProcess.shader.setInt("tex0",hdrMultiSamplerFBO.texUnits[0]);
    convertPostProcess.shader.setInt("tex1",hdrMultiSamplerFBO.texUnits[1]);
    /*
    // This code verty simple, and it's do not use texture unit
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, hdrMultiSamplerFBO.textures[0]);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, hdrMultiSamplerFBO.textures[1]);
    convertPostProcess.shader.setInt("tex0",0);
    convertPostProcess.shader.setInt("tex1",1);
    */

    convertPostProcess.draw();

    // 3----------------------------- Render Scene to ConvertFBO ------------------------




    // 4 ------------- RENDER TO Post processing FBO -----------------

#if 1
    // use the convert FBO texture for final processing FBO
    toneMapping.update(display_w,display_w/2);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    toneMapping.shader.use();
    /*
    // // This code use texture unit
    glActiveTexture(convertFBO.texChannels[0]);
    glBindTexture(GL_TEXTURE_2D,convertFBO.textures[0]);
    toneMapping.shader.setInt("sceneImage", convertFBO.texUnits[0]);

    glActiveTexture(convertFBO.texChannels[1]);
    glBindTexture(GL_TEXTURE_2D,convertFBO.textures[1]);
    toneMapping.shader.setInt("brightImage", convertFBO.texUnits[1]);
    */

    //This code very simple, and it's do not use texture unit
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,convertFBO.textures[0]);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,convertFBO.textures[1]);
    toneMapping.shader.setInt("sceneImage", 0);
    toneMapping.shader.setInt("brightImage", 1);
    toneMapping.shader.setFloat("exposure",4.0f);
    toneMapping.draw();

#else
    toneMapping.update(display_w,display_w/2);
    CopyFrameBuffer(hdrMultiSamplerFBO.fbo, toneMapping.FBO, display_w, display_w / 2);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    ClearAllBufferColor();
    toneMapping.shader.use();


    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,toneMapping.FBOTexture);
    toneMapping.shader.setInt("sceneImage", 0);

    glActiveTexture(hdrMultiSamplerFBO.texChannels[1]);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE,hdrMultiSamplerFBO.textures[1]);
    toneMapping.shader.setInt("brightImage", hdrMultiSamplerFBO.texUnits[1]);

    toneMapping.shader.setFloat("exposure",4.0f);
    toneMapping.draw();
#endif
View Code

init:

// framebuffer
static MultiSampledFrameBufferAttachments<GL_RGBA16F,2> hdrMultiSamplerFBO;  // multisampler with two attachment
static GLuint multiRBO; // renderbuffer
static DrawPostProcessingQuad convertPostProcess;
static FrameBufferAttachments<GL_RGBA16F,2> convertFBO;  // multisampler convert to GL_TEXTURE_2D two attachment
//
    cout << "---------------- STARTING HDR Sampled FrameBuffer -----------
 ";
    // Create MultiSampler Framebuffer with two attached texture
    hdrMultiSamplerFBO.initialize(SRC_WIDTH,SRC_HEIGHT,samplers);
    hdrMultiSamplerFBO.setUpTexChannels(SceneAssembly::currentActiveTextureChannel,SceneAssembly::currentTextureLoaderIndex);
    hdrMultiSamplerFBO.drawBuffers();
    hdrMultiSamplerFBO.debug();
    cout << "---------------- ENDING HDR Sampled FrameBuffer -----------
 ";


    CreateMultiSampledRenderBufferObject(SRC_WIDTH, SRC_HEIGHT, multiRBO, samplers);



    cout << "
---------------- STARTING Convert FrameBuffer -----------
 ";
    // Create Convert Framebuffer with two attached texture
    convertFBO.initialize(SRC_WIDTH,SRC_HEIGHT);
    convertFBO.setUpTexChannels(SceneAssembly::currentActiveTextureChannel,SceneAssembly::currentTextureLoaderIndex);
    convertFBO.drawBuffers();
    convertFBO.debug();
    cout << "next
";
    cout << "---------------- ENDING Convert FrameBuffer -----------
 ";

CP_37:

接下来就是one pass的高斯模糊 bloom。

vec3 toneMapping(vec3 hdrColor ,float exposure){
    const float gamma = 2.2;
    // 曝光色调映射
    vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure);
    // Gamma校正
    mapped = pow(mapped, vec3(1.0 / gamma));
    return mapped;
}
// https://www.youtube.com/watch?v=01xAJ5giuu0
// https://zhuanlan.zhihu.com/p/21983679
vec3 ReinhardMapping(vec3 hdrColor)
{
    vec3 mapped = hdrColor / (1.0f +hdrColor);
    mapped = pow(mapped, vec3(1.0 / 2.2f));
    return mapped;
}




vec3 ACESToneMapping(vec3 color,float exposure)
{
    const float gamma = 2.2;
    const float A = 2.51f;
    const float B = 0.03f;
    const float C = 2.43f;
    const float D = 0.59f;
    const float E = 0.14f;
    color *= exposure;
    vec3 mapped =  (color * (A * color + B)) / (color * (C * color + D) + E);
    mapped = pow(mapped, vec3(1.0/gamma));
    return mapped;
}


//precision mediump float;
float normpdf(in float x, in float sigma)
{
    return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma;
}



vec3 gaussianBlur(sampler2D tex, vec2 texCoords){
    //https://www.shadertoy.com/view/XdfGDH
    //declare stuff
    const int mSize = 30;
    const int kSize = (mSize-1)/2;
    float kernel[mSize];
    vec3 final_colour = vec3(0.0);

    //create the 1-D kernel
    float sigma = 7.0;
    float Z = 0.0;
    for (int j = 0; j <= kSize; ++j)
    {
        kernel[kSize+j] = kernel[kSize-j] = normpdf(float(j), sigma);
    }

    //get the normalization factor (as the gaussian has been clamped)
    for (int j = 0; j < mSize; ++j)
    {
        Z += kernel[j];
    }

    //read out the texels
    for (int i=-kSize; i <= kSize; ++i)
    {
        for (int j=-kSize; j <= kSize; ++j)
        {
            //https://gamedev.stackexchange.com/questions/166182/my-single-pass-gaussian-blur-looks-awful
            final_colour += kernel[kSize+j]*kernel[kSize+i]* texture(tex, (texCoords+vec2(float(i),float(j))/textureSize(tex, 0).xy)  ).rgb;

        }
    }

    return final_colour/(Z*Z);
}
单层高斯模糊.glsl
#version 450 core
#extension GL_ARB_shading_language_include : require
#include "/shaders/common/utils.glsl"
#include "/shaders/common/postprocess.glsl"

out vec4 FragColor;
in vec2 TexCoords;

uniform sampler2D sceneImage;
uniform sampler2D brightImage;
uniform float exposure;

void main()
{
    vec3 scene = texture(sceneImage,TexCoords).rgb;
    vec3 brightColor = texture(brightImage,TexCoords).rgb;
    vec3 blur = gaussianBlur(brightImage,TexCoords).rgb;

    scene+= blur*3.0;

    //scene = toneMapping(scene, exposure) ;
    scene = ACESToneMapping(scene,exposure);
    FragColor = vec4( scene , 1.0f);
}

CP_38:

two pass blur:

 bool horizontal = true, first_iteration = true;
    unsigned int amount = 400;
    pingpongBlurShader.use();
    for (unsigned int i = 0; i < amount; i++)
    {
        glBindFramebuffer(GL_FRAMEBUFFER, pingpongPost[horizontal].FBO);
        pingpongBlurShader.setInt("horizontal", 1);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, first_iteration ? convertFBO.textures[1] : pingpongPost[!horizontal].FBOTexture);  // bind texture of other framebuffer (or scene if first iteration)
        pingpongPost[0].draw(pingpongBlurShader);
        horizontal = !horizontal;
        if (first_iteration)
          first_iteration = false;
    }

原文地址:https://www.cnblogs.com/gearslogy/p/12623553.html