Skip to content

Added shadow mapping for directional lights #315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4a12dfc
Initial (dirty) implementation
adriengivry Nov 15, 2024
61200e6
Now (nearly) using scene lights
adriengivry Nov 16, 2024
5c5141b
Made it work with the actual scene directional light
adriengivry Nov 18, 2024
ee43847
Removed unused camera override
adriengivry Nov 18, 2024
49f886a
Moved the shadow render feature and pass to the appropriate project (…
adriengivry Nov 18, 2024
c5c354c
Fixed framebuffer out of bounds shadow sampling
adriengivry Nov 18, 2024
05287b4
Fixed debug line drawer
adriengivry Nov 18, 2024
0834ddf
Fixed material editor std::any exception
adriengivry Nov 19, 2024
758e5f6
Added shadow fading with distance (quadratic falloff)
adriengivry Nov 19, 2024
a16c576
Removed quadratic falloff (inconsistent). Will re-add once a more per…
adriengivry Nov 19, 2024
41faf0b
Added future support for multiple shadow maps
adriengivry Nov 20, 2024
323d523
Cleaned up shader code, and made post-build for OvEditor and OvGame r…
adriengivry Nov 20, 2024
4952319
* updated default material to cast and receive shadows
adriengivry Nov 20, 2024
ffa37f5
Simplified shadow shader
adriengivry Nov 20, 2024
273197a
Uniforms starting with '_' are now "private", meaning they don't get …
adriengivry Nov 20, 2024
a704e13
Removed framebuffer unused comment
adriengivry Nov 21, 2024
bfd1d2c
Exposed shadow area size through the directional light component
adriengivry Nov 21, 2024
4dadd49
Added shadowFollowCamera toggle, and areaSize settings
adriengivry Nov 21, 2024
6827e37
Updated roadmap in README
adriengivry Nov 21, 2024
819e092
Updated README (fixed overloadengine.org link)
adriengivry Nov 21, 2024
1e5f204
Added "singleUse" option to material properties. Ideal to make sure a…
adriengivry Nov 21, 2024
8a82e4a
Exposed shadow map resolution to the `CDirectionaLight` component, an…
adriengivry Nov 22, 2024
38f329b
Updated default shadow map resolution from 4K to 8K
adriengivry Nov 22, 2024
3251824
Fixed broken material texture assignment
adriengivry Nov 26, 2024
88c2ff5
Fixed ShadowRenderPass class comment
adriengivry Nov 29, 2024
ba0dd02
Exposed `GetCastShadow` and `SetCastShadow` from CDirectionaLight to lua
adriengivry Nov 29, 2024
bfe3f7c
Exposed missing methods from CDirectionalLight
adriengivry Nov 29, 2024
d9df8b8
Removed TODO in PBR.ovfxh
adriengivry Feb 6, 2025
cfeac8c
Improved shadowMapResolution choices definition to avoid multiple emp…
adriengivry Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@ Here is a non-exhaustive list of Overload main features:

## 3.2. To implement
Again, a non-exhaustive list of Overload in-coming features:
- Shadow mapping
- Custom post-processing
- Renderer Hardware Interface (Multiple graphics backend support)
- More input device support (Gamepad)
- Prefab system
- Skeletal animation
Expand Down Expand Up @@ -130,7 +128,7 @@ Overload is licenced under an MIT licence.

## 4.6. More information
If you are interested in Overload, you can download our engine and the demo game we made with it on our website:<br>
http://overloadengine.org
https://overloadengine.org

Learn about Overload (Tutorials and Scripting API) by visiting our Wiki:<br>
https://github.com/adriengivry/Overload/wiki
Expand Down
2 changes: 2 additions & 0 deletions Resources/Engine/Materials/Default.ovmat
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<backface_culling>true</backface_culling>
<depth_test>true</depth_test>
<gpu_instances>1</gpu_instances>
<cast_shadows>1</cast_shadows>
<receive_shadows>1</receive_shadows>
</settings>
<uniforms>
<uniform>
Expand Down
18 changes: 14 additions & 4 deletions Resources/Engine/Shaders/Lighting/BlinnPhong.ovfxh
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,19 @@ vec3 ComputePointLight(mat4 light, vec3 fragPos, vec4 diffuseTexel, vec4 specula
return BlinnPhong(lightDirection, lightColor, intensity * luminosity, diffuseTexel, specularTexel, normal, viewDir, shininess);
}

vec3 ComputeDirectionalLight(mat4 light, vec4 diffuseTexel, vec4 specularTexel, vec3 normal, vec3 viewDir, float shininess)
vec3 ComputeDirectionalLight(mat4 light, vec3 fragPos, vec4 diffuseTexel, vec4 specularTexel, vec3 normal, vec3 viewDir, float shininess, sampler2D shadowMap, mat4 lightSpaceMatrix)
{
return BlinnPhong(-light[1].rgb, UnPack(light[2][0]), light[3][3], diffuseTexel, specularTexel, normal, viewDir, shininess);
vec3 lightDir = -light[1].rgb;
vec3 blinnPhong = BlinnPhong(lightDir, UnPack(light[2][0]), light[3][3], diffuseTexel, specularTexel, normal, viewDir, shininess);

if (light[2][1] > 0.0f)
{
vec4 fragPosLightSpace = lightSpaceMatrix * vec4(fragPos, 1.0);
float shadow = CalculateShadow(fragPosLightSpace, shadowMap, normal, lightDir);
blinnPhong *= 1.0 - shadow;
}

return blinnPhong;
}

vec3 ComputeSpotLight(mat4 light, vec3 fragPos, vec4 diffuseTexel, vec4 specularTexel, vec3 normal, vec3 viewDir, float shininess)
Expand Down Expand Up @@ -71,7 +81,7 @@ vec3 ComputeAmbientSphereLight(mat4 light, vec3 fragPos, vec4 diffuseTexel)
return IsPointInSphere(fragPos, lightPosition, radius) ? diffuseTexel.rgb * lightColor * intensity : vec3(0.0);
}

