add material sphere preview, add ssr, improve shadow

This commit is contained in:
2026-05-14 21:37:02 +08:00
parent d11f8bb0c9
commit f50824bdb1
30 changed files with 1277 additions and 356 deletions

View File

@ -1,4 +1,4 @@
// -----------------------------
// -----------------------------
// -- Based on Hazel PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
@ -22,8 +22,6 @@ uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_Transform;
uniform mat4 u_LightSpaceMatrix;
out VertexOutput
{
vec3 WorldPosition;
@ -33,7 +31,7 @@ out VertexOutput
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
vec4 FragPosLightSpace;
float ViewZ;
} vs_Output;
void main()
@ -45,9 +43,8 @@ void main()
vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal;
vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * vec4(a_Position, 1.0);
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
vs_Output.ViewZ = vs_Output.ViewPosition.z;
gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
}
@ -62,7 +59,8 @@ const int LightCount = 1;
const vec3 Fdielectric = vec3(0.04);
layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_BloomColor;
layout(location = 1) out vec4 o_MaterialInfo;
layout(location = 2) out vec4 o_BloomColor;
struct DirectionalLight {
vec3 Direction;
@ -100,7 +98,7 @@ in VertexOutput
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
vec4 FragPosLightSpace;
float ViewZ;
} vs_Input;
uniform DirectionalLight u_DirectionalLights;
@ -143,7 +141,15 @@ uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
// shadow
uniform sampler2D u_ShadowMap;
const int CSM_CASCADE_COUNT = 4;
uniform sampler2D u_ShadowMap[4];
uniform mat4 u_LightSpaceMatrix0;
uniform mat4 u_LightSpaceMatrix1;
uniform mat4 u_LightSpaceMatrix2;
uniform mat4 u_LightSpaceMatrix3;
uniform vec4 u_CascadeSplits;
uniform float u_ShadowFar;
uniform float u_ShadowNear;
uniform float u_ShadowBias;
uniform float u_ShadowSoftness;
uniform float u_ShadowIntensity;
@ -338,7 +344,7 @@ vec3 IBL(vec3 F0, vec3 Lr)
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
).rgb;
).rgb;
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
@ -347,23 +353,54 @@ vec3 IBL(vec3 F0, vec3 Lr)
}
// shadow
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
int selectCascade(float viewZ)
{
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
float depth = -viewZ;
float linearDepth = (depth - u_ShadowNear) / (u_ShadowFar - u_ShadowNear);
if (linearDepth < u_CascadeSplits.x) return 0;
if (linearDepth < u_CascadeSplits.y) return 1;
if (linearDepth < u_CascadeSplits.z) return 2;
return 3;
}
if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
mat4 getLightSpaceMatrix(int cascade)
{
if (cascade == 0) return u_LightSpaceMatrix0;
else if (cascade == 1) return u_LightSpaceMatrix1;
else if (cascade == 2) return u_LightSpaceMatrix2;
else return u_LightSpaceMatrix3;
}
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float getCascadeSplit(int cascade)
{
if (cascade == 0) return u_CascadeSplits.x;
else if (cascade == 1) return u_CascadeSplits.y;
else if (cascade == 2) return u_CascadeSplits.z;
else return u_CascadeSplits.w;
}
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
float sampleShadow(int cascade, vec2 uv, float compareDepth, float bias)
{
float depth;
if (cascade == 0) depth = texture(u_ShadowMap[0], uv).r;
else if (cascade == 1) depth = texture(u_ShadowMap[1], uv).r;
else if (cascade == 2) depth = texture(u_ShadowMap[2], uv).r;
else depth = texture(u_ShadowMap[3], uv).r;
return (compareDepth - bias) > depth ? 1.0 : 0.0;
}
vec2 getTexelSize(int cascade)
{
if (cascade == 0) return 1.0 / vec2(textureSize(u_ShadowMap[0], 0));
else if (cascade == 1) return 1.0 / vec2(textureSize(u_ShadowMap[1], 0));
else if (cascade == 2) return 1.0 / vec2(textureSize(u_ShadowMap[2], 0));
else return 1.0 / vec2(textureSize(u_ShadowMap[3], 0));
}
float pcfShadow(int cascade, vec3 projCoords, float bias)
{
vec2 texelSize = getTexelSize(cascade);
int pcfRange = clamp(int(u_ShadowSoftness), 0, 3);
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
float shadow = 0.0;
int samples = 0;
@ -372,34 +409,57 @@ float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
for (int y = -pcfRange; y <= pcfRange; ++y)
{
vec2 offset = vec2(x, y) * texelSize;
float pcfDepth = texture(u_ShadowMap, projCoords.xy + offset).r;
shadow += (currentDepth - bias) > pcfDepth ? 1.0 : 0.0;
shadow += sampleShadow(cascade, projCoords.xy + offset, projCoords.z, bias);
samples++;
}
}
shadow /= float(samples);
return shadow * u_ShadowIntensity;
return shadow / float(samples);
}
float ComputeShadow(vec4 fragPosLightSpace, float NdotL)
float calculateCSMShadow(vec3 worldPos, vec3 normal, vec3 lightDir, float viewZ)
{
if (u_ShadowEnabled == 0) return 1.0;
int cascade = selectCascade(viewZ);
vec4 fragPosLightSpace = getLightSpaceMatrix(cascade) * vec4(worldPos, 1.0);
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0) return 1.0;
if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
float shadow = pcfShadow(cascade, projCoords, bias);
float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5);
float cascadeEdge = 0.0;
if (cascade < CSM_CASCADE_COUNT - 1)
{
float depth = -viewZ;
float linearDepth = (depth - u_ShadowNear) / (u_ShadowFar - u_ShadowNear);
float splitDist = getCascadeSplit(cascade);
float prevSplit = (cascade > 0) ? getCascadeSplit(cascade - 1) : 0.0;
float blendStart = splitDist - (splitDist - prevSplit) * 0.1;
if (linearDepth > blendStart)
{
cascadeEdge = (linearDepth - blendStart) / (splitDist - blendStart);
float shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0;
return mix(1.0, 1.0 - u_ShadowIntensity, shadow);
int nextCascade = cascade + 1;
vec4 fragPosNext = getLightSpaceMatrix(nextCascade) * vec4(worldPos, 1.0);
vec3 projCoordsNext = fragPosNext.xyz / fragPosNext.w;
projCoordsNext = projCoordsNext * 0.5 + 0.5;
if (!(projCoordsNext.z > 1.0 || projCoordsNext.x < 0.0 || projCoordsNext.x > 1.0 ||
projCoordsNext.y < 0.0 || projCoordsNext.y > 1.0))
{
float nextShadow = pcfShadow(nextCascade, projCoordsNext, bias);
shadow = mix(shadow, nextShadow, cascadeEdge);
}
}
}
return shadow * u_ShadowIntensity;
}
@ -428,7 +488,7 @@ void main()
// Shadow
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0.5) {
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
float shadow = calculateCSMShadow(vs_Input.WorldPosition, m_Params.Normal, u_DirectionalLights.Direction, vs_Input.ViewZ);
shadowFactor = 1.0 - shadow;
}
@ -459,4 +519,6 @@ void main()
// Bloom
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
o_BloomColor = brightness > u_BloomThreshold ? color : vec4(0.0, 0.0, 0.0, 1.0);
o_MaterialInfo = vec4(m_Params.Metalness, m_Params.Roughness, 0.0, 1.0);
}