diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj index 6b9b1e0..162402e 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj @@ -213,6 +213,8 @@ + + diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters index 07da7a6..bf7d9be 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters @@ -466,6 +466,8 @@ + + diff --git a/ArchitectureColoredPainting/RendererWidget.ui b/ArchitectureColoredPainting/RendererWidget.ui index f741d0e..4ad3fcd 100644 --- a/ArchitectureColoredPainting/RendererWidget.ui +++ b/ArchitectureColoredPainting/RendererWidget.ui @@ -81,6 +81,16 @@ + + + + SSAO + + + true + + + diff --git a/ArchitectureColoredPainting/res/MainWindow.qrc b/ArchitectureColoredPainting/res/MainWindow.qrc index af9f79c..9150e6b 100644 --- a/ArchitectureColoredPainting/res/MainWindow.qrc +++ b/ArchitectureColoredPainting/res/MainWindow.qrc @@ -34,6 +34,8 @@ Shaders/pageId_downsample.comp Shaders/tone_mapping.comp Shaders/painting_shadow.frag + Shaders/ssao.comp + Shaders/ssao_blur.comp qt.conf diff --git a/ArchitectureColoredPainting/res/Shaders/shadow_mapping.comp b/ArchitectureColoredPainting/res/Shaders/shadow_mapping.comp index a82e706..12c251b 100644 --- a/ArchitectureColoredPainting/res/Shaders/shadow_mapping.comp +++ b/ArchitectureColoredPainting/res/Shaders/shadow_mapping.comp @@ -5,7 +5,7 @@ layout (local_size_x = 8, local_size_y = 8) in; layout(rgba8, binding = 0) uniform image2D gBaseColor; uniform sampler2D gNormal; uniform sampler2D gPosition; -uniform sampler2D gMetallicRoughness; +uniform sampler2D gMetallicRoughnessAO; uniform sampler2DArray gShadowMap; uniform samplerCube irradianceMap; @@ -187,8 +187,9 @@ void main() vec4 baseColor = imageLoad(gBaseColor, pixelLocation); if(baseColor.a == 0) return; vec3 albedo = pow(baseColor.rgb, vec3(2.2)); - float metallic = texelFetch(gMetallicRoughness, pixelLocation, 0).r; - float roughness = texelFetch(gMetallicRoughness, pixelLocation, 0).g; + float metallic = texelFetch(gMetallicRoughnessAO, pixelLocation, 0).r; + float roughness = texelFetch(gMetallicRoughnessAO, pixelLocation, 0).g; + float ambientOcclusion = 1.0 - texelFetch(gMetallicRoughnessAO, pixelLocation, 0).b; vec3 worldPos = texelFetch(gPosition, pixelLocation,0).xyz; vec3 normal = (texelFetch(gNormal, pixelLocation,0).xyz - vec3(0.5)) * 2; @@ -224,7 +225,7 @@ void main() float NdotL = max(dot(normal, L), 0.0); vec3 Lo = (kD * albedo / PI + specular) * radiance * NdotL; - vec3 ambient = ambientLighting(normal, V, F0, albedo, metallic, roughness, 1); + vec3 ambient = ambientLighting(normal, V, F0, albedo, metallic, roughness, 1) * ambientOcclusion; int debugLayer; float shadow = ShadowCalculation(worldPos, normal, debugLayer); diff --git a/ArchitectureColoredPainting/res/Shaders/ssao.comp b/ArchitectureColoredPainting/res/Shaders/ssao.comp new file mode 100644 index 0000000..54b7c80 --- /dev/null +++ b/ArchitectureColoredPainting/res/Shaders/ssao.comp @@ -0,0 +1,63 @@ +#version 450 core + +layout (local_size_x = 8, local_size_y = 8) in; + +layout(rgba8, binding = 1) uniform image2D gMetallicRoughnessAO; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D texNoise; + +uniform vec3 samples[64]; + +// parameters (you'd probably want to use them as uniforms to more easily tweak the effect) +int kernelSize = 32; +float radius = 0.5; +float bias = 0.025; + +// tile noise texture over screen based on screen dimensions divided by noise size +const vec2 noiseScale = vec2(1./4.0, 1./4.0); + +uniform mat4 projection; +uniform mat4 view; + +void main() +{ + ivec2 pixelLocation = ivec2(gl_GlobalInvocationID.xy); + + // get input for SSAO algorithm + vec3 worldPos = texelFetch(gPosition, pixelLocation, 0).xyz; + vec3 fragPos = (view * vec4(worldPos, 1.0)).xyz; + vec3 normal = normalize((texelFetch(gNormal, pixelLocation, 0).xyz - vec3(0.5)) * 2); + vec3 randomVec = normalize(texture(texNoise, vec2(pixelLocation) * noiseScale).xyz); + // create TBN change-of-basis matrix: from tangent-space to view-space + vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 TBN = mat3(tangent, bitangent, normal); + // iterate over the sample kernel and calculate occlusion factor + float occlusion = 0.0; + for(int i = 0; i < kernelSize; ++i) + { + // get sample position + vec3 samplePos = TBN * samples[i]; // from tangent to view-space + samplePos = fragPos + samplePos * radius; + + // project sample position (to sample texture) (to get position on screen/texture) + vec4 offset = vec4(samplePos, 1.0); + offset = projection * offset; // from view to clip-space + offset.xyz /= offset.w; // perspective divide + offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0 + + // get sample depth + float sampleDepth = (view * vec4(texture(gPosition, offset.xy).xyz, 1.0)).z; // get depth value of kernel sample + + // range check & accumulate + float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth)); + occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck; + } + occlusion = occlusion / kernelSize; + + vec4 color = imageLoad(gMetallicRoughnessAO, pixelLocation); + color.b = occlusion; + imageStore(gMetallicRoughnessAO, pixelLocation, color); +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/res/Shaders/ssao_blur.comp b/ArchitectureColoredPainting/res/Shaders/ssao_blur.comp new file mode 100644 index 0000000..0588310 --- /dev/null +++ b/ArchitectureColoredPainting/res/Shaders/ssao_blur.comp @@ -0,0 +1,23 @@ +#version 450 core + +layout (local_size_x = 8, local_size_y = 8) in; + +layout(rgba8, binding = 1) uniform image2D gMetallicRoughnessAO; + +void main() +{ + ivec2 pixelLocation = ivec2(gl_GlobalInvocationID.xy); + float result = 0.0; + for (int x = -1; x < 2; ++x) + { + for (int y = -1; y < 2; ++y) + { + ivec2 samplePos = pixelLocation + ivec2(x, y); + samplePos = clamp(samplePos, ivec2(0), imageSize(gMetallicRoughnessAO) - ivec2(1)); + result += imageLoad(gMetallicRoughnessAO, samplePos).b; + } + } + vec4 color = imageLoad(gMetallicRoughnessAO, pixelLocation); + color.b = result / (3.0 * 3.0); + imageStore(gMetallicRoughnessAO, pixelLocation, color); +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Renderer/Camera.h b/ArchitectureColoredPainting/src/Renderer/Camera.h index b8090cc..b6686f8 100644 --- a/ArchitectureColoredPainting/src/Renderer/Camera.h +++ b/ArchitectureColoredPainting/src/Renderer/Camera.h @@ -25,7 +25,7 @@ namespace Renderer // Default camera values static constexpr float YAW = -90.0f; static constexpr float PITCH = 0.0f; - static constexpr float SPEED = 200.f; + static constexpr float SPEED = 2.f; static constexpr float SENSITIVITY = 0.1f; static constexpr float ZOOM = 45.0f; // camera Attributes diff --git a/ArchitectureColoredPainting/src/Renderer/Model.cpp b/ArchitectureColoredPainting/src/Renderer/Model.cpp index cea5989..bdca36c 100644 --- a/ArchitectureColoredPainting/src/Renderer/Model.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Model.cpp @@ -87,7 +87,6 @@ void Renderer::Model::loadModel(QString path) aiMatrix4x4 transform; - aiMatrix4x4::Scaling(aiVector3D(1 / 0.008), transform); processNode(scene->mRootNode, scene, transform * scene->mRootNode->mTransformation); AABB.clear(); AABB.emplace_back(minPos.x, minPos.y, minPos.z); diff --git a/ArchitectureColoredPainting/src/Renderer/RenderPass.cpp b/ArchitectureColoredPainting/src/Renderer/RenderPass.cpp index b8dc345..ca45214 100644 --- a/ArchitectureColoredPainting/src/Renderer/RenderPass.cpp +++ b/ArchitectureColoredPainting/src/Renderer/RenderPass.cpp @@ -1,6 +1,7 @@ #include #include "RenderPass.h" #include "IblUtils.h" +#include namespace Renderer { @@ -132,6 +133,75 @@ namespace Renderer } } + SsaoPass::SsaoPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness, QMatrix4x4& projection, QMatrix4x4& view) + : RenderPass(gl) + , ssaoShader(":/Shaders/ssao.comp") + , blurShader(":/Shaders/ssao_blur.comp") + , frameWidth(frameWidth) + , frameHeight(frameHeight) + , gNormal(gNormal) + , gPosition(gPosition) + , gMetallicRoughness(gMetallicRoughness) + , projection(projection) + , view(view) + { + // generate sample kernel + // ---------------------- + std::uniform_real_distribution randomFloats(0.0, 1.0); // generates random floats between 0.0 and 1.0 + std::default_random_engine generator; + for (unsigned int i = 0; i < 64; ++i) + { + QVector3D sample(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, randomFloats(generator)); + sample.normalize(); + sample *= randomFloats(generator); + float scale = float(i) / 64.0; + + // scale samples s.t. they're more aligned to center of kernel + scale = std::lerp(0.1f, 1.0f, scale * scale); + sample *= scale; + ssaoKernel.push_back(sample); + } + + // generate noise texture + // ---------------------- + std::vector ssaoNoise; + for (unsigned int i = 0; i < 16; i++) + // rotate around z-axis (in tangent space) + ssaoNoise.emplace_back(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f); + + gl->GenTextures(1, &noiseTexture); + gl->BindTexture(GL_TEXTURE_2D, noiseTexture); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + ssaoShader.bind(); + ssaoShader.setUniformValue("gNormal", gNormalBinding); + ssaoShader.setUniformValue("gPosition", gPositionBinding); + ssaoShader.setUniformValue("texNoise", texNoiseBinding); + ssaoShader.release(); + } + + void SsaoPass::dispatch() + { + ssaoShader.bind(); + ssaoShader.setUniformValueArray("samples", ssaoKernel.data(), ssaoKernel.size()); + ssaoShader.setUniformValue("projection", projection); + ssaoShader.setUniformValue("view", view); + gl->BindTextureUnit(gNormalBinding, gNormal); + gl->BindTextureUnit(gPositionBinding, gPosition); + gl->BindTextureUnit(texNoiseBinding, noiseTexture); + gl->BindImageTexture(1, gMetallicRoughness, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); + gl->DispatchCompute(ceil(frameWidth / 8.), ceil(frameHeight / 8.), 1); + ssaoShader.release(); + + blurShader.bind(); + gl->DispatchCompute(ceil(frameWidth / 8.), ceil(frameHeight / 8.), 1); + blurShader.release(); + } + LightingPass::LightingPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, QMatrix4x4& view, Camera& camera, Light& light, GLuint& gBaseColor, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness, GLuint& shadowGbuffer, GLuint& irradianceMap, GLuint& prefilterMap, GLuint& brdfLUTTexture) : RenderPass(gl) , pbrShader(":/Shaders/shadow_mapping.comp") @@ -149,7 +219,7 @@ namespace Renderer pbrShader.bind(); pbrShader.setUniformValue("gNormal", gNormalBinding); pbrShader.setUniformValue("gPosition", gPositionBinding); - pbrShader.setUniformValue("gMetallicRoughness", gMetallicRoughnessBinding); + pbrShader.setUniformValue("gMetallicRoughnessAO", gMetallicRoughnessBinding); pbrShader.setUniformValue("gShadowMap", gShadowMapBinding); pbrShader.setUniformValue("irradianceMap", irradianceMapBinding); pbrShader.setUniformValue("prefilterMap", prefilterMapBinding); @@ -284,4 +354,5 @@ namespace Renderer { enableFxaa = enable; } + } \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Renderer/RenderPass.h b/ArchitectureColoredPainting/src/Renderer/RenderPass.h index 218d9b1..0da8054 100644 --- a/ArchitectureColoredPainting/src/Renderer/RenderPass.h +++ b/ArchitectureColoredPainting/src/Renderer/RenderPass.h @@ -23,7 +23,7 @@ namespace Renderer { public: ShadowMapPass(GladGLContext* gl, GLuint& shadowFboHandle, int& shadowMapResolution, Light& light, Model*& model); - void dispatch(); + void dispatch() override; private: Shader modelShadowShader, paintingShadowShader; GLuint& shadowFboHandle; @@ -38,7 +38,7 @@ namespace Renderer public: GeometryPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, QOpenGLFramebufferObject*& fbo, Model*& model, QMatrix4x4& projection, QMatrix4x4& view); - void dispatch(); + void dispatch() override; private: Shader modelShader, paintingShader, plainShader; unsigned int& frameWidth; @@ -54,7 +54,7 @@ namespace Renderer public: PageIDFeedbackPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, QOpenGLFramebufferObject*& fbo, GLuint* gBuffers, GLuint& gPageID, VirtualTextureManager& vtManager); - void dispatch(); + void dispatch() override; private: Shader pageIdDownsampleShader; unsigned int& frameWidth; @@ -65,6 +65,28 @@ namespace Renderer VirtualTextureManager& vtManager; }; + class SsaoPass : public RenderPass + { + public: + SsaoPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness, QMatrix4x4& projection, QMatrix4x4& view); + void dispatch() override; + private: + Shader ssaoShader, blurShader; + std::vector ssaoKernel; + GLuint noiseTexture; + unsigned int& frameWidth; + unsigned int& frameHeight; + GLuint& gNormal; + GLuint& gPosition; + GLuint& gMetallicRoughness; + QMatrix4x4& projection; + QMatrix4x4& view; + + constexpr static GLuint gNormalBinding = 1, + gPositionBinding = 2, + texNoiseBinding = 3; + }; + class LightingPass : public RenderPass { public: @@ -72,7 +94,7 @@ namespace Renderer QMatrix4x4& view, Camera& camera, Light& light, GLuint& gBaseColor, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness, GLuint& shadowGbuffer, GLuint& irradianceMap, GLuint& prefilterMap, GLuint& brdfLUTTexture); - void dispatch(); + void dispatch() override; private: Shader pbrShader; unsigned int& frameWidth; @@ -102,7 +124,7 @@ namespace Renderer { public: SkyboxPass(GladGLContext* gl, QOpenGLFramebufferObject*& fbo, QMatrix4x4& projection, QMatrix4x4& view, GLuint& skyCubemap); - void dispatch(); + void dispatch() override; private: Shader skyBoxShader; QOpenGLFramebufferObject*& fbo; @@ -117,7 +139,7 @@ namespace Renderer { public: ToneMappingPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gBaseColor, float& exposure); - void dispatch(); + void dispatch() override; private: Shader toneMappingShader; @@ -131,7 +153,7 @@ namespace Renderer { public: FinalPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gBaseColor); - void dispatch(); + void dispatch() override; void setEnableFxaa(bool enable); private: Shader finalShader; diff --git a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp index c61ca57..ef5a218 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp +++ b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp @@ -19,7 +19,7 @@ static int sunSpeed = 10; RendererGLWidget::RendererGLWidget(QWidget* parent) : QOpenGLWidget(parent) - , camera(QVector3D(0.0f, 100.0f, 0.0f)) + , camera(QVector3D(0.0f, 1.0f, 0.0f)) , light(&camera) { //startTimer(); @@ -97,6 +97,11 @@ void Renderer::RendererGLWidget::setEnableFxaa(bool enable) finalPass->setEnableFxaa(enable); } +void Renderer::RendererGLWidget::setEnableSsao(bool enable) +{ + enableSsao = enable; +} + QOpenGLTexture randomMap(QOpenGLTexture::Target2D); void GLAPIENTRY messageCallback(GLenum source, @@ -139,6 +144,7 @@ void RendererGLWidget::initializeGL() shadowMapPass = std::make_unique(gl.get(), shadowFboHandle, shadowMapResolution, light, model); geometryPass = std::make_unique(gl.get(), frameWidth, frameHeight, fboPtr, model, projection, view); pageIDFeedbackPass = std::make_unique(gl.get(), frameWidth, frameHeight, fboPtr, gbuffers, gPageID, *vtManager); + ssaoPass = std::make_unique(gl.get(), frameWidth, frameHeight, gNormal, gPosition, gMetallicRoughness, projection, view); lightingPass = std::make_unique(gl.get(), frameWidth, frameHeight, view, camera, light, gBaseColor, gNormal, gPosition, gMetallicRoughness, shadowGbuffer, irradianceMap, prefilterMap, brdfLUTTexture); skyboxPass = std::make_unique(gl.get(), fboPtr, projection, view, skyCubemap); @@ -213,6 +219,8 @@ void RendererGLWidget::paintGL() pageIDFeedbackPass->dispatch(); //gl->Finish(); //vtManager->commitMutex.unlock(); + if (enableSsao) + ssaoPass->dispatch(); lightingPass->dispatch(); skyboxPass->dispatch(); toneMappingPass->dispatch(); @@ -293,12 +301,16 @@ void RendererGLWidget::resizeGL(int width, int height) gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, frameWidth, frameHeight, 0, GL_RGB, GL_FLOAT, NULL); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gbuffers[2], 0); - //MetallicRoughness + //R: Metallic, G: Roughness, B: AO gl->BindTexture(GL_TEXTURE_2D, gbuffers[3]); - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RG8, frameWidth, frameHeight, 0, GL_RG, GL_UNSIGNED_BYTE, NULL); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, frameWidth, frameHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, gbuffers[3], 0); //gPageID gl->BindTexture(GL_TEXTURE_2D, gbuffers[4]); diff --git a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h index 0fb9b1a..9034790 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h +++ b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h @@ -35,6 +35,7 @@ namespace Renderer void setMainLightYaw(float yaw); void setExposure(float exposure); void setEnableFxaa(bool enable); + void setEnableSsao(bool enable); protected: void initializeGL() override; void paintGL() override; @@ -56,6 +57,8 @@ namespace Renderer int shadowMapResolution; float exposure = 0.8; + bool enableSsao = true; + QMatrix4x4 projection; QMatrix4x4 view; @@ -64,6 +67,7 @@ namespace Renderer std::unique_ptr shadowMapPass; std::unique_ptr geometryPass; std::unique_ptr pageIDFeedbackPass; + std::unique_ptr ssaoPass; std::unique_ptr lightingPass; std::unique_ptr skyboxPass; std::unique_ptr toneMappingPass; diff --git a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp index 185f1ac..92e3f19 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp +++ b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp @@ -68,6 +68,9 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent) QObject::connect(ui.fxaaCheckBox, &QCheckBox::toggled, this, [&](bool checked) { ui.openGLWidget->setEnableFxaa(checked); }); + QObject::connect(ui.ssaoCheckBox, &QCheckBox::toggled, this, [&](bool checked) { + ui.openGLWidget->setEnableSsao(checked); + }); QObject::connect(ui.lightRadianceSlider, &QSlider::valueChanged, [&](int value) { ui.openGLWidget->setMainLightRadiance(value / 10.f * QVector3D(0.7529, 0.7450, 0.6784).normalized());