vec4 ComputeBlinnPhongLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 diffuse, vec3 specular, sampler2D diffuseMap, sampler2D specularMap, float shininess)
vec4 ComputeBlinnPhongLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 diffuse, vec3 specular, sampler2D diffuseMap, sampler2D specularMap, float shininess, sampler2D shadowMap, mat4 lightSpaceMatrix)
{
vec3 viewDir = normalize(viewPos - fragPos);
vec4 diffuseTexel = texture(diffuseMap, texCoords) * diffuse;
Expand All @@ -87,7 +97,7 @@ vec4 ComputeBlinnPhongLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 f
switch(lightType)
{
case 0: lightAccumulation += ComputePointLight(light, fragPos, diffuseTexel, specularTexel, normal, viewDir, shininess); break;
case 1: lightAccumulation += ComputeDirectionalLight(light, diffuseTexel, specularTexel, normal, viewDir, shininess); break;
case 1: lightAccumulation += ComputeDirectionalLight(light, fragPos, diffuseTexel, specularTexel, normal, viewDir, shininess, shadowMap, lightSpaceMatrix); break;
case 2: lightAccumulation += ComputeSpotLight(light, fragPos, diffuseTexel, specularTexel, normal, viewDir, shininess); break;
case 3: lightAccumulation += ComputeAmbientBoxLight(light, fragPos, diffuseTexel); break;
case 4: lightAccumulation += ComputeAmbientSphereLight(light, fragPos, diffuseTexel); break;
Expand Down
11 changes: 10 additions & 1 deletion Resources/Engine/Shaders/Lighting/PBR.ovfxh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ vec3 ComputeAmbientSphereLight(mat4 light, vec3 fragPos)
return IsPointInSphere(fragPos, lightPosition, radius) ? lightColor * intensity : vec3(0.0);
}

vec4 ComputePBRLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 inAlbedo, float inMetallic, float inRoughness, sampler2D albedoMap, sampler2D metallicMap, sampler2D roughnessMap, sampler2D aoMap)
vec4 ComputePBRLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos, vec4 inAlbedo, float inMetallic, float inRoughness, sampler2D albedoMap, sampler2D metallicMap, sampler2D roughnessMap, sampler2D aoMap, sampler2D shadowMap, mat4 lightSpaceMatrix)
{
vec4 albedoRGBA = texture(albedoMap, texCoords) * inAlbedo;
vec3 albedo = pow(albedoRGBA.rgb, vec3(2.2));
Expand Down Expand Up @@ -110,6 +110,15 @@ vec4 ComputePBRLighting(vec2 texCoords, vec3 normal, vec3 viewPos, vec3 fragPos,

case 1:
lightCoeff = light[3][3];

if (light[2][1] > 0.0f)
{
const vec3 lightDir = light[1].rgb;
vec4 fragPosLightSpace = lightSpaceMatrix * vec4(fragPos, 1.0);
float shadow = CalculateShadow(fragPosLightSpace, shadowMap, normal, lightDir);
lightCoeff *= 1.0 - shadow;
}

break;

case 2:
Expand Down
63 changes: 63 additions & 0 deletions Resources/Engine/Shaders/Lighting/Shared.ovfxh
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,66 @@ float LuminosityFromAttenuation(mat4 light, vec3 fragPos)

return 1.0 / attenuation;
}

float SampleShadow(sampler2D shadowMap, vec3 projCoords, float bias)
{
float depth = texture(shadowMap, projCoords.xy).r;
return 1.0 - step(projCoords.z - bias, depth);
}

// Distance fade for shadows to appear smoothly
float CalculateShadowFalloff(vec3 projCoords, float intensity)
{
projCoords.z = clamp(projCoords.z, 0.5, 1.0); // Prevents falloff when the light source is close to a shadow caster
const float dist = (0.5 - clamp(distance(projCoords, vec3(0.5)), 0.0, 0.5)) * 2.0;
return 1.0 - pow(1.0 - dist, intensity);
}

// PCF (Percentage-Closer Filtering) shadows => AKA Soft Shadows
float CalculateSoftShadow(sampler2D shadowMap, vec3 projCoords, float bias, float texelSize)
{
const int range = 1;
const float invSamples = 1.0 / pow((range * 2 + 1), 2);

float shadow = 0.0;

for (int x = -range; x <= range; ++x)
{
for (int y = -range; y <= range; ++y)
{
const vec2 offset = vec2(x, y) * texelSize;
const vec3 offsettedProjCoords = vec3(projCoords.xy + offset, projCoords.z);
shadow += SampleShadow(shadowMap, offsettedProjCoords, bias);
}
}

return shadow * invSamples;
}

// Default shadow calculation
float CalculateHardShadow(sampler2D shadowMap, vec3 projCoords, float bias)
{
return SampleShadow(shadowMap, projCoords, bias);
}

float CalculateShadowBias(vec3 normal, vec3 lightDir, float texelSize)
{
const float bias = 0.001;
const float k = bias * (texelSize * 8096);
return max(k * (1.0 - dot(normal, lightDir)), k);
}

float CalculateShadow(vec4 fragPosLightSpace, sampler2D shadowMap, vec3 normal, vec3 lightDir)
{
const float texelSize = 1.0 / vec2(textureSize(shadowMap, 0)).x;
const vec3 projCoords = (fragPosLightSpace.xyz / fragPosLightSpace.w) * 0.5 + 0.5;

if (projCoords.z > 1.0) return 0.0;

float bias = CalculateShadowBias(normal, lightDir, texelSize);
float shadow = CalculateSoftShadow(shadowMap, projCoords, bias, texelSize);

shadow *= CalculateShadowFalloff(projCoords, 8);

return shadow;
}
20 changes: 20 additions & 0 deletions Resources/Engine/Shaders/Shadow.ovfx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#shader vertex
#version 430 core

layout (location = 0) in vec3 geo_Pos;

uniform mat4 _LightSpaceMatrix;
uniform mat4 _ModelMatrix;

void main()
{
gl_Position = _LightSpaceMatrix * _ModelMatrix * vec4(geo_Pos, 1.0);
}

#shader fragment
#version 430 core

void main()
{
gl_FragDepth = gl_FragCoord.z;
}
17 changes: 16 additions & 1 deletion Resources/Engine/Shaders/Standard.ovfx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ uniform sampler2D u_NormalMap;
uniform sampler2D u_HeightMap;
uniform sampler2D u_MaskMap;

uniform sampler2D _ShadowMap;
uniform mat4 _LightSpaceMatrix;

out vec4 FRAGMENT_COLOR;

void main()
Expand All @@ -71,7 +74,19 @@ void main()
if (!IsMasked(u_MaskMap, texCoords))
{
vec3 normal = ComputeNormal(u_EnableNormalMapping, texCoords, fs_in.Normal, u_NormalMap, fs_in.TBN);
FRAGMENT_COLOR = ComputeBlinnPhongLighting(texCoords, normal, ubo_ViewPos, fs_in.FragPos, u_Diffuse, u_Specular, u_DiffuseMap, u_SpecularMap, u_Shininess);
FRAGMENT_COLOR = ComputeBlinnPhongLighting(
texCoords,
normal,
ubo_ViewPos,
fs_in.FragPos,
u_Diffuse,
u_Specular,
u_DiffuseMap,
u_SpecularMap,
u_Shininess,
_ShadowMap,
_LightSpaceMatrix
);
}
else
{
Expand Down
5 changes: 4 additions & 1 deletion Resources/Engine/Shaders/StandardPBR.ovfx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ uniform float u_Roughness = 1.0;
uniform sampler2D u_HeightMap;
uniform sampler2D u_MaskMap;

uniform sampler2D _ShadowMap;
uniform mat4 _LightSpaceMatrix;

out vec4 FRAGMENT_COLOR;

void main()
Expand All @@ -73,7 +76,7 @@ void main()
if (!IsMasked(u_MaskMap, texCoords))
{
vec3 normal = ComputeNormal(u_EnableNormalMapping, texCoords, fs_in.Normal, u_NormalMap, fs_in.TBN);
FRAGMENT_COLOR = ComputePBRLighting(texCoords, normal, ubo_ViewPos, fs_in.FragPos, u_Albedo, u_Metallic, u_Roughness, u_AlbedoMap, u_MetallicMap, u_RoughnessMap, u_AmbientOcclusionMap);
FRAGMENT_COLOR = ComputePBRLighting(texCoords, normal, ubo_ViewPos, fs_in.FragPos, u_Albedo, u_Metallic, u_Roughness, u_AlbedoMap, u_MetallicMap, u_RoughnessMap, u_AmbientOcclusionMap, _ShadowMap, _LightSpaceMatrix);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,51 @@ namespace OvCore::ECS::Components
*/
std::string GetName() override;

/**
* Set if the light should cast shadows
* @param p_enabled
*/
void SetCastShadows(bool p_enabled);

/**
* Returns true if the light should cast shadows
*/
bool GetCastShadows() const;

/**
* Defines the area size of the shadow
* @param p_shadowAreaSize
*/
void SetShadowAreaSize(float p_shadowAreaSize);

/**
* Returns the area size of the shadow
*/
float GetShadowAreaSize() const;

/**
* Defines whether or not the light position should snap to the camera position
* @param p_enabled
*/
void SetShadowFollowCamera(bool p_enabled);

/**
* Returns true if the light position should snap to the camera position
*/
bool GetShadowFollowCamera() const;

/**
* Sets the shadow map resolution
* @note The resolution should be a power of 2 for better results
* @param p_resolution
*/
void SetShadowMapResolution(uint32_t p_resolution);

/**
* Returns the shadow map resolution
*/
uint32_t GetShadowMapResolution() const;

/**
* Serialize the component
* @param p_doc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace OvCore::ECS::Components
/**
* Returns light data
*/
const OvRendering::Entities::Light& GetData() const;
OvRendering::Entities::Light& GetData();

/**
* Returns light color
Expand Down
3 changes: 3 additions & 0 deletions Sources/Overload/OvCore/include/OvCore/Helpers/Serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace OvCore::Helpers
static void SerializeVec2(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, const OvMaths::FVector2& p_value);
static void SerializeVec3(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, const OvMaths::FVector3& p_value);
static void SerializeVec4(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, const OvMaths::FVector4& p_value);
static void SerializeMat4(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, const OvMaths::FMatrix4& p_value);
static void SerializeQuat(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, const OvMaths::FQuaternion& p_value);
static void SerializeColor(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, const OvUI::Types::Color& p_value);
static void SerializeModel(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvRendering::Resources::Model* p_value);
Expand All @@ -72,6 +73,7 @@ namespace OvCore::Helpers
static void DeserializeVec2(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvMaths::FVector2& p_out);
static void DeserializeVec3(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvMaths::FVector3& p_out);
static void DeserializeVec4(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvMaths::FVector4& p_out);
static void DeserializeMat4(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvMaths::FMatrix4& p_out);
static void DeserializeQuat(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvMaths::FQuaternion& p_out);
static void DeserializeColor(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvUI::Types::Color& p_out);
static void DeserializeModel(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name, OvRendering::Resources::Model*& p_out);
Expand All @@ -90,6 +92,7 @@ namespace OvCore::Helpers
static OvMaths::FVector2 DeserializeVec2(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name);
static OvMaths::FVector3 DeserializeVec3(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name);
static OvMaths::FVector4 DeserializeVec4(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name);
static OvMaths::FMatrix4 DeserializeMat4(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name);
static OvMaths::FQuaternion DeserializeQuat(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name);
static OvUI::Types::Color DeserializeColor(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name);
static OvRendering::Resources::Model* DeserializeModel(tinyxml2::XMLDocument& p_doc, tinyxml2::XMLNode* p_node, const std::string& p_name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ namespace OvCore::Rendering
protected:
std::chrono::high_resolution_clock::time_point m_startTime;
std::unique_ptr<OvRendering::Buffers::UniformBuffer> m_engineBuffer;
OvRendering::Data::FrameDescriptor m_cachedFrameDescriptor;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @project: Overload
* @author: Overload Tech.
* @licence: MIT
*/

#pragma once

#include <OvRendering/Entities/Camera.h>
#include <OvRendering/Features/DebugShapeRenderFeature.h>

#include <OvCore/ECS/Actor.h>
#include <OvCore/SceneSystem/SceneManager.h>
#include <OvCore/ECS/Components/CModelRenderer.h>
#include <OvCore/Resources/Material.h>
#include <OvCore/ECS/Components/CAmbientBoxLight.h>
#include <OvCore/ECS/Components/CAmbientSphereLight.h>
#include <OvCore/Rendering/SceneRenderer.h>

namespace OvCore::Rendering
{
/**
* Draw the scene for actor picking
*/
class ShadowRenderFeature : public OvRendering::Features::ARenderFeature
{
public:
/**
* Constructor
* @param p_renderer
*/
ShadowRenderFeature(OvRendering::Core::CompositeRenderer& p_renderer);

protected:
virtual void OnBeforeDraw(OvRendering::Data::PipelineState& p_pso, const OvRendering::Entities::Drawable& p_drawable);
virtual void OnAfterDraw(OvRendering::Data::PipelineState& p_pso, const OvRendering::Entities::Drawable& p_drawable);
};
}
Loading