Skip to content

Commit

Permalink
Point Light Shadows
Browse files Browse the repository at this point in the history
  • Loading branch information
fszewczyk committed Dec 2, 2024
1 parent c6ded68 commit e413d39
Show file tree
Hide file tree
Showing 14 changed files with 429 additions and 134 deletions.
9 changes: 9 additions & 0 deletions resources/shaders/fragment/distance.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#version 330 core

in vec3 FragPos;

out float FragValue;

void main() {
FragValue = length(FragPos);// / 30.0;
}
189 changes: 107 additions & 82 deletions resources/shaders/fragment/uber.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,27 @@ in vec3 Normal;
uniform vec3 viewPos; // Camera position

// ******** LIGHT DATA ********
#define POINT_LIGHT_MAX_NUM 8
struct PointLight {
vec3 position;
vec3 color;
float range;
};

uniform int numPointLights;
uniform PointLight pointLights[16];
uniform PointLight pointLights[POINT_LIGHT_MAX_NUM];
uniform sampler2D pointLightsDepthCubeMap[POINT_LIGHT_MAX_NUM];

#define DirectionalLightLevelsOfDetail 4
#define DIRECTIONAL_LIGHT_LODS 4
#define DIRECTIONAL_LIGHT_MAX_NUM 2
struct DirectionalLight {
sampler2D shadowSampler;
mat4 lightSpaceMatrix[DirectionalLightLevelsOfDetail];
mat4 lightSpaceMatrix[DIRECTIONAL_LIGHT_LODS];
vec3 direction;
vec3 color;
};
uniform int numDirectionalLights;
uniform DirectionalLight directionalLights[2];
uniform DirectionalLight directionalLights[DIRECTIONAL_LIGHT_MAX_NUM];
uniform sampler2D directionalLightsDepthMap[DIRECTIONAL_LIGHT_MAX_NUM];

uniform vec3 ambientLight;

Expand All @@ -36,134 +40,155 @@ struct Material {
};
uniform Material material;

// ******** PROCESSING DATA ********
struct LodBlend {
int highLod;
int lowLod;
float highLodWeight;
};

// ******** FUNCTIONS ********
vec3 calculateAmbient() {
return ambientLight * material.color;
}

float sampleAtlas(sampler2D map, int cols, int index, vec2 xy)
{
return texture(map, vec2((1.0 / cols) * index + 0.0005, 0) + xy * vec2((1.0 / cols) - 0.001, 1)).r;
}

float sampleCubeMap(sampler2D map, vec3 direction)
{
vec2 uv;
int faceIndex;
if (abs(direction.x) > abs(direction.y) && abs(direction.x) > abs(direction.z)) {
faceIndex = direction.x > 0.0 ? 1 : 0;
if(faceIndex == 0)
{
uv = vec2(direction.z, direction.y) / abs(direction.x);
}
else
{
uv = vec2(-direction.z, direction.y) / abs(direction.x);
}
} else if (abs(direction.y) > abs(direction.x) && abs(direction.y) > abs(direction.z)) {
faceIndex = direction.y > 0.0 ? 3 : 2;
if(faceIndex == 3)
{
uv = vec2(-direction.x, direction.z) / abs(direction.y);
}
else
{
uv = vec2(-direction.x, -direction.z) / abs(direction.y);
}
} else {
faceIndex = direction.z > 0.0 ? 5 : 4;
if(faceIndex == 5)
{
uv = vec2(direction.x, direction.y) / abs(direction.z);
}
else
{
uv = vec2(-direction.x, direction.y) / abs(direction.z);
}
}

uv = uv * 0.5 + 0.5;

return sampleAtlas(map, 6, faceIndex, uv);
}

