diff --git a/Editor/resources/Shaders/blur.shader b/Editor/resources/Shaders/blur.shader new file mode 100644 index 00000000..4e28d40d --- /dev/null +++ b/Editor/resources/Shaders/blur.shader @@ -0,0 +1,36 @@ +#shader vertex +#version 460 core + +layout(location = 0) in vec3 VertexPosition; +layout(location = 1) in vec2 UVPosition; + +out flat vec2 UV; + +void main() +{ + UV = UVPosition; + gl_Position = vec4(VertexPosition, 1.0f); +} + +#shader fragment +#version 460 core +out vec4 FragColor; + +in vec2 UV; + +uniform sampler2D u_Input; + +void main() { + vec2 texelSize = 1.0 / vec2(textureSize(u_Input, 0)); + float result = 0.0; + for (int x = -2; x < 2; ++x) + { + for (int y = -2; y < 2; ++y) + { + vec2 offset = vec2(float(x), float(y)) * texelSize; + result += texture(u_Input, UV + offset).r; + } + } + float channel = result / (4.0 * 4.0); + FragColor = vec4(channel, channel, channel, 1.0); +} \ No newline at end of file diff --git a/Editor/resources/Shaders/deferred.shader b/Editor/resources/Shaders/deferred.shader index b3b823a4..10f8bd3b 100644 --- a/Editor/resources/Shaders/deferred.shader +++ b/Editor/resources/Shaders/deferred.shader @@ -37,6 +37,7 @@ uniform sampler2D m_Depth; uniform sampler2D m_Albedo; uniform sampler2D m_Material; uniform sampler2D m_Normal; +uniform sampler2D m_SSAO; // Lights const int MaxLight = 29; @@ -221,7 +222,7 @@ void main() float metallic = texture(m_Material, UV).r; float roughness = texture(m_Material, UV).b; float ao = texture(m_Material, UV).g; - + float ssao = texture(m_SSAO, UV).r; vec3 N = normal; vec3 V = normalize(u_EyePosition - worldPos); vec3 R = reflect(-V, N); @@ -280,8 +281,8 @@ void main() vec3 kD = 1.0 - kS; kD *= 1.0 - metallic; - vec3 ambient = (kD * albedo) * ao; - vec3 color = ambient + Lo; + vec3 ambient = (kD * albedo) * (ao); + vec3 color = (ambient * ssao) + Lo; // Display CSM splits.. /*float depth = length(worldPos - u_EyePosition); diff --git a/Editor/resources/Shaders/ssao.shader b/Editor/resources/Shaders/ssao.shader new file mode 100644 index 00000000..111e2023 --- /dev/null +++ b/Editor/resources/Shaders/ssao.shader @@ -0,0 +1,115 @@ +#shader vertex +#version 460 core + +layout(location = 0) in vec3 VertexPosition; +layout(location = 1) in vec2 UVPosition; + +out flat vec2 UV; +out mat4 v_InvView; +out mat4 v_InvProjection; +out mat4 v_View; + +uniform mat4 u_View; +uniform mat4 u_Projection; + +void main() +{ + v_View = u_View; + v_InvView = inverse(u_View); + v_InvProjection = inverse(u_Projection); + + UV = UVPosition; + gl_Position = vec4(VertexPosition, 1.0f); +} + +#shader fragment +#version 460 core + +uniform sampler2D u_Depth; +uniform sampler2D u_Normal; +uniform sampler2D u_Noise; + +uniform int u_KernelSize = 64; + +uniform vec3 u_Samples[64]; +uniform mat4 u_Projection; +uniform vec2 u_NoiseScale; +uniform float u_Radius = 0.5f; +uniform float u_Bias = 0.025f; +uniform float u_Area; +in vec2 UV; +in mat4 v_View; +in mat4 v_InvView; +in mat4 v_InvProjection; + +out vec4 FragColor; + +vec3 WorldPosFromDepth(float depth) +{ + float z = depth * 2.0 - 1.0; + + vec4 clipSpacePosition = vec4(UV * 2.0 - 1.0, z, 1.0); + vec4 viewSpacePosition = v_InvProjection * clipSpacePosition; + + // Perspective division + viewSpacePosition /= viewSpacePosition.w; + + vec4 worldSpacePosition = v_InvView * viewSpacePosition; + + return worldSpacePosition.xyz; +} + +vec3 ViewPosFromDepth(float depth) +{ + float z = depth * 2.0 - 1.0; + + vec4 clipSpacePosition = vec4(UV * 2.0 - 1.0, z, 1.0); + vec4 viewSpacePosition = v_InvProjection * clipSpacePosition; + viewSpacePosition.xyz /= viewSpacePosition.w; + + return viewSpacePosition.xyz; +} + +vec3 normal_from_depth(float depth, vec2 texcoords) { + + const vec2 offset1 = vec2(0.0,0.0002); + const vec2 offset2 = vec2(0.0002,0.0); + + float depth1 = texture(u_Depth, UV + offset1).r; + float depth2 = texture(u_Depth, UV + offset2).r; + + vec3 p1 = vec3(offset1, depth1 - depth); + vec3 p2 = vec3(offset2, depth2 - depth); + + vec3 normal = cross(p1, p2); + normal.z = -normal.z; + + return normalize(normal); +} + +void main() +{ + float depth = texture(u_Depth, UV).r ; + vec3 fragPos = ViewPosFromDepth(depth); + + vec3 normal = normal_from_depth(depth, UV) ; + vec3 randomVec = texture(u_Noise, UV * u_NoiseScale).xyz; + float radius_depth = u_Radius/depth; + float occlusion = 0.0; + vec3 position = vec3(UV, depth); + const float falloff = 0.0002; + const float area = 0.0075; + for(int i=0; i < 64; i++) + { + vec3 ray = radius_depth * reflect(u_Samples[i], randomVec); + vec3 hemi_ray = position + sign(dot(ray,normal)) * ray; + + float occ_depth = texture(u_Depth, hemi_ray.xy).r; + float difference = depth - occ_depth ; + occlusion += step(falloff, difference) * (1.0 - smoothstep(falloff, u_Bias, difference)); + } + + float ao = 1.0 - 2.0 * occlusion * (1.0 / 64); + + FragColor = vec4(ao, ao, ao , 1.0); +} \ No newline at end of file diff --git a/Editor/src/Windows/EditorInterface.cpp b/Editor/src/Windows/EditorInterface.cpp index 2b6e2147..28e6da6e 100644 --- a/Editor/src/Windows/EditorInterface.cpp +++ b/Editor/src/Windows/EditorInterface.cpp @@ -507,6 +507,41 @@ namespace Nuake { ImGui::PopStyleColor(); } + if (env->SSAOEnabled) + { + ImGui::TableNextColumn(); + { + // Title + ImGui::Text("SSAO Radius"); + ImGui::TableNextColumn(); + + ImGui::DragFloat("##SSAORadius", &env->mSSAO->Radius, 0.01f, 0.0f, 1.0f); + ImGui::TableNextColumn(); + + // Reset button + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, 1, 1, 0)); + std::string resetRadius = ICON_FA_UNDO + std::string("##resetRadius"); + if (ImGui::Button(resetRadius.c_str())) env->mSSAO->Radius = 0.5f; + ImGui::PopStyleColor(); + } + + ImGui::TableNextColumn(); + { + // Title + ImGui::Text("SSAO Bias"); + ImGui::TableNextColumn(); + + ImGui::DragFloat("##SSAOBias", &env->mSSAO->Bias, 0.0001f, 0.0f, 0.5f); + ImGui::TableNextColumn(); + + // Reset button + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1, 1, 1, 0)); + std::string resetBloomThreshold = ICON_FA_UNDO + std::string("##resetSSAOBias"); + if (ImGui::Button(resetBloomThreshold.c_str())) env->mSSAO->Bias = 0.025f; + ImGui::PopStyleColor(); + } + } + if (env->BloomEnabled) { ImGui::TableNextColumn(); diff --git a/Nuake/src/Rendering/PostFX/SSAO.cpp b/Nuake/src/Rendering/PostFX/SSAO.cpp index b568bb35..df53923b 100644 --- a/Nuake/src/Rendering/PostFX/SSAO.cpp +++ b/Nuake/src/Rendering/PostFX/SSAO.cpp @@ -1,5 +1,143 @@ #include "SSAO.h" +#include "src/Core/Core.h" +#include "src/Rendering/Textures/Texture.h" +#include +#include "src/Rendering/Shaders/ShaderManager.h" +#include "src/Rendering/Renderer.h" -namespace Nuake { +#include +#include +#include + +namespace Nuake +{ + SSAO::SSAO() + { + GenerateKernel(); + GenerateNoise(); + + _size = { 1280, 720 }; + + _ssaoFramebuffer = CreateRef(false, Vector2(_size)); + + auto& renderTarget = CreateRef(_size, GL_RGBA, GL_RGBA16F, GL_FLOAT); + _ssaoFramebuffer->SetTexture(renderTarget, GL_COLOR_ATTACHMENT0); + + _ssaoBlurFramebuffer = CreateRef(false, Vector2(_size)); + + auto& renderTargetBlur = CreateRef(_size, GL_RGBA, GL_RGBA16F, GL_FLOAT); + _ssaoBlurFramebuffer->SetTexture(renderTargetBlur); + } + + void SSAO::Resize(const Vector2& size) + { + if (_size == size) + return; + + _size = size; + _ssaoFramebuffer->QueueResize(size); + _ssaoBlurFramebuffer->QueueResize(size); + } + + void SSAO::GenerateKernel() + { + std::uniform_real_distribution randomFloats(0.0, 1.0); // random floats between [0.0, 1.0] + std::default_random_engine generator; + std::vector ssaoKernel; + for (unsigned int i = 0; i < 64; ++i) + { + glm::vec3 sample( + randomFloats(generator) * 2.0 - 1.0, + randomFloats(generator) * 2.0 - 1.0, + randomFloats(generator) + ); + + sample = glm::normalize(sample); + sample *= randomFloats(generator); + + float scale = (float)i / 64.0; + scale = lerp(0.1f, 1.0f, scale * scale); + sample *= scale; + _ssaoKernel.push_back(sample); + } + } + + void SSAO::GenerateNoise() + { + std::uniform_real_distribution randomFloats(0.0, 1.0); + std::default_random_engine generator; + + for (unsigned int i = 0; i < 16; i++) + { + glm::vec3 noise( + randomFloats(generator) * 2.0 - 1.0, + randomFloats(generator) * 2.0 - 1.0, + 0.0f); + _ssaoNoise.push_back(noise); + } + + _ssaoNoiseTexture = CreateRef(Vector2(4, 4), GL_RGB, GL_RGBA16F, GL_FLOAT, &_ssaoNoise[0]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + void SSAO::Draw(FrameBuffer* gBuffer, const Matrix4& projection, const Matrix4& view) + { + _ssaoFramebuffer->Bind(); + { + _ssaoFramebuffer->Clear(); + const auto& depthTexture = gBuffer->GetTexture(GL_DEPTH_ATTACHMENT); + const auto& normalTexture = gBuffer->GetTexture(GL_COLOR_ATTACHMENT1); + Shader* shader = ShaderManager::GetShader("resources/Shaders/ssao.shader"); + shader->Bind(); + shader->SetUniformMat4f("u_Projection", projection); + shader->SetUniformMat4f("u_View", view); + shader->SetUniformTex("u_Depth", depthTexture.get(), 2); + shader->SetUniformTex("u_Normal", normalTexture.get(), 3); + shader->SetUniformTex("u_Noise", _ssaoNoiseTexture.get(), 6); + shader->SetUniform1i("u_KernelSize", 64); + shader->SetUniform1f("u_Radius", Radius); + shader->SetUniform1f("u_Bias", Bias); + shader->SetUniformVec2("u_NoiseScale", Vector2(_size.x / 4, _size.y / 4) ); + + int i = 0; + for (const auto& k : _ssaoKernel) + { + const std::string& uniformName = "u_Samples[" + std::to_string(i) + "]"; + shader->SetUniform3f(uniformName, k.x, k.y, k.z); + i++; + } + + //shader->SetUniform1fv("u_Samples", 64 * 3, (float*)&(_ssaoKernel.begin())); + Renderer::DrawQuad(Matrix4(1.0)); + } + _ssaoFramebuffer->Unbind(); + + _ssaoBlurFramebuffer->Bind(); + { + Shader* shader = ShaderManager::GetShader("resources/Shaders/blur.shader"); + shader->Bind(); + + shader->SetUniformTex("u_Input", _ssaoFramebuffer->GetTexture().get(), 2); + + Renderer::DrawQuad(Matrix4(1.0)); + } + _ssaoBlurFramebuffer->Unbind(); + + /* + if (ImGui::Begin("SSAO debug")) + { + ImGui::Image((void*)(uintptr_t)(_ssaoFramebuffer->GetTexture(GL_COLOR_ATTACHMENT0)->GetID()), ImGui::GetContentRegionAvail(), ImVec2(0, 1), ImVec2(1, 0)); + } + ImGui::End(); + */ + } + + Ref SSAO::GetOuput() const + { + return _ssaoBlurFramebuffer; + } } \ No newline at end of file diff --git a/Nuake/src/Rendering/PostFX/SSAO.h b/Nuake/src/Rendering/PostFX/SSAO.h index 20dc525d..1ece4d8d 100644 --- a/Nuake/src/Rendering/PostFX/SSAO.h +++ b/Nuake/src/Rendering/PostFX/SSAO.h @@ -1,8 +1,38 @@ #pragma once +#include "src/Core/Maths.h" +#include "src/Core/Core.h" +#include "src/Rendering/Buffers/Framebuffer.h" +#include -namespace Nuake { - class SSAO { +namespace Nuake +{ + class SSAO + { + private: + std::vector _ssaoKernel; + std::vector _ssaoNoise; + Ref _ssaoFramebuffer; + Ref _ssaoBlurFramebuffer; + Ref _ssaoNoiseTexture; + + Vector2 _size; + + void GenerateKernel(); + void GenerateNoise(); + float lerp(float a, float b, float f) + { + return a + f * (b - a); + } + + public: + float Radius = 0.07f; + float Bias = 0.003f; + + void Resize(const Vector2& size); + SSAO(); + void Draw(FrameBuffer* gBuffer, const Matrix4& projection, const Matrix4& view); + Ref GetOuput() const; }; } \ No newline at end of file diff --git a/Nuake/src/Rendering/PostFX/Volumetric.cpp b/Nuake/src/Rendering/PostFX/Volumetric.cpp index 620b7681..bd64ea40 100644 --- a/Nuake/src/Rendering/PostFX/Volumetric.cpp +++ b/Nuake/src/Rendering/PostFX/Volumetric.cpp @@ -64,6 +64,7 @@ namespace Nuake { Renderer::DrawQuad(); mFinalFramebuffer->Unbind(); + /* if (ImGui::Begin("Volumetric debug")) { ImGui::Image((void*)(uintptr_t)(mFinalFramebuffer->GetTexture()->GetID()), ImGui::GetContentRegionAvail(), ImVec2(0, 1), ImVec2(1, 0)); @@ -73,6 +74,7 @@ namespace Nuake { { ImGui::Image((void*)(uintptr_t)(mDepth->GetID()), ImGui::GetContentRegionAvail(), ImVec2(0, 1), ImVec2(1, 0)); } + */ ImGui::End(); } } \ No newline at end of file diff --git a/Nuake/src/Rendering/SceneRenderer.cpp b/Nuake/src/Rendering/SceneRenderer.cpp index d5e510a5..a8d5dc2f 100644 --- a/Nuake/src/Rendering/SceneRenderer.cpp +++ b/Nuake/src/Rendering/SceneRenderer.cpp @@ -90,7 +90,10 @@ namespace Nuake { } finalOutput = framebuffer.GetTexture().get(); - + // SSAO + sceneEnv->mSSAO->Resize(framebuffer.GetSize()); + sceneEnv->mSSAO->Draw(mGBuffer.get(), mProjection, mView); + // Copy final output to target framebuffer mToneMapBuffer->QueueResize(framebuffer.GetSize()); mToneMapBuffer->Bind(); @@ -253,6 +256,8 @@ namespace Nuake { shadingShader->SetUniformMat4f("u_View", mView); shadingShader->SetUniformVec3("u_EyePosition", scene.GetCurrentCamera()->Translation); + shadingShader->SetUniformTex("m_SSAO", scene.GetEnvironment()->mSSAO->GetOuput()->GetTexture().get(), 9); + Ref env = scene.GetEnvironment(); auto view = scene.m_Registry.view(); diff --git a/Nuake/src/Rendering/Textures/Texture.cpp b/Nuake/src/Rendering/Textures/Texture.cpp index c2be1aad..3e9c3df0 100644 --- a/Nuake/src/Rendering/Textures/Texture.cpp +++ b/Nuake/src/Rendering/Textures/Texture.cpp @@ -36,7 +36,7 @@ namespace Nuake std::cout << "Error: failed to load texture: " << path << std::endl; } - Texture::Texture(glm::vec2 size, GLenum format, GLenum format2, GLenum format3) + Texture::Texture(glm::vec2 size, GLenum format, GLenum format2, GLenum format3, void* data) { m_RendererId = 0; m_Format = format; @@ -54,7 +54,7 @@ namespace Nuake glGenTextures(1, &m_RendererId); glBindTexture(GL_TEXTURE_2D, m_RendererId); - glTexImage2D(GL_TEXTURE_2D, 0, format2, size.x, size.y, 0, format, format3, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, format2, size.x, size.y, 0, format, format3, data); 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); diff --git a/Nuake/src/Rendering/Textures/Texture.h b/Nuake/src/Rendering/Textures/Texture.h index b6a2ef2a..4510bcac 100644 --- a/Nuake/src/Rendering/Textures/Texture.h +++ b/Nuake/src/Rendering/Textures/Texture.h @@ -27,7 +27,7 @@ namespace Nuake public: Texture(const std::string& path); Texture(glm::vec2 size, msdfgen::BitmapConstRef& bitmap, bool t); - Texture(glm::vec2 size, GLenum format, GLenum format2 = 0, GLenum format3 = 0); + Texture(glm::vec2 size, GLenum format, GLenum format2 = 0, GLenum format3 = 0, void* data = NULL); Texture(Vector2 size, unsigned char* data, int len); ~Texture(); diff --git a/Nuake/src/Scene/Lighting/Environment.cpp b/Nuake/src/Scene/Lighting/Environment.cpp index c89b6e2c..72b68a83 100644 --- a/Nuake/src/Scene/Lighting/Environment.cpp +++ b/Nuake/src/Scene/Lighting/Environment.cpp @@ -10,6 +10,7 @@ namespace Nuake { ProceduralSkybox = CreateRef(); mBloom = CreateScope(4); mVolumetric = CreateScope(); + mSSAO = CreateScope(); } glm::vec4 Environment::GetAmbientColor() diff --git a/Nuake/src/Scene/Lighting/Environment.h b/Nuake/src/Scene/Lighting/Environment.h index 3da7d67a..462619f2 100644 --- a/Nuake/src/Scene/Lighting/Environment.h +++ b/Nuake/src/Scene/Lighting/Environment.h @@ -3,8 +3,10 @@ #include "src/Core/Core.h" #include "src/Scene/Environment/ProceduralSky.h" #include "src/Resource/Serializable.h" + #include "src/Rendering/PostFX/Bloom.h" #include "src/Rendering/PostFX/Volumetric.h" +#include "src/Rendering/PostFX/SSAO.h" #include @@ -26,7 +28,7 @@ namespace Nuake Color AmbientColor = Color(0, 0, 0, 1); Ref ProceduralSkybox; - bool VolumetricEnabled = false; + bool VolumetricEnabled = true; float VolumetricFog = 0.90f; float VolumetricStepCount = 50.f; @@ -37,6 +39,9 @@ namespace Nuake Scope mBloom; Scope mVolumetric; + bool SSAOEnabled = true; + Scope mSSAO; + Vector3 ClearColor; glm::vec4 m_AmbientColor;