• QT With OpenGL(泛光)(Bloom)


    一、渲染场景到HDR帧缓存

    1. HDR帧缓存需要两个颜色纹理–MRT(Multiple Render Targets,多渲染目标)

    HDRFBO = new QOpenGLFramebufferObject(size(),QOpenGLFramebufferObject::CombinedDepthStencil,GL_TEXTURE,GL_RGBA16F);
    HDRFBO->addColorAttachment(size(),GL_RGBA16F);
    core = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
    GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
    core->glDrawBuffers(2, buffers);
    HDRFBO->release();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用QOpenGLFunctions_3_3_Core调用glDrawBuffers显式告知OpenGL渲染到多个颜色缓冲。

    补充:
    QOpenGLFunctions 是OpenGL ES 2.0 API,所以只有部分接口可用。
    The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API. More…
    在官方文档中可以看到ES中没有glDrawBuffers。
    在这里插入图片描述

    2. 将场景渲染到HDR帧缓存,提取高光图。

    修改shader,使用多颜色缓冲渲染

    //输出
    layout (location = 0) out vec4 FragColor;
    layout (location = 1) out vec4 BrightColor;
    
    • 1
    • 2
    • 3
           //检查该像素亮度是否高于阈值
           float brightness = dot(FragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
           if(brightness > 1.0) BrightColor = vec4(FragColor.rgb, 1.0);
    
    • 1
    • 2
    • 3

    3. 将HDR帧缓存渲染到屏幕,并可切换查看颜色缓冲1、2

    HDRFBO->release();
        //HDR输出
        glViewport(0,0,width(),height());
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
        BloomShader->bind();//shader
        BloomShader->setUniformValue("RenderResult",0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D,HDRFBO->textures().at(HDRNUM));
        renderQuad();
        BloomShader->release();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    在这里插入图片描述
    提取高光效果并没有想象中的好,按照直觉,应该只提取发光物的高光效果,但是环境中也出现了大量提取点。
    且,若背对光源(发光面不可见,但发光物体可见),则不会出现泛光现象,与想象中不同。

    二、 高斯模糊

    理论

    在这里插入图片描述
    在这里插入图片描述

    性质

    1. 二维高斯模糊可以通过组合两个一维高斯模糊来应用:
      在这里插入图片描述
    2. 应用连续的高斯模糊与应用单个较大的高斯模糊的效果相同,高斯模糊的半径是模糊半径平方和的平方根。

    高斯函数的这两个性质都给我们提供了优化的空间。

    垂直过滤器

    uniform sampler2D image;
     
    out vec4 FragmentColor;
     
    uniform float offset[5] = float[](0.0, 1.0, 2.0, 3.0, 4.0);
    uniform float weight[5] = float[](0.2270270270, 0.1945945946, 0.1216216216,
                                      0.0540540541, 0.0162162162);
     
    void main(void) {
        FragmentColor = texture2D(image, vec2(gl_FragCoord) / 1024.0) * weight[0];
        for (int i=1; i<5; i++) {
            FragmentColor +=
                texture2D(image, (vec2(gl_FragCoord) + vec2(0.0, offset[i])) / 1024.0)
                    * weight[i];
            FragmentColor +=
                texture2D(image, (vec2(gl_FragCoord) - vec2(0.0, offset[i])) / 1024.0)
                    * weight[i];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    引用GPU硬件加速,使用GPU双线性滤波器优化

    uniform sampler2D image;
     
    out vec4 FragmentColor;
     
    uniform float offset[3] = float[](0.0, 1.3846153846, 3.2307692308);
    uniform float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703);
     
    void main(void) {
        FragmentColor = texture2D(image, vec2(gl_FragCoord) / 1024.0) * weight[0];
        for (int i=1; i<3; i++) {
            FragmentColor +=
                texture2D(image, (vec2(gl_FragCoord) + vec2(0.0, offset[i])) / 1024.0)
                    * weight[i];
            FragmentColor +=
                texture2D(image, (vec2(gl_FragCoord) - vec2(0.0, offset[i])) / 1024.0)
                    * weight[i];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    图解

    垂直过滤一次:红色色块的颜色为上下5个色块颜色的加权和

    • 红色权重表示:0.227027
    • 橙色权重表示:0.1945946
    • 黄色权重表示:0.1216216
    • 灰色权重表示:0.054054
    • 白色权重表示(没画):0.054054
      在这里插入图片描述
      再将红色色块进行一次水平过滤,即将该范围内的像素进行了如下加权(红色色块为最终得到的像素块,其他颜色色块仅表示加权权值)
      如:
    • 红色色块加权为 0.227027 * 0.227027
    • 红色上方色块(暗黄色,柠檬黄,灰色)加权分别为:0.1945946 ,0.1216216, 0.054054
    • 红色右方色块(橘黄色,土黄色,淡绿色)加权分别为:
      0.227027 * 0.1945946
      0.227027 * 0.1216216
      0.227027 * 0.054054
    • 棕色加权为0.1945946 * 0.1945946
    • 其他类推
      在这里插入图片描述
      可以看出以这种形式组成的高斯模糊,并非是严格意义上的高斯模糊,而是一种近似。但若继续循环如上的垂直-水平过滤器,则结果与实际高斯模糊效果近似。循环次数越多,模糊效果越强。

    对高光图应用高斯模糊

    (1)编写高斯模糊shader

    使用LearnOpenGL代码

    #version 450 core
    out vec4 FragColor;
    in vec2 TexCoords;
    uniform sampler2D image;
    uniform bool horizontal;
    uniform float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
    void main()
    {
        vec2 tex_offset = 1.0 / textureSize(image, 0); // gets size of single texel
        vec3 result = texture(image, TexCoords).rgb * weight[0]; // current fragment's contribution
        if(horizontal)
        {
            for(int i = 1; i < 5; ++i)
            {
                result += texture(image, TexCoords + vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
                result += texture(image, TexCoords - vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
            }
        }
        else
        {
            for(int i = 1; i < 5; ++i)
            {
                result += texture(image, TexCoords + vec2(0.0, tex_offset.y * i)).rgb * weight[i];
                result += texture(image, TexCoords - vec2(0.0, tex_offset.y * i)).rgb * weight[i];
            }
        }
        FragColor = vec4(result, 1.0);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    (2)使用两个FBO

    //高斯模糊缓冲
    GBHorizontal = new QOpenGLFramebufferObject(size(),QOpenGLFramebufferObject::NoAttachment,GL_TEXTURE_2D,GL_RGBA16F);
    GBVertical = new QOpenGLFramebufferObject(size(),QOpenGLFramebufferObject::NoAttachment,GL_TEXTURE_2D,GL_RGBA16F);
    
    • 1
    • 2
    • 3

    注意再resizeGL中也要设置。

    (3)高斯模糊

    //高斯模糊
        GLboolean horizontal = true, first_iteration = true;
        GLuint amount = 10;
        GaussianBlurShader->bind();
        for (GLuint i = 0; i < amount; i++)
        {
            if(horizontal)GBHorizontal->bind();
            else GBVertical->bind();
            //glClear(GL_COLOR_BUFFER_BIT);
            GaussianBlurShader->setUniformValue("horizontal",horizontal);
            GaussianBlurShader->setUniformValue("image",0);
            glActiveTexture(GL_TEXTURE0);
            unsigned int id = 0;
            if(first_iteration){
                id = HDRFBO->textures().at(1);
            }
            else{
                if(horizontal)id = GBVertical->texture();
                else id = GBHorizontal->texture();
            }
            glBindTexture(GL_TEXTURE_2D, id);
            renderQuad();
            horizontal = !horizontal;
            if (first_iteration)
                first_iteration = false;
        }
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    结果

    模糊前
    在这里插入图片描述
    模糊后
    在这里插入图片描述

    三、把两个纹理混合

    有了场景的HDR纹理和模糊处理的亮区纹理,我们只需把它们结合起来就能实现泛光或称光晕效果了。最终的像素着色器(大部分和HDR教程用的差不多)要把两个纹理混合:

    最简单的混合(直接相加)

    #version 450 core
    out vec4 FragColor;
    
    in vec2 TexCoords;
    
    uniform sampler2D RenderResult;
    uniform sampler2D bloomBlur;
    
    void main()
    {
        vec3 hdrColor = texture(RenderResult, TexCoords).rgb;
        vec3 bloomColor = texture(bloomBlur, TexCoords).rgb;
        if(addBloom) hdrColor += bloomColor;
    
        FragColor = vec4(hdrColor , 1.0f);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    结果

    在这里插入图片描述
    头顶光源出现泛光现象
    在这里插入图片描述
    但除了光源其他物体也有泛光现象(这不合理)。
    应该修改为只有光源才出现泛光现象

    维护

    待更新…

  • 相关阅读:
    MySQL表的增查(进阶)
    [在线文档]学习编程常见的在线教程和文档
    实时即未来!Flink Forward Asia 2021 议程正式上线!
    【C++编程能力提升】
    LeetCode刷题
    IntelliJ IDEA 2023.2 最新变化
    【docker 实战】Docker数据卷的清理
    滚动条详解:跨平台iOS、Android、小程序滚动条隐藏及自定义样式综合指南
    Figma切图,轻松上手!
    STM32个人笔记-RS485通讯
  • 原文地址:https://blog.csdn.net/weixin_44518102/article/details/126137704