feat: shader support multiple directional lights
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
#version 410 core
|
#version 410 core
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
in vec3 vertexPos;
|
in vec3 vertexPos; // must be world-space position
|
||||||
in vec3 vertexNormal;
|
in vec3 vertexNormal;
|
||||||
in vec2 TexCoords;
|
in vec2 TexCoords;
|
||||||
|
|
||||||
@ -9,8 +9,8 @@ uniform vec3 viewPos;
|
|||||||
|
|
||||||
// Lights
|
// Lights
|
||||||
struct Light {
|
struct Light {
|
||||||
int type;
|
int type; // 0 = directional (shadowed), other = non-directional (no shadow)
|
||||||
vec3 position;
|
vec3 position; // for directional: encode light direction (see note)
|
||||||
vec3 color;
|
vec3 color;
|
||||||
float intensity;
|
float intensity;
|
||||||
mat4 lightSpace;
|
mat4 lightSpace;
|
||||||
@ -39,47 +39,52 @@ uniform bool useRoughnessMap;
|
|||||||
uniform bool useAoMap;
|
uniform bool useAoMap;
|
||||||
|
|
||||||
uniform float opacity;
|
uniform float opacity;
|
||||||
// uniform int currentLight;
|
|
||||||
|
|
||||||
#define PI 3.14159265359
|
#define PI 3.14159265359
|
||||||
#define LIGHT_COLOR vec3(1.0, 1.0, 1.0)
|
#define LIGHT_COLOR vec3(1.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
// Improved ShadowCalculation: returns [0,1] shadow factor (1 = fully in shadow)
|
||||||
float ShadowCalculation(sampler2D shadowMap, mat4 lightSpace, vec3 N, vec3 L)
|
float ShadowCalculation(sampler2D shadowMap, mat4 lightSpace, vec3 N, vec3 L)
|
||||||
{
|
{
|
||||||
|
// Transform fragment position to light's clip / NDC space
|
||||||
vec4 fragPosLightSpace = lightSpace * vec4(vertexPos, 1.0);
|
vec4 fragPosLightSpace = lightSpace * vec4(vertexPos, 1.0);
|
||||||
|
|
||||||
// transform to [0,1]
|
// perspective divide
|
||||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||||
|
// to [0,1]
|
||||||
projCoords = projCoords * 0.5 + 0.5;
|
projCoords = projCoords * 0.5 + 0.5;
|
||||||
if(projCoords.z > 1.0) {
|
|
||||||
|
// if outside the light's orthographic frustum (xy outside or z > 1), consider unshadowed
|
||||||
|
if (projCoords.z > 1.0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
if (projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get depth from shadow map
|
|
||||||
float closestDepth = texture(shadowMap, projCoords.xy).r;
|
|
||||||
float currentDepth = projCoords.z;
|
float currentDepth = projCoords.z;
|
||||||
|
// basic bias (slope-dependent)
|
||||||
// bias to prevent self-shadowing (depend on slope)
|
|
||||||
float bias = max(0.001 * (1.0 - dot(N, L)), 0.0005);
|
float bias = max(0.001 * (1.0 - dot(N, L)), 0.0005);
|
||||||
// float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
|
|
||||||
|
|
||||||
|
// PCF (3x3)
|
||||||
float shadow = 0.0;
|
float shadow = 0.0;
|
||||||
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
|
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
|
||||||
for(int x = -1; x <= 1; ++x)
|
for (int x = -1; x <= 1; ++x) {
|
||||||
{
|
for (int y = -1; y <= 1; ++y) {
|
||||||
for(int y = -1; y <= 1; ++y)
|
|
||||||
{
|
|
||||||
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||||
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
shadow += (currentDepth - bias) > pcfDepth ? 1.0 : 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shadow /= 9.0;
|
shadow /= 9.0;
|
||||||
|
|
||||||
|
// clamp to [0,1]
|
||||||
|
shadow = clamp(shadow, 0.0, 1.0);
|
||||||
return shadow;
|
return shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helper functions (GGX, Fresnel, Geometry)
|
// PBR helpers (unchanged)
|
||||||
float DistributionGGX(vec3 N, vec3 H, float roughness)
|
float DistributionGGX(vec3 N, vec3 H, float roughness)
|
||||||
{
|
{
|
||||||
float a = roughness * roughness;
|
float a = roughness * roughness;
|
||||||
@ -137,22 +142,33 @@ void main()
|
|||||||
vec3 F0 = mix(vec3(0.04), baseColor, metal);
|
vec3 F0 = mix(vec3(0.04), baseColor, metal);
|
||||||
|
|
||||||
vec3 Lo = vec3(0.0);
|
vec3 Lo = vec3(0.0);
|
||||||
// FragColor = vec4(1.0 - shadow, 1.0 - shadow, 1.0 - shadow, 1.0);
|
|
||||||
// return;
|
|
||||||
|
|
||||||
float shadow = 0.0;
|
|
||||||
|
|
||||||
// Loop over all lights
|
// Loop over all lights
|
||||||
for (int i = 0; i < lightsCount; i++)
|
for (int i = 0; i < lightsCount; i++)
|
||||||
{
|
{
|
||||||
vec3 L = normalize(lights[i].position - vertexPos);
|
// compute light vector L depending on type
|
||||||
|
vec3 L;
|
||||||
|
if (lights[i].type == 0) {
|
||||||
|
// directional light: convention here is that lights[i].position stores the direction
|
||||||
|
// *towards* the light (for example, for sun direction you may upload -sunDir).
|
||||||
|
// Adjust according to your CPU-side convention.
|
||||||
|
L = normalize(lights[i].position); // expect this to be a direction
|
||||||
|
} else {
|
||||||
|
// point / spot style light: position is world-space point
|
||||||
|
L = normalize(lights[i].position - vertexPos);
|
||||||
|
}
|
||||||
|
|
||||||
vec3 H = normalize(V + L);
|
vec3 H = normalize(V + L);
|
||||||
|
|
||||||
float NDF = DistributionGGX(N, H, rough);
|
float NDF = DistributionGGX(N, H, rough);
|
||||||
float G = GeometrySmith(N, V, L, rough);
|
float G = GeometrySmith(N, V, L, rough);
|
||||||
vec3 F = fresnelSchlick(max(dot(H,V),0.0), F0);
|
vec3 F = fresnelSchlick(max(dot(H,V),0.0), F0);
|
||||||
|
|
||||||
shadow = ShadowCalculation(lights[i].shadowMap, lights[i].lightSpace, N, L);
|
// compute shadow only for directional (type==0) lights that have shadow maps
|
||||||
|
float shadow_i = 0.0;
|
||||||
|
if (lights[i].type == 0) {
|
||||||
|
shadow_i = ShadowCalculation(lights[i].shadowMap, lights[i].lightSpace, N, L);
|
||||||
|
}
|
||||||
|
|
||||||
vec3 numerator = NDF * G * F;
|
vec3 numerator = NDF * G * F;
|
||||||
float denominator = 4.0 * max(dot(N,V),0.0) * max(dot(N,L),0.0) + 0.001;
|
float denominator = 4.0 * max(dot(N,V),0.0) * max(dot(N,L),0.0) + 0.001;
|
||||||
@ -165,14 +181,19 @@ void main()
|
|||||||
float NdotL = max(dot(N,L), 0.0);
|
float NdotL = max(dot(N,L), 0.0);
|
||||||
|
|
||||||
vec3 radiance = lights[i].color * lights[i].intensity;
|
vec3 radiance = lights[i].color * lights[i].intensity;
|
||||||
Lo += (kD * baseColor / PI + specular) * radiance * NdotL;
|
|
||||||
|
// Apply shadow_i only to this light's contribution:
|
||||||
|
// when shadow_i == 1.0 -> this light contributes 0
|
||||||
|
// when shadow_i == 0.0 -> full contribution
|
||||||
|
vec3 contrib = (kD * baseColor / PI + specular) * radiance * NdotL * (1.0 - shadow_i);
|
||||||
|
|
||||||
|
Lo += contrib;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ambient
|
// Ambient (unshadowed by design)
|
||||||
vec3 ambient = vec3(0.03) * baseColor * aoValue;
|
vec3 ambient = vec3(0.03) * baseColor * aoValue;
|
||||||
|
|
||||||
// TODO: apply shadow
|
vec3 color = ambient + Lo;
|
||||||
vec3 color = ambient + (1.0 - shadow) * Lo;
|
|
||||||
|
|
||||||
// HDR tonemapping + gamma
|
// HDR tonemapping + gamma
|
||||||
color = color / (color + vec3(1.0));
|
color = color / (color + vec3(1.0));
|
||||||
|
Reference in New Issue
Block a user