From 39d2bb68f8b23660d559ff981030af545923a637 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Thu, 5 Apr 2018 12:39:30 +0200 Subject: [PATCH] SSAO getting close (excited) --- data/shaders/fs_depthbuffer.glsl | 4 +- data/shaders/fs_simpletexture.glsl | 4 +- data/shaders/fs_ssao.glsl | 45 ++++++++++ data/shaders/vs_default.glsl | 5 +- data/shaders/vs_passthrough.glsl | 4 +- src/Utils.h | 5 +- src/modules/RenderModule.cc | 136 ++++++++++++++++++++++++++++- src/modules/RenderModule.h | 11 +++ src/modules/RenderUtils.h | 5 ++ 9 files changed, 205 insertions(+), 14 deletions(-) create mode 100644 data/shaders/fs_ssao.glsl diff --git a/data/shaders/fs_depthbuffer.glsl b/data/shaders/fs_depthbuffer.glsl index 2f67730..8ab6767 100644 --- a/data/shaders/fs_depthbuffer.glsl +++ b/data/shaders/fs_depthbuffer.glsl @@ -1,6 +1,6 @@ #version 150 core -in vec2 ioUV; +in vec2 ioFragTexCoords; out vec3 outColor; @@ -10,7 +10,7 @@ uniform float uNear; uniform float uFar; void main() { - float z = texture(uDepthTexture, ioUV).r; + float z = texture(uDepthTexture, ioFragTexCoords).r; float c; if (uIsOrthographic == 1.0) { c = z; diff --git a/data/shaders/fs_simpletexture.glsl b/data/shaders/fs_simpletexture.glsl index d6a2915..a7e638f 100644 --- a/data/shaders/fs_simpletexture.glsl +++ b/data/shaders/fs_simpletexture.glsl @@ -1,11 +1,11 @@ #version 150 core -in vec2 ioUV; +in vec2 ioFragTexCoords; out vec4 outColor; uniform sampler2D uTexture; void main() { - outColor = texture(uTexture, ioUV); + outColor = texture(uTexture, ioFragTexCoords); } diff --git a/data/shaders/fs_ssao.glsl b/data/shaders/fs_ssao.glsl new file mode 100644 index 0000000..ef16ccf --- /dev/null +++ b/data/shaders/fs_ssao.glsl @@ -0,0 +1,45 @@ +#version 150 core + +in vec2 ioFragTexCoords; + +out vec3 outColor; + +uniform sampler2D uPositions; +uniform sampler2D uNormals; +uniform sampler2D uNoise; + +uniform float uRadius; +uniform float uBias; +uniform int uSampleCount; +uniform vec3 uSamples[64]; +uniform mat4 uProjection; + +void main() { + vec2 noise_scale = textureSize(uPositions, 0) / 2.0; + + vec3 position = texture(uPositions, ioFragTexCoords).xyz; + vec3 normal = normalize(texture(uNormals, ioFragTexCoords)).rgb; + vec3 random_vector = normalize(texture(uNoise, ioFragTexCoords * noise_scale).xyz); + + vec3 tangent = normalize(random_vector - normal * dot(random_vector, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 TBN = mat3 (tangent, bitangent, normal); + + float occlusion = 0.0; + for (int i = 0; i < uSampleCount; ++i) { + vec3 sample = TBN * uSamples[i]; + sample = position + sample * uRadius; + + vec4 offset = vec4(sample, 1.0); + offset = uProjection * offset; + offset.xyz /= offset.w; + offset.xyz = offset.xyz * 0.5 + 0.5; + float sample_depth = texture(uPositions, offset.xy).z; + + float range_check = smoothstep(0.0, 1.0, uRadius / abs(position.z - sample_depth)); + occlusion += (sample_depth >= sample.z + uBias ? 1.0 : 0.0) * range_check; + } + + occlusion = 1.0 - (occlusion / uSampleCount); + outColor = vec3(occlusion); +} diff --git a/data/shaders/vs_default.glsl b/data/shaders/vs_default.glsl index ad54b26..4434d9c 100644 --- a/data/shaders/vs_default.glsl +++ b/data/shaders/vs_default.glsl @@ -1,5 +1,4 @@ #version 150 core -#extension GL_ARB_explicit_attrib_location : require in vec4 inCoord; in vec3 inNormal; @@ -21,11 +20,11 @@ smooth out vec4 ioFragColor; out vec4 ioFragPosLightSpace; void main() { - ioFragPosition = (uModelMatrix * inCoord).xyz; + ioFragPosition = (uViewMatrix * uModelMatrix * inCoord).xyz; ioFragNormal = transpose(inverse(mat3(uModelMatrix))) * inNormal; ioFragTexCoords = inUV; ioFragColor = inColor; - ioFragPosLightSpace = uLightSpaceMatrix * vec4(ioFragPosition, 1.0); + ioFragPosLightSpace = uLightSpaceMatrix * uModelMatrix * inCoord; gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * inCoord; } diff --git a/data/shaders/vs_passthrough.glsl b/data/shaders/vs_passthrough.glsl index 4fc7165..24ae372 100644 --- a/data/shaders/vs_passthrough.glsl +++ b/data/shaders/vs_passthrough.glsl @@ -3,9 +3,9 @@ in vec4 inCoord; in vec2 inUV; -out vec2 ioUV; +out vec2 ioFragTexCoords; void main() { - ioUV = inUV; + ioFragTexCoords = inUV; gl_Position = inCoord; } diff --git a/src/Utils.h b/src/Utils.h index d254566..3aff290 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -78,5 +78,8 @@ inline void gLog (const char* format, ...) { fprintf(gLogFile, "%s\n", buffer); fflush(gLogFile); - +} + +inline float gRandomFloat() { + return (static_cast(rand()) / static_cast(RAND_MAX)); } diff --git a/src/modules/RenderModule.cc b/src/modules/RenderModule.cc index caee3bf..7c549ad 100644 --- a/src/modules/RenderModule.cc +++ b/src/modules/RenderModule.cc @@ -17,6 +17,7 @@ float moving_factor = 1.0f; struct RendererSettings { bool DrawDepth = false; bool DrawLightDepth = false; + bool DrawSSAO = false; }; static RendererSettings sRendererSettings; @@ -87,8 +88,9 @@ static void module_serialize ( Serializer* serializer) { SerializeBool(*serializer, "protot.RenderModule.DrawDepth", sRendererSettings.DrawDepth); SerializeBool(*serializer, "protot.RenderModule.DrawLightDepth", sRendererSettings.DrawLightDepth); - SerializeBool(*serializer, "protot.RenderModule.mIsSSAOEnabled", gRenderer->mIsSSAOEnabled); + SerializeBool(*serializer, "protot.RenderModule.DrawSSAO", sRendererSettings.DrawSSAO); + SerializeBool(*serializer, "protot.RenderModule.mIsSSAOEnabled", gRenderer->mIsSSAOEnabled); SerializeBool(*serializer, "protot.RenderModule.Camera.mIsOrthographic", gRenderer->mCamera.mIsOrthographic); SerializeFloat(*serializer, "protot.RenderModule.Camera.mFov", gRenderer->mCamera.mFov); SerializeVec3(*serializer, "protot.RenderModule.Camera.mEye", gRenderer->mCamera.mEye); @@ -163,8 +165,6 @@ const struct module_api MODULE_API = { }; } - - void Light::Initialize() { gLog("Initializing light"); mShadowMapProgram = RenderProgram("data/shaders/vs_shadowmap.vert", "data/shaders/fs_shadowmap.frag"); @@ -374,6 +374,14 @@ void Renderer::Initialize(int width, int height) { mRenderQuadProgramDepth.RegisterFileModification(); assert(load_result); + // Program for SSAO + mSSAOProgram = RenderProgram("data/shaders/vs_passthrough.glsl", "data/shaders/fs_ssao.glsl"); + load_result = mSSAOProgram.Load(); + mSSAOProgram.RegisterFileModification(); + assert(load_result); + + InitializeSSAOKernelAndNoise(); + // Render Target gLog("Initializing main render target size: %d,%d", width, height); int render_target_flags = RenderTarget::EnableColor @@ -392,6 +400,9 @@ void Renderer::Initialize(int width, int height) { mRenderTarget.mLinearizeDepthProgram = mRenderQuadProgramDepth; mRenderTarget.mLinearizeDepthProgram.RegisterFileModification(); + // SSAO Target + mSSAOTarget.Initialize(width, height, RenderTarget::EnableColor); + // Light mLight.Initialize(); mLight.mShadowMapTarget.mVertexArray = &gVertexArray; @@ -406,6 +417,9 @@ void Renderer::Initialize(int width, int height) { } void Renderer::Shutdown() { + if (mSSAONoiseTexture != -1) { + glDeleteTextures(1, &mSSAONoiseTexture); + } } @@ -428,6 +442,9 @@ void Renderer::RenderGl() { || mSceneAreaHeight != mRenderTarget.mHeight || mRenderTarget.mFlags != required_render_flags ) { mRenderTarget.Resize(mSceneAreaWidth, mSceneAreaHeight, required_render_flags); + if (mIsSSAOEnabled) { + mSSAOTarget.Resize(mSceneAreaWidth, mSceneAreaHeight, RenderTarget::EnableColor); + } } if (mCamera.mWidth != mSceneAreaWidth @@ -522,6 +539,46 @@ void Renderer::RenderGl() { if (mSettings->DrawDepth) { mRenderTarget.RenderToLinearizedDepth(mCamera.mNear, mCamera.mFar, mCamera.mIsOrthographic); } + + if (mIsSSAOEnabled) { + mSSAOTarget.Bind(); + glViewport(0, 0, mCamera.mWidth, mCamera.mHeight); + GLenum draw_attachment_0[] = {GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, draw_attachment_0); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + + // Positions + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mRenderTarget.mPositionTexture); + + // Normals + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mRenderTarget.mNormalTexture); + + // TODO: noise texture + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mSSAONoiseTexture); + + glUseProgram(mSSAOProgram.mProgramId); + + mSSAOProgram.SetInt("uPositions", 0); + mSSAOProgram.SetInt("uNormals", 1); + mSSAOProgram.SetInt("uNoise", 2); + + mSSAOProgram.SetFloat("uRadius", mSSAORadius); + mSSAOProgram.SetFloat("uBias", mSSAOBias); + mSSAOProgram.SetInt("uSampleCount", mSSAOKernel.size()); + mSSAOProgram.SetMat44("uProjection", mCamera.mProjectionMatrix); + + mSSAOProgram.SetVec3Array("uSamples", mSSAOKernel.size(), &mSSAOKernel[0][0]); +// for (int i = 0; i < mSSAOKernel.size(); ++i) { +// mSSAOProgram.SetVec3(std::string("uSamples[" + std::to_string(i) + "]").c_str(), mSSAOKernel[i]); +// } + + gVertexArray.Bind(); + gScreenQuad.Draw(GL_TRIANGLES); + } } void Renderer::RenderScene(RenderProgram &program, const Camera& camera) { @@ -593,10 +650,33 @@ void Renderer::RenderScene(RenderProgram &program, const Camera& camera) { void Renderer::DrawGui() { if (ImGui::BeginDock("Scene")) { - ImGui::Checkbox("Draw Depth", &mSettings->DrawDepth); + static int e = 0; + if (mSettings->DrawDepth) { + e = 1; + } else if (mSettings->DrawSSAO) { + e = 2; + } + + ImGui::RadioButton("Default", &e, 0); ImGui::SameLine(); + ImGui::RadioButton("Depth", &e, 1); ImGui::SameLine(); + ImGui::RadioButton("SSAO", &e, 2); + + switch (e) { + case 0: mSettings->DrawDepth = 0; + mSettings->DrawSSAO = 0; + break; + case 1: mSettings->DrawDepth = 1; + mSettings->DrawSSAO = 0; + break; + case 2: mSettings->DrawDepth = 0; + mSettings->DrawSSAO = 1; + break; + } if (mSettings->DrawDepth) { mRenderTextureRef.mTextureIdPtr = &mRenderTarget.mLinearizedDepthTexture; + } else if (mSettings->DrawSSAO) { + mRenderTextureRef.mTextureIdPtr = &mSSAOTarget.mColorTexture; } else { mRenderTextureRef.mTextureIdPtr = &mRenderTarget.mColorTexture; } @@ -628,6 +708,14 @@ void Renderer::DrawGui() { ImGui::Checkbox("Enable SSAO", &mIsSSAOEnabled); if (mIsSSAOEnabled) { + ImGui::SliderFloat("Radius", &mSSAORadius, 0.0f, 1.0f); + ImGui::SliderFloat("Bias", &mSSAOBias, 0.0f, 0.1f); + ImGui::SliderInt("Samples", &mSSAOKernelSize, 1, 64); + + if (mSSAOKernelSize != mSSAOKernel.size()) { + InitializeSSAOKernelAndNoise(); + } + ImGui::Text("Position Texture"); mPositionTextureRef.mTextureIdPtr = &mRenderTarget.mPositionTexture; mPositionTextureRef.magic = (GLuint)0xbadface; @@ -657,3 +745,43 @@ void Renderer::DrawGui() { ImGui::EndDock(); } +void Renderer::InitializeSSAOKernelAndNoise() { + mSSAOKernel.clear(); + + for (unsigned int i = 0; i < mSSAOKernelSize; ++i) { + Vector3f sample( + gRandomFloat() * 2.0f - 1.0f, + gRandomFloat() * 2.0f - 1.0f, + gRandomFloat() + ); + sample.normalize(); + + // Have a higher distribution of samples close to the origin + float scale = (float) i / mSSAOKernelSize; + scale = 0.1 + scale * scale * (1.0f - 0.1); + + mSSAOKernel.push_back(sample * scale); + } + + std::vector noise_vectors; + for (unsigned int i = 0; i < 16; ++i) { + Vector3f noise( + gRandomFloat() * 2.0f - 1.0f, + gRandomFloat() * 2.0f - 1.0f, + 0.0f); + noise_vectors.push_back(noise); + } + + if (mSSAONoiseTexture != -1) { + glDeleteTextures(1, &mSSAONoiseTexture); + } + + glGenTextures(1, &mSSAONoiseTexture); + glBindTexture(GL_TEXTURE_2D, mSSAONoiseTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &noise_vectors[0][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); +} + diff --git a/src/modules/RenderModule.h b/src/modules/RenderModule.h index 29d9f5f..3dce07b 100644 --- a/src/modules/RenderModule.h +++ b/src/modules/RenderModule.h @@ -71,12 +71,21 @@ struct Renderer { RenderProgram mDefaultProgram; RenderProgram mRenderQuadProgramColor; RenderProgram mRenderQuadProgramDepth; + RenderProgram mSSAOProgram; RenderTarget mRenderTarget; + RenderTarget mSSAOTarget; + GLTextureRef mRenderTextureRef = { (int)0xbadface }; GLTextureRef mPositionTextureRef = { (int)0xbadface }; GLTextureRef mNormalTextureRef = { (int)0xbadface }; + float mSSAORadius = 0.5f; + float mSSAOBias = 0.025f; + int mSSAOKernelSize = 64; + std::vector mSSAOKernel; + GLuint mSSAONoiseTexture = -1; + Renderer() : mInitialized(false), mWidth (0), @@ -88,4 +97,6 @@ struct Renderer { void RenderGl(); void RenderScene(RenderProgram &program, const Camera& camera); void DrawGui(); + + void InitializeSSAOKernelAndNoise(); }; diff --git a/src/modules/RenderUtils.h b/src/modules/RenderUtils.h index d56b6da..6725038 100644 --- a/src/modules/RenderUtils.h +++ b/src/modules/RenderUtils.h @@ -218,6 +218,11 @@ struct RenderProgram : AFileModificationListener { glUniform3fv(location, 1, vec.data()); return location; } + GLint SetVec3Array(const char* name, int count, const float* array) { + GLint location = glGetUniformLocation(mProgramId, name); + glUniform3fv(location, count, array); + return location; + } GLint SetVec4(const char* name, const Vector3f& vec, float w = 1.0f) { GLint location = glGetUniformLocation(mProgramId, name); glUniform4f(location, vec[0], vec[1], vec[2], w);