From e9ba7f77ccd124337ca222819d39ec7d4d2526a9 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Sat, 7 Jul 2018 23:09:34 +0200 Subject: [PATCH] Implemented Cascaded Shadow Maps --- data/shaders/fs_deferred_lighting.glsl | 78 +++++++------ src/modules/RenderModule.cc | 150 ++++++++++++++----------- src/modules/RenderModule.h | 17 ++- src/modules/RenderUtils.h | 12 +- 4 files changed, 142 insertions(+), 115 deletions(-) diff --git a/data/shaders/fs_deferred_lighting.glsl b/data/shaders/fs_deferred_lighting.glsl index 702b76f..4171034 100644 --- a/data/shaders/fs_deferred_lighting.glsl +++ b/data/shaders/fs_deferred_lighting.glsl @@ -1,30 +1,38 @@ #version 150 core -uniform sampler2D uColor; -uniform sampler2D uNormal; +uniform sampler2D uPosition; uniform sampler2D uDepth; +uniform sampler2D uNormal; +uniform sampler2D uColor; uniform sampler2D uAmbientOcclusion; +const int NUM_SPLITS = 3; + #define USE_SAMPLER2D_SHADOW 1 #ifdef USE_SAMPLER2D_SHADOW -uniform sampler2DShadow uShadowMap; +uniform sampler2DShadow uShadowMap[NUM_SPLITS]; #else -uniform sampler2D uShadowMap; +uniform sampler2D uShadowMaps[NUM_SPLITS]; #endif -uniform sampler2D uPosition; +uniform mat4 uViewToLightMatrix[NUM_SPLITS]; +uniform float uNear; +uniform float uFar; +uniform vec4 uShadowSplits; uniform vec3 uLightDirection; -uniform mat4 uLightSpaceMatrix; -uniform mat4 uViewToLightSpaceMatrix; uniform float uShadowBias; in vec2 ioFragTexCoords; out vec3 outColor; -float ShadowCalculationPCF(vec4 frag_pos_light_space, vec3 frag_normal_light_space) { +#ifdef USE_SAMPLER2D_SHADOW +float ShadowCalculationPCF(sampler2DShadow shadow_map, vec4 frag_pos_light_space, vec3 frag_normal_light_space) { +#else +float ShadowCalculationPCF(sampler2D shadow_map, vec4 frag_pos_light_space, vec3 frag_normal_light_space) { +#endif vec3 projected_coordinates = frag_pos_light_space.xyz / frag_pos_light_space.w; projected_coordinates = projected_coordinates * 0.5 + 0.5; @@ -38,14 +46,14 @@ float ShadowCalculationPCF(vec4 frag_pos_light_space, vec3 frag_normal_light_spa bias = max(0.001 * (1.0 - dot(frag_normal_light_space, uLightDirection)), uShadowBias); float shadow = 0.0; - vec2 texel_size = 1.0 / textureSize(uShadowMap, 0); + vec2 texel_size = 1.0 / textureSize(shadow_map, 0); for (int x = -1; x <= 1; ++x) { for (int y = -1; y <= 1; ++y) { #ifdef USE_SAMPLER2D_SHADOW vec2 coordinate = projected_coordinates.xy + vec2(x, y) * texel_size; - float pcf_depth = texture(uShadowMap, vec3(coordinate, current_depth - bias)); + float pcf_depth = texture(shadow_map, vec3(coordinate, current_depth - bias)); #else - float pcf_depth = texture(uShadowMap, projected_coordinates.xy).r; + float pcf_depth = texture(shadow_map, projected_coordinates.xy).r; #endif shadow += current_depth - bias > pcf_depth ? 1.0 : 0.0; } @@ -56,28 +64,10 @@ float ShadowCalculationPCF(vec4 frag_pos_light_space, vec3 frag_normal_light_spa return shadow; } -float ShadowCalculation(vec4 frag_pos_light_space, vec3 frag_normal_light_space) { - vec3 projected_coordinates = frag_pos_light_space.xyz / frag_pos_light_space.w; - projected_coordinates = projected_coordinates * 0.5 + 0.5; - - float current_depth = projected_coordinates.z; - - float bias = 0.01; - -#ifdef USE_SAMPLER2D_SHADOW - float closest_depth = texture(uShadowMap, vec3(projected_coordinates.xy, current_depth - bias)); -#else - float closest_depth = texture(uShadowMap, projected_coordinates.xy).r; - bias = max(0.005 * (1.0 - dot(frag_normal_light_space, uLightDirection)), 0.003); -#endif - - return current_depth - bias > closest_depth ? 1.0 : 0.0; -} - vec3 get_cascade_color (float depth) { - if (depth < 0.97) { + if (depth < uShadowSplits[1]) { return vec3 (1.0, 0.0, 0.0); - } else if (depth < 0.99) { + } else if (depth < uShadowSplits[2]) { return vec3 (0.0, 1.0, 0.0); } @@ -108,13 +98,27 @@ void main() { float spec = pow(max(dot(normal, halfway_dir), 0.0), 32); vec3 specular = spec * vec3(0.5); - // shadow (need to transform position and normal to light space) - vec4 position_light_space = uViewToLightSpaceMatrix * vec4(position, 1.0); - vec3 normal_light_space = (transpose(inverse(uViewToLightSpaceMatrix)) * vec4(normal, 1.0)).xyz; - float shadow = ShadowCalculationPCF(position_light_space, normal); - -// vec3 cascade = get_cascade_color(depth); + float shadow = 0; + float normalized_depth = (depth - uNear) / (uFar - uNear); + + if (-position.z < uShadowSplits[1]) { + // shadow (need to transform position and normal to light space) + vec4 position_light_space = uViewToLightMatrix[0] * vec4(position, 1.0); + vec3 normal_light_space = (transpose(inverse(uViewToLightMatrix[0])) * vec4(normal, 1.0)).xyz; + shadow = ShadowCalculationPCF(uShadowMap[0], position_light_space, normal); + } else if (-position.z< uShadowSplits[2]) { + vec4 position_light_space = uViewToLightMatrix[1] * vec4(position, 1.0); + vec3 normal_light_space = (transpose(inverse(uViewToLightMatrix[1])) * vec4(normal, 1.0)).xyz; + shadow = ShadowCalculationPCF(uShadowMap[1], position_light_space, normal); + } else { + vec4 position_light_space = uViewToLightMatrix[2] * vec4(position, 1.0); + vec3 normal_light_space = (transpose(inverse(uViewToLightMatrix[2])) * vec4(normal, 1.0)).xyz; + shadow = ShadowCalculationPCF(uShadowMap[2], position_light_space, normal); + } + +// vec3 cascade = get_cascade_color(-position.z); // ambient = cascade; outColor = (ambient + (1.0 - shadow) * (diffuse + specular)) * ambient_occlusion; +// outColor = diffuse; // outColor = (ambient + (diffuse + specular)) * ambient_occlusion; } diff --git a/src/modules/RenderModule.cc b/src/modules/RenderModule.cc index e217c0f..c4217f9 100644 --- a/src/modules/RenderModule.cc +++ b/src/modules/RenderModule.cc @@ -210,12 +210,12 @@ void Light::Initialize() { | RenderTarget::EnableLinearizedDepthTexture); for (int i = 0; i < cNumSplits; ++i) { - mSplits[i].mShadowMapTarget.Initialize(mShadowMapSize, mShadowMapSize, + mSplitTarget[i].Initialize(mShadowMapSize, mShadowMapSize, RenderTarget::EnableColor | RenderTarget::EnableDepthTexture | RenderTarget::EnableLinearizedDepthTexture); - mSplits[i].mShadowMapTarget.mVertexArray = &gVertexArray; - mSplits[i].mShadowMapTarget.mQuadMesh = &gScreenQuad; + mSplitTarget[i].mVertexArray = &gVertexArray; + mSplitTarget[i].mQuadMesh = &gScreenQuad; } } @@ -257,7 +257,7 @@ void Light::DrawGui() { if (sRendererSettings.ActiveShadowMapSplit == 3) { shadow_split_target = &mShadowMapTarget; } else { - shadow_split_target = &mSplits[sRendererSettings.ActiveShadowMapSplit].mShadowMapTarget; + shadow_split_target = &mSplitTarget[sRendererSettings.ActiveShadowMapSplit]; } void* texture; @@ -295,14 +295,16 @@ void Light::UpdateSplits(const Camera& camera) { Matrix44f light_matrix = LookAt (mPosition, mPosition + mDirection, Vector3f (0.f, 1.0f, 0.0f)); Matrix44f light_matrix_inv = light_matrix.inverse(); + mShadowSplits[0] = near; + mShadowSplits[1] = near + length * 0.02; + mShadowSplits[2] = near + length * 0.2; + mShadowSplits[3] = far; + for (int i = 0; i < cNumSplits; ++i) { - ShadowSplitInfo &split = mSplits[i]; + split_near = mShadowSplits[i]; + float split_far = mShadowSplits[i + 1]; - split_near = near + mShadowSplits[i] * length; - float split_far = near + mShadowSplits[i + 1] * length; - - split.mViewFrustum = - look_at * Perspective (camera.mFov, aspect, split_near, split_far); + mSplitViewFrustum[i] = look_at * Perspective (camera.mFov, aspect, split_near, split_far); float xn = split_near * tan_half_vfov; float xf = split_far * tan_half_vfov; @@ -338,8 +340,8 @@ void Light::UpdateSplits(const Camera& camera) { bbox_light.Update(v_light.block<3,1>(0,0)); } - split.mBoundsWorld = bbox_world; - split.mBoundsLight = bbox_light; + mSplitBoundsWorldSpace[i] = bbox_world; + mSplitBoundsLightSpace[i] = bbox_light; // gLog ("min/max %.3f,%.3f, %.3f,%.3f, %.3f,%.3f", // min_x, max_x, @@ -352,11 +354,11 @@ void Light::UpdateSplits(const Camera& camera) { Vector3f dimensions = (bbox_light.mMax - bbox_light.mMin) * 0.5f; Vector3f center = bbox_light.mMin + dimensions; - split.mCamera.mIsOrthographic = true; - split.mCamera.mViewMatrix = light_matrix; + mSplitCamera[i].mIsOrthographic = true; + mSplitCamera[i].mViewMatrix = light_matrix; // TODO: values for near and far planes are off - split.mCamera.mProjectionMatrix = Ortho ( + mSplitCamera[i].mProjectionMatrix = Ortho ( bbox_light.mMin[0], bbox_light.mMax[0], bbox_light.mMin[1], bbox_light.mMax[1], // mLight.mNear, bbox_light.mMax[2] @@ -365,9 +367,8 @@ void Light::UpdateSplits(const Camera& camera) { // bbox_light.mMin[2], bbox_light.mMax[2] ); - split.mFrustum = - split.mCamera.mViewMatrix - * split.mCamera.mProjectionMatrix; + mSplitLightFrustum[i] = mSplitCamera[i].mViewMatrix + * mSplitCamera[i].mProjectionMatrix; } } } @@ -643,8 +644,8 @@ void Renderer::Initialize(int width, int height) { mLight.mShadowMapTarget.mLinearizeDepthProgram.RegisterFileModification(); for (int i = 0; i < cNumSplits; ++i) { - mLight.mSplits[i].mShadowMapTarget.mLinearizeDepthProgram = mRenderQuadProgramDepth; - mLight.mSplits[i].mShadowMapTarget.mLinearizeDepthProgram.RegisterFileModification(); + mLight.mSplitTarget[i].mLinearizeDepthProgram = mRenderQuadProgramDepth; + mLight.mSplitTarget[i].mLinearizeDepthProgram.RegisterFileModification(); } // Model @@ -726,24 +727,24 @@ void Renderer::RenderGl() { mCamera.UpdateMatrices(); mLight.UpdateMatrices(); - mLight.UpdateSplits(mDebugCamera); + mLight.UpdateSplits(mCamera); // Cascaded Shadow Maps for (int i = 0; i < cNumSplits; i++) { - ShadowSplitInfo &split = mLight.mSplits[i]; - split.mShadowMapTarget.Bind(); + + mLight.mSplitTarget[i].Bind(); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glUseProgram(mLight.mShadowMapProgram.mProgramId); glViewport(0, 0, mLight.mShadowMapSize, mLight.mShadowMapSize); - if (mLight.mShadowMapProgram.SetMat44("uViewProjectionMatrix", split.mFrustum) == -1) { + if (mLight.mShadowMapProgram.SetMat44("uViewProjectionMatrix", mLight.mSplitLightFrustum[i]) == -1) { gLog ("Warning: Uniform %s not found!", "uViewProjectionMatrix"); } - RenderScene(mLight.mShadowMapProgram, split.mCamera); - split.mShadowMapTarget.RenderToLinearizedDepth(mLight.mCamera.mNear, mLight.mCamera.mFar, true); + RenderScene(mLight.mShadowMapProgram, mLight.mSplitCamera[i]); + mLight.mSplitTarget[i].RenderToLinearizedDepth(mLight.mCamera.mNear, mLight.mCamera.mFar, true); // glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -766,8 +767,6 @@ void Renderer::RenderGl() { glViewport(0, 0, mCamera.mWidth, mCamera.mHeight); - -// glBindFramebuffer(GL_FRAMEBUFFER, 0); Matrix44f model_matrix = TranslateMat44(0.0f, 0.0f, 0.0f); Matrix44f model_view_projection = model_matrix @@ -901,34 +900,54 @@ void Renderer::RenderGl() { glClear(GL_COLOR_BUFFER_BIT); glUseProgram(mDeferredLighting.mProgramId); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, mRenderTarget.mColorTexture); - mDeferredLighting.SetInt("uColorTexture", 0); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, mRenderTarget.mNormalTexture); - mDeferredLighting.SetInt("uNormal", 1); - - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, mRenderTarget.mDepthTexture); - mDeferredLighting.SetInt("uDepth", 2); - - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, mLight.mShadowMapTarget.mDepthTexture); - mDeferredLighting.SetInt("uShadowMap", 3); - - mDeferredLighting.SetMat44("uLightSpaceMatrix", mLight.mLightSpaceMatrix); - Matrix44f view_to_light_matrix = mCamera.mViewMatrix.inverse() * mLight.mLightSpaceMatrix; - mDeferredLighting.SetMat44("uViewToLightSpaceMatrix", view_to_light_matrix); // TODO: remove and reconstruct position from depth - glActiveTexture(GL_TEXTURE4); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mRenderTarget.mPositionTexture); - mDeferredLighting.SetInt("uPosition", 4); + mDeferredLighting.SetInt("uPosition", 0); - glActiveTexture(GL_TEXTURE5); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, mRenderTarget.mDepthTexture); + mDeferredLighting.SetInt("uDepth", 1); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, mRenderTarget.mNormalTexture); + mDeferredLighting.SetInt("uNormal", 2); + + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, mRenderTarget.mColorTexture); + mDeferredLighting.SetInt("uColor", 3); + + glActiveTexture(GL_TEXTURE4); glBindTexture(GL_TEXTURE_2D, mSSAOBlurTarget.mColorTexture); - mDeferredLighting.SetInt("uAmbientOcclusion", 5); + mDeferredLighting.SetInt("uAmbientOcclusion", 4); + + // Shadow Map Cascades + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_2D, mLight.mSplitTarget[0].mDepthTexture); + glActiveTexture(GL_TEXTURE6); + glBindTexture(GL_TEXTURE_2D, mLight.mSplitTarget[1].mDepthTexture); + glActiveTexture(GL_TEXTURE7); + glBindTexture(GL_TEXTURE_2D, mLight.mSplitTarget[2].mDepthTexture); + + GLint shadow_maps[3]; + shadow_maps[0] = 5; + shadow_maps[1] = 6; + shadow_maps[2] = 7; + mDeferredLighting.SetIntArray("uShadowMap", 3, shadow_maps); + + Matrix44f light_matrices[3]; + + for (int i = 0; i < cNumSplits; ++i) { + light_matrices[i] = mCamera.mViewMatrix.inverse() + * mLight.mSplitLightFrustum[i]; + } + + mDeferredLighting.SetMat44Array("uViewToLightMatrix", 3, light_matrices); + + mDeferredLighting.SetFloat("uNear", mCamera.mNear); + mDeferredLighting.SetFloat("uFar", mCamera.mFar); + mDeferredLighting.SetVec4("uShadowSplits", mLight.mShadowSplits); mDeferredLighting.SetMat44("uViewMatrix", mCamera.mViewMatrix); Matrix33f view_mat_rot = mCamera.mViewMatrix.block<3,3>(0,0); @@ -963,14 +982,13 @@ void Renderer::DebugDrawShadowCascades() { glUseProgram(mSimpleProgram.mProgramId); for (int i = 0; i < cNumSplits; ++i) { - const ShadowSplitInfo& split = mLight.mSplits[i]; - const BBox& bbox_light = split.mBoundsLight; - const BBox& bbox_world = split.mBoundsWorld; + const BBox& bbox_light = mLight.mSplitBoundsLightSpace[i]; + const BBox& bbox_world = mLight.mSplitBoundsWorldSpace[i]; // Draw view split frustum if (mLight.mDebugDrawSplitViewBounds) { - Matrix44f model_view_projection = - split.mViewFrustum.inverse() + Matrix44f model_view_projection = + mLight.mSplitViewFrustum[i].inverse() * mCamera.mViewMatrix * mCamera.mProjectionMatrix; @@ -999,7 +1017,7 @@ void Renderer::DebugDrawShadowCascades() { // Draw bounding boxes in light space if (mLight.mDebugDrawSplitLightBounds) { Matrix44f model_view_projection = - mLight.mSplits[i].mFrustum.inverse() + mLight.mSplitLightFrustum[i].inverse() * mCamera.mViewMatrix * mCamera.mProjectionMatrix; @@ -1007,19 +1025,18 @@ void Renderer::DebugDrawShadowCascades() { mSimpleProgram.SetVec4("uColor", Vector4f (0.0f, 0.0f, 1.0f, 1.0f)); gUnitCubeLines.Draw(GL_LINES); } - } // // Draw bounding box in light space // if (mLight.mDebugDrawSplitLightBounds) { - Matrix44f model_view_projection = - mLight.mLightSpaceMatrix.inverse() - * mCamera.mViewMatrix - * mCamera.mProjectionMatrix; - - mSimpleProgram.SetMat44("uModelViewProj", model_view_projection); - mSimpleProgram.SetVec4("uColor", Vector4f (0.0f, 1.0f, 1.0f, 1.0f)); - gUnitCubeLines.Draw(GL_LINES); +// Matrix44f model_view_projection = +// mLight.mLightSpaceMatrix.inverse() +// * mCamera.mViewMatrix +// * mCamera.mProjectionMatrix; +// +// mSimpleProgram.SetMat44("uModelViewProj", model_view_projection); +// mSimpleProgram.SetVec4("uColor", Vector4f (0.0f, 1.0f, 1.0f, 1.0f)); +// gUnitCubeLines.Draw(GL_LINES); // } // Disable wireframe @@ -1043,7 +1060,6 @@ void Renderer::RenderScene(RenderProgram &program, const Camera& camera) { program.SetMat33("uNormalMatrix", normal_matrix); program.SetVec4("uColor", Vector4f (1.0f, 1.0f, 1.0f, 1.0f)); -// program.SetVec3("uLightDirection", light_dir.normalize()); program.SetVec3("uLightDirection", mLight.mDirection); program.SetVec3("uViewPosition", camera.mEye); diff --git a/src/modules/RenderModule.h b/src/modules/RenderModule.h index ae68fed..a4c881e 100644 --- a/src/modules/RenderModule.h +++ b/src/modules/RenderModule.h @@ -14,15 +14,6 @@ #include "Globals.h" #include "RenderUtils.h" -struct ShadowSplitInfo { - BBox mBoundsLight; - BBox mBoundsWorld; - Matrix44f mViewFrustum; - Matrix44f mFrustum; - RenderTarget mShadowMapTarget; - Camera mCamera; -}; - struct Light { Vector3f mPosition; Vector3f mDirection; @@ -47,7 +38,13 @@ struct Light { Matrix44f mLightSpaceMatrix; Vector4f mShadowSplits = Vector4f (0.0, 0.1, 0.4, 1.0); - ShadowSplitInfo mSplits[4]; + + Matrix44f mSplitViewFrustum[3]; + Matrix44f mSplitLightFrustum[3]; + RenderTarget mSplitTarget[3]; + BBox mSplitBoundsLightSpace[3]; + BBox mSplitBoundsWorldSpace[3]; + Camera mSplitCamera[3]; Light() : mPosition (Vector3f(0.f, 3, 0.0f)), diff --git a/src/modules/RenderUtils.h b/src/modules/RenderUtils.h index 2138704..5592f52 100644 --- a/src/modules/RenderUtils.h +++ b/src/modules/RenderUtils.h @@ -232,6 +232,11 @@ struct RenderProgram : AFileModificationListener { glUniform1i(location, val); return location; } + GLint SetIntArray(const char* name, const int count, const int* array) { + GLint location = glGetUniformLocation(mProgramId, name); + glUniform1iv(location, count, array); + return location; + } GLint SetFloat(const char* name, const float& val) { GLint location = glGetUniformLocation(mProgramId, name); glUniform1f(location, val); @@ -242,7 +247,7 @@ struct RenderProgram : AFileModificationListener { glUniform3fv(location, 1, vec.data()); return location; } - GLint SetVec3Array(const char* name, int count, const float* array) { + GLint SetVec3Array(const char* name, const int count, const float* array) { GLint location = glGetUniformLocation(mProgramId, name); glUniform3fv(location, count, array); return location; @@ -262,6 +267,11 @@ struct RenderProgram : AFileModificationListener { glUniformMatrix4fv(location, 1, GL_FALSE, mat.data()); return location; } + GLint SetMat44Array (const char* name, int count, const Matrix44f* mat_array) { + GLint location = glGetUniformLocation(mProgramId, name); + glUniformMatrix4fv(location, count, GL_FALSE, mat_array[0].data()); + return location; + } GLint SetMat33 (const char* name, const Matrix33f& mat) { GLint location = glGetUniformLocation(mProgramId, name); glUniformMatrix3fv(location, 1, GL_FALSE, mat.data());