vec3 calculatePointLights() {
vec3 normal = normalize(Normal);
vec3 result = vec3(0.0);

for (int i = 0; i < numPointLights; ++i) {
float relativeIntensity = 1.0 - (length(pointLights[i].position - FragPos) /
pointLights[i].range);
vec3 lightToFrag = FragPos - pointLights[i].position;
float distanceToLight = length(lightToFrag);
float relativeIntensity = 1.0 - (distanceToLight / pointLights[i].range);

if (relativeIntensity > 0.0) {
// Calculate light direction
vec3 lightDir = normalize(-lightToFrag);

// Perform PCF
float bias = 0.1; // Bias to reduce shadow acne
float shadow = 0.0;
int pcfSamples = 1; // Number of PCF samples per dimension
float pcfRadius = 0.005; // Radius for PCF sampling

for (int x = -pcfSamples; x <= pcfSamples; ++x) {
for (int y = -pcfSamples; y <= pcfSamples; ++y) {
for (int z = -pcfSamples; z <= pcfSamples; ++z) {
vec3 offsetDir = lightDir + vec3(x, y, z) * pcfRadius;
float shadowMapDepth = sampleCubeMap(pointLightsDepthCubeMap[i], offsetDir);
if (distanceToLight - bias <= shadowMapDepth) {
shadow += 1.0;
}
}
}
}

// Normalize shadow value
int totalSamples = (2 * pcfSamples + 1) * (2 * pcfSamples + 1) * (2 * pcfSamples + 1);
shadow /= float(totalSamples);

if (relativeIntensity > 0) {
// Diffuse
vec3 lightDir = normalize(pointLights[i].position - FragPos);
float diff = max(dot(normal, lightDir), 0.0);
// Diffuse lighting
vec3 lightDirNormalized = normalize(lightDir);
float diff = max(dot(normal, lightDirNormalized), 0.0);
vec3 diffuse = pointLights[i].color * diff * material.color;

// Specular
// Specular lighting
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 reflectDir = reflect(-lightDirNormalized, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = pointLights[i].color * spec * material.color;

result += relativeIntensity * (diffuse + specular);
// Apply lighting with shadows
result += shadow * relativeIntensity * (diffuse + specular);
}
}
return result;
}

float sampleShadowMap(sampler2D map, int index, vec2 xy)
{
return texture(map, vec2((1.0 / DirectionalLightLevelsOfDetail) * index + 0.0005, 0) + xy * vec2((1.0 / DirectionalLightLevelsOfDetail) - 0.001, 1)).r * 0.5 + 0.5;
return result;
}

float calculateShadowFactor(int directionalLightIndex, LodBlend lodBlend) {
vec4 fragPosLightSpaceLowLod = directionalLights[directionalLightIndex].lightSpaceMatrix[lodBlend.lowLod] * vec4(FragPos, 1.0);
vec3 projCoordsLowLod = fragPosLightSpaceLowLod.xyz / fragPosLightSpaceLowLod.w;
projCoordsLowLod = projCoordsLowLod * 0.5 + 0.5;

vec4 fragPosLightSpaceHighLod = directionalLights[directionalLightIndex].lightSpaceMatrix[lodBlend.highLod] * vec4(FragPos, 1.0);
float calculateShadowFactor(int directionalLightIndex, int cascadeIndex) {
vec4 fragPosLightSpaceHighLod = directionalLights[directionalLightIndex].lightSpaceMatrix[cascadeIndex] * vec4(FragPos, 1.0);
vec3 projCoordsHighLod = fragPosLightSpaceHighLod.xyz / fragPosLightSpaceHighLod.w;
projCoordsHighLod = projCoordsHighLod * 0.5 + 0.5;

if (projCoordsLowLod.z > 1.0 || projCoordsHighLod.z > 1.0) {
if (projCoordsHighLod.z > 1.0) {
return 1.0;
}

float bias = 0.01;
int pcfKernelSize = 3;

float texelSizeLowLod = 1.0 / 2048.0;
float texelSizeHighLod = 2 * texelSizeLowLod;

if (lodBlend.highLod == lodBlend.lowLod) {
texelSizeHighLod = texelSizeLowLod;
}

// Calculate shadow factor for low LOD
float shadowLowLod = 0.0;
for (int x = -pcfKernelSize; x <= pcfKernelSize; ++x) {
for (int y = -pcfKernelSize; y <= pcfKernelSize; ++y) {
// Sample shadow map for low LOD
float closestDepthLowLod = sampleShadowMap(directionalLights[directionalLightIndex].shadowSampler, lodBlend.lowLod, (projCoordsLowLod.xy + vec2(x, y) * texelSizeLowLod));
if (projCoordsLowLod.z - bias > closestDepthLowLod) {
shadowLowLod += 1.0;
}
}
}
shadowLowLod /= float((pcfKernelSize * 2 + 1) * (pcfKernelSize * 2 + 1));
float texelSizeHighLod = 1 / 1024.0;

// Calculate shadow factor for high LOD
float shadowHighLod = 0.0;
for (int x = -pcfKernelSize; x <= pcfKernelSize; ++x) {
for (int y = -pcfKernelSize; y <= pcfKernelSize; ++y) {
// Sample shadow map for high LOD
float closestDepthHighLod = sampleShadowMap(directionalLights[directionalLightIndex].shadowSampler, lodBlend.highLod, (projCoordsHighLod.xy + vec2(x, y) * texelSizeHighLod));
float closestDepthHighLod = sampleAtlas(directionalLightsDepthMap[directionalLightIndex], DIRECTIONAL_LIGHT_LODS, cascadeIndex, (projCoordsHighLod.xy + vec2(x, y) * texelSizeHighLod)) * 0.5 + 0.5;
if (projCoordsHighLod.z - bias > closestDepthHighLod) {
shadowHighLod += 1.0;
}
}
}
shadowHighLod /= float((pcfKernelSize * 2 + 1) * (pcfKernelSize * 2 + 1));

// Blend the shadow factors between the low and high LOD based on highLodWeight
return mix(1.0 - shadowLowLod, 1.0 - shadowHighLod, lodBlend.highLodWeight);
return 1 - shadowHighLod;
}

LodBlend chooseLightLOD(mat4 lightSpaceMatrix[DirectionalLightLevelsOfDetail], vec3 fragPos) {
int chooseLightCascade(mat4 lightSpaceMatrix[DIRECTIONAL_LIGHT_LODS], vec3 fragPos) {
const float blendRange = 1.0; // Range near cascade far edge to blend

for (int lod = 0; lod < DirectionalLightLevelsOfDetail; ++lod) {
for (int lod = 0; lod < DIRECTIONAL_LIGHT_LODS; ++lod) {
vec4 fragPosLightSpace = lightSpaceMatrix[lod] * vec4(fragPos, 1.0);
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;

if (projCoords.x >= 0.0 && projCoords.y >= 0.0 && projCoords.x <= 1.0 &&
projCoords.y <= 1.0 && projCoords.z < 1.0) {

if (lod == 3) {
return LodBlend(lod, lod, 1.0);
}

vec4 fragPosLightSpaceNext = lightSpaceMatrix[lod + 1] * vec4(fragPos, 1.0);
vec3 projCoordsNext = fragPosLightSpaceNext.xyz / fragPosLightSpaceNext.w;
projCoordsNext = projCoordsNext * 0.5 + 0.5;

if (projCoordsNext.x >= 0.0 && projCoordsNext.y >= 0.0 &&
projCoordsNext.x <= 1.0 && projCoordsNext.y <= 1.0 &&
projCoordsNext.z < 1.0) {

float depthInCurrentLOD = projCoords.z;
float highLodWeight = smoothstep(1.0 - blendRange, 1.0, depthInCurrentLOD);

return LodBlend(lod, lod + 1, highLodWeight);
}

return LodBlend(lod, lod, 1.0);
return lod;
}
}

return LodBlend(-1, -1, 0.0);
return -1;
}

vec3 calculateDirectionalLights() {
Expand All @@ -183,10 +208,10 @@ vec3 calculateDirectionalLights() {
vec3 specular = directionalLights[i].color * spec * material.color;

// Calculate shadow factor
LodBlend lodBlend = chooseLightLOD(directionalLights[i].lightSpaceMatrix, FragPos);
int cascadeIndex = chooseLightCascade(directionalLights[i].lightSpaceMatrix, FragPos);
float shadow = 1.0;
if (lodBlend.lowLod != -1) {
shadow = calculateShadowFactor(i, lodBlend);
if (cascadeIndex != -1) {
shadow = calculateShadowFactor(i, cascadeIndex);
}
result += (diffuse + specular) * shadow;
}
Expand Down
19 changes: 19 additions & 0 deletions resources/shaders/vertex/distance.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#version 330 core

layout(location = 0) in vec3 aPos; // Vertex position

uniform mat4 lightSpaceMatrix; // Model matrix
uniform mat4 modelMatrix; // Projection matrix

out vec3 FragPos; // Output the depth value to the fragment shader

void main() {
// Transform vertex position to world space, then to light space
vec4 lightSpacePosition = lightSpaceMatrix * modelMatrix * vec4(aPos, 1.0);

// Set the final position
gl_Position = lightSpacePosition;

// Calculate the depth in light space (NDC) range [0,1]
FragPos = lightSpacePosition.xyz;
}
7 changes: 2 additions & 5 deletions resources/shaders/vertex/position.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

layout(location = 0) in vec3 position; // Vertex position

uniform mat4 modelMatrix; // Model matrix
uniform mat4 viewMatrix; // View matrix
uniform mat4 projectionMatrix; // Projection matrix
uniform mat4 projectionViewModelMatrix; // Model matrix

void main() {
// Transform the vertex position to clip space
gl_Position =
projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
gl_Position = projectionViewModelMatrix * vec4(position, 1.0);
}
5 changes: 2 additions & 3 deletions resources/shaders/vertex/position_and_normal.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ layout(location = 0) in vec3 position; // Vertex position
layout(location = 1) in vec3 normal; // Vertex normal

uniform mat4 modelMatrix; // Model matrix
uniform mat4 viewMatrix; // View matrix
uniform mat4 projectionMatrix; // Projection matrix
uniform mat4 projectionViewMatrix; // View matrix

out vec3 FragPos; // Fragment position in world space
out vec3 Normal; // Normal in world space
Expand All @@ -14,5 +13,5 @@ void main() {
FragPos = vec3(modelMatrix * vec4(position, 1.0));
Normal = normalize(mat3(transpose(inverse(modelMatrix))) * normal);

gl_Position = projectionMatrix * viewMatrix * vec4(FragPos, 1.0);
gl_Position = projectionViewMatrix * vec4(FragPos, 1.0);
}
2 changes: 2 additions & 0 deletions src/AssetManager/Shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ GLenum Shader::shaderTypeToGLenum(Type type) {
switch (type) {
case Type::Vertex:
return GL_VERTEX_SHADER;
case Type::Geometry:
return GL_GEOMETRY_SHADER;
case Type::Fragment:
return GL_FRAGMENT_SHADER;
default:
Expand Down
3 changes: 2 additions & 1 deletion src/AssetManager/Shader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class Shader : public Asset {
public:
enum class Type {
Vertex,
Fragment
Geometry,
Fragment,
};

Shader(const std::string& filepath, Type type);
Expand Down
2 changes: 1 addition & 1 deletion src/Components/DirectionalLightComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class DirectionalLightComponent : public BaseComponent<DirectionalLightComponent
glm::vec3 color = {1.0f, 1.0f, 1.0f};

inline static uint8_t LevelsOfDetail = 4;
inline static std::vector<float> CascadePlanes = {0.01, 5.0, 16.0, 32.0, 64.0};
inline static std::vector<float> CascadePlanes = {0.01, 5.0, 16.0, 32.0, 96.0};

glm::mat4 getLightSpaceMatrix(
const TransformComponent& lightTransformComponent,
Expand Down
4 changes: 0 additions & 4 deletions src/Rendering/FrameBuffers/DepthAtlasFrameBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ DepthAtlasFrameBuffer::DepthAtlasFrameBuffer(int texturesInAtlas, GLenum minFilt
: _texturesInAtlas(texturesInAtlas), _width(2048), _height(2048),
_textureDepthBuffer(minFilter, magFilter, wrapS, wrapT) {
setupFramebuffer();
bind();
setSize(_width, _height);
clear();
unbind();
}

DepthAtlasFrameBuffer::DepthAtlasFrameBuffer(DepthAtlasFrameBuffer&& other) noexcept
Expand Down
Loading

0 comments on commit e413d39

Please sign in to comment.