@@ -19,10 +19,13 @@ const float PI = 3.141592654;
1919const float PI_OVER_2 = PI / 2.0 ;
2020const float ATTENUATION_CONSTANT = 1.0 ;
2121const int LIGHTS_MAX = 64 ;
22- const int SHADOW_TEXTURES_MAX = 9 ;
23- const int SHADOW_MAPS_MAX = 9 ;
22+ const int SHADOW_TEXTURES_MAX = 8 ;
23+ const int SHADOW_MAPS_MAX = 8 ;
24+ const float SHADOW_DIRECTIONAL_SEAM_INSET = 0.05 ; // TODO: see if this should be proportionate to shadow texel size.
25+ const int SHADOW_CASCADES_MAX = 2 ;
26+ const int SHADOW_CASCADE_LEVELS = 3 ;
27+ const float SHADOW_CASCADE_SEAM_INSET = 0.001 ;
2428const float SHADOW_FOV_MAX = 2.1 ;
25- const float SHADOW_SEAM_INSET = 0.05 ; // TODO: see if this should be proportionate to shadow texel size.
2629
2730const vec4 SSVF_DITHERING[4 ] =
2831vec4 [](
@@ -55,6 +58,7 @@ uniform sampler2D subdermalPlusTexture;
5558uniform sampler2D scatterPlusTexture;
5659uniform sampler2D shadowTextures[SHADOW_TEXTURES_MAX];
5760uniform samplerCube shadowMaps[SHADOW_MAPS_MAX];
61+ uniform sampler2DArray shadowCascades[SHADOW_CASCADES_MAX];
5862uniform vec3 lightOrigins[LIGHTS_MAX];
5963uniform vec3 lightDirections[LIGHTS_MAX];
6064uniform vec3 lightColors[LIGHTS_MAX];
@@ -69,7 +73,7 @@ uniform int lightDesireFogs[LIGHTS_MAX];
6973uniform int lightShadowIndices[LIGHTS_MAX];
7074uniform int lightsCount;
7175uniform float shadowNear;
72- uniform mat4 shadowMatrices[SHADOW_TEXTURES_MAX];
76+ uniform mat4 shadowMatrices[SHADOW_TEXTURES_MAX + SHADOW_CASCADES_MAX * SHADOW_CASCADE_LEVELS ];
7377
7478in vec2 texCoordsOut;
7579
@@ -186,7 +190,7 @@ float fadeShadowScalar(vec2 shadowTexCoords, float shadowScalar)
186190 return 1.0 - (1.0 - shadowScalar) * (1.0 - fadeScalar);
187191}
188192
189- float computeShadowScalarPoint(vec4 position, vec3 lightOrigin, int shadowMapIndex )
193+ float computeShadowScalarPoint(vec4 position, vec3 lightOrigin, int shadowIndex )
190194{
191195 vec3 positionShadow = position.xyz - lightOrigin;
192196 float shadowZ = length (positionShadow);
@@ -198,7 +202,7 @@ float computeShadowScalarPoint(vec4 position, vec3 lightOrigin, int shadowMapInd
198202 for (int k = 0 ; k < lightShadowSamples; ++ k)
199203 {
200204 vec3 offset = (vec3 (i, j, k) - vec3 (lightShadowSamples / 2.0 )) * (lightShadowSampleScalar / lightShadowSamples);
201- shadowHits += shadowZ - lightShadowBias > texture(shadowMaps[shadowMapIndex ], positionShadow + offset).x ? 1.0 : 0.0 ;
205+ shadowHits += shadowZ - lightShadowBias > texture(shadowMaps[shadowIndex - SHADOW_TEXTURES_MAX ], positionShadow + offset).x ? 1.0 : 0.0 ;
202206 }
203207 }
204208 }
@@ -232,9 +236,9 @@ float computeShadowScalarDirectional(vec4 position, int shadowIndex)
232236 vec4 positionShadowClip = shadowMatrix * position;
233237 vec3 shadowTexCoordsProj = positionShadowClip.xyz / positionShadowClip.w;
234238 vec3 shadowTexCoords = shadowTexCoordsProj * 0.5 + 0.5 ;
235- if (shadowTexCoords.x > SHADOW_SEAM_INSET && shadowTexCoords.x < 1.0 - SHADOW_SEAM_INSET &&
236- shadowTexCoords.y > SHADOW_SEAM_INSET && shadowTexCoords.y < 1.0 - SHADOW_SEAM_INSET &&
237- shadowTexCoords.z > 0.5 + SHADOW_SEAM_INSET && shadowTexCoords.z < 1.0 - SHADOW_SEAM_INSET ) // TODO: figure out why shadowTexCoords.z range is 0.5 to 1.0.
239+ if (shadowTexCoords.x > SHADOW_DIRECTIONAL_SEAM_INSET && shadowTexCoords.x < 1.0 - SHADOW_DIRECTIONAL_SEAM_INSET &&
240+ shadowTexCoords.y > SHADOW_DIRECTIONAL_SEAM_INSET && shadowTexCoords.y < 1.0 - SHADOW_DIRECTIONAL_SEAM_INSET &&
241+ shadowTexCoords.z > 0.5 + SHADOW_DIRECTIONAL_SEAM_INSET && shadowTexCoords.z < 1.0 - SHADOW_DIRECTIONAL_SEAM_INSET ) // TODO: figure out why shadowTexCoords.z range is 0.5 to 1.0.
238242 {
239243 float shadowZ = shadowTexCoords.z;
240244 float shadowZExp = exp (- lightShadowExponent * shadowZ);
@@ -246,7 +250,30 @@ float computeShadowScalarDirectional(vec4 position, int shadowIndex)
246250 return 1.0 ;
247251}
248252
249- float geometryTravelPoint(vec4 position, int lightIndex, int shadowMapIndex)
253+ float computeShadowScalarCascaded(vec4 position, float shadowCutoff, int shadowIndex)
254+ {
255+ for (int i = 0 ; i < SHADOW_CASCADE_LEVELS; ++ i)
256+ {
257+ mat4 shadowMatrix = shadowMatrices[SHADOW_TEXTURES_MAX + (shadowIndex - SHADOW_TEXTURES_MAX) * SHADOW_CASCADE_LEVELS + i];
258+ vec4 positionShadowClip = shadowMatrix * position;
259+ vec3 shadowTexCoordsProj = positionShadowClip.xyz / positionShadowClip.w;
260+ vec3 shadowTexCoords = shadowTexCoordsProj * 0.5 + 0.5 ;
261+ if (shadowTexCoords.x > SHADOW_CASCADE_SEAM_INSET && shadowTexCoords.x < 1.0 - SHADOW_CASCADE_SEAM_INSET &&
262+ shadowTexCoords.y > SHADOW_CASCADE_SEAM_INSET && shadowTexCoords.y < 1.0 - SHADOW_CASCADE_SEAM_INSET &&
263+ shadowTexCoords.z > 0.5 + SHADOW_CASCADE_SEAM_INSET && shadowTexCoords.z < 1.0 - SHADOW_CASCADE_SEAM_INSET) // TODO: figure out why shadowTexCoords.z range is 0.5 to 1.0.
264+ {
265+ float shadowZ = shadowTexCoordsProj.z * 0.5 + 0.5 ;
266+ float shadowZExp = exp (- lightShadowExponent * shadowZ);
267+ float shadowDepthExp = texture(shadowCascades[shadowIndex - SHADOW_TEXTURES_MAX], vec3 (shadowTexCoords.xy, float (i))).y;
268+ float shadowScalar = clamp (shadowZExp * shadowDepthExp, 0.0 , 1.0 );
269+ shadowScalar = pow (shadowScalar, lightShadowDensity);
270+ return shadowScalar;
271+ }
272+ }
273+ return 1.0 ;
274+ }
275+
276+ float geometryTravelPoint(vec4 position, int lightIndex, int shadowIndex)
250277{
251278 // compute travel average in world space
252279 vec3 lightOrigin = lightOrigins[lightIndex];
@@ -260,7 +287,7 @@ float geometryTravelPoint(vec4 position, int lightIndex, int shadowMapIndex)
260287 for (int k = - 1 ; k <= 1 ; k += 2 )
261288 {
262289 vec3 offset = vec3 (i, j, k) * lightShadowSampleScalar;
263- float shadowDepth = texture(shadowMaps[shadowMapIndex ], positionShadow + offset).x;
290+ float shadowDepth = texture(shadowMaps[shadowIndex - SHADOW_TEXTURES_MAX ], positionShadow + offset).x;
264291 float delta = shadowZ - shadowDepth;
265292 travel += max (0.0 , delta);
266293 }
@@ -330,6 +357,34 @@ float geometryTravelDirectional(vec4 position, int lightIndex, int shadowIndex)
330357 return 1.0 ;
331358}
332359
360+ float geometryTravelCascaded(vec4 position, int lightIndex, int shadowIndex)
361+ {
362+ for (int i = 0 ; i < SHADOW_CASCADE_LEVELS; ++ i)
363+ {
364+ // attempt to compute travel average in view space
365+ mat4 shadowMatrix = shadowMatrices[SHADOW_TEXTURES_MAX + (shadowIndex - SHADOW_TEXTURES_MAX) * SHADOW_CASCADE_LEVELS + i];
366+ vec4 positionShadowClip = shadowMatrix * position;
367+ vec3 shadowTexCoordsProj = positionShadowClip.xyz / positionShadowClip.w; // ndc space
368+ vec3 shadowTexCoords = shadowTexCoordsProj * 0.5 + 0.5 ; // adj-ndc space
369+ if (shadowTexCoords.x > 0.0 && shadowTexCoords.x < 1.0 &&
370+ shadowTexCoords.y > 0.0 && shadowTexCoords.y < 1.0 &&
371+ shadowTexCoords.z > 0.5 && shadowTexCoords.z < 1.0 ) // TODO: figure out why shadowTexCoords.z range is 0.5 to 1.0.
372+ {
373+ // compute light distance travel through surface (not accounting for incidental surface concavity)
374+ float shadowZScreen = shadowTexCoords.z; // linear, screen space
375+ vec2 shadowTextureSize = textureSize(shadowCascades[shadowIndex - SHADOW_TEXTURES_MAX], 0 ).xy;
376+ vec2 shadowTexelSize = 1.0 / shadowTextureSize;
377+ float shadowDepthScreen = texture(shadowCascades[shadowIndex - SHADOW_TEXTURES_MAX], vec3 (shadowTexCoords.xy, float (i))).x; // linear, screen space
378+ float delta = shadowZScreen - shadowDepthScreen;
379+ float shadowFar = lightCutoffs[lightIndex];
380+ return max (0.0 , delta * shadowFar);
381+ }
382+ }
383+
384+ // tracing out of range, return default
385+ return 1.0 ;
386+ }
387+
333388vec3 computeSubsurfaceScatter(vec4 position, vec3 albedo, vec4 subdermalPlus, vec4 scatterPlus, float nDotL, vec2 texCoords, int lightIndex)
334389{
335390 // retrieve light and shadow values
@@ -343,14 +398,17 @@ vec3 computeSubsurfaceScatter(vec4 position, vec3 albedo, vec4 subdermalPlus, ve
343398 switch (lightType)
344399 {
345400 case 0 : // point light
346- travel = geometryTravelPoint(position, lightIndex, shadowIndex - SHADOW_TEXTURES_MAX );
401+ travel = geometryTravelPoint(position, lightIndex, shadowIndex);
347402 break ;
348403 case 1 : // spot light
349404 travel = geometryTravelSpot(position, lightIndex, shadowIndex);
350405 break ;
351- default : // directional light
406+ case 2 : // directional light
352407 travel = geometryTravelDirectional(position, lightIndex, shadowIndex);
353408 break ;
409+ default : // cascaded light
410+ travel = geometryTravelCascaded(position, lightIndex, shadowIndex);
411+ break ;
354412 }
355413 }
356414
@@ -601,6 +659,66 @@ vec3 computeFogAccumDirectional(vec4 position, int lightIndex)
601659 return result;
602660}
603661
662+ vec3 computeFogAccumCascaded(vec4 position, int lightIndex)
663+ {
664+ vec3 result = vec3 (0.0 );
665+ int shadowIndex = lightShadowIndices[lightIndex];
666+ if (shadowIndex >= 0 )
667+ {
668+ // grab light values
669+ vec3 lightOrigin = lightOrigins[lightIndex];
670+ vec3 lightDirection = lightDirections[lightIndex];
671+
672+ // compute ray info
673+ vec3 startPosition = eyeCenter;
674+ vec3 rayVector = position.xyz - startPosition;
675+ float rayLength = length (rayVector);
676+ vec3 rayDirection = rayVector / rayLength;
677+
678+ // compute step info
679+ float stepLength = rayLength / ssvfSteps;
680+ vec3 step = rayDirection * stepLength;
681+
682+ // compute light view term
683+ float theta = dot (- rayDirection, lightDirection);
684+
685+ // compute dithering
686+ float dithering = SSVF_DITHERING[int (gl_FragCoord .x) % 4 ][int (gl_FragCoord .y) % 4 ];
687+
688+ // march over ray, accumulating fog light value
689+ vec3 currentPosition = startPosition + step * dithering;
690+ for (int i = 0 ; i < ssvfSteps; ++ i)
691+ {
692+ // use the nearest available cascade for this step
693+ for (int j = 0 ; j < SHADOW_CASCADE_LEVELS; ++ j)
694+ {
695+ // compute depths
696+ mat4 shadowMatrix = shadowMatrices[SHADOW_TEXTURES_MAX + (shadowIndex - SHADOW_TEXTURES_MAX) * SHADOW_CASCADE_LEVELS + j];
697+ vec4 positionShadowClip = shadowMatrix * vec4 (currentPosition, 1.0 );
698+ vec3 shadowTexCoordsProj = positionShadowClip.xyz / positionShadowClip.w;
699+ vec3 shadowTexCoords = shadowTexCoordsProj * 0.5 + 0.5 ;
700+ bool shadowTexCoordsInRange = shadowTexCoords.x >= 0.0 && shadowTexCoords.x < 1.0 && shadowTexCoords.y >= 0.0 && shadowTexCoords.y < 1.0 ;
701+ float shadowZ = shadowTexCoords.z;
702+ float shadowDepth = shadowTexCoordsInRange ? texture(shadowCascades[shadowIndex - SHADOW_TEXTURES_MAX], vec3 (shadowTexCoords.xy, float (i))).x : 1.0 ;
703+
704+ // step through ray, accumulating fog light moment
705+ if (shadowZ <= shadowDepth || shadowZ >= 1 .0f)
706+ {
707+ // mie scaterring approximated with Henyey-Greenstein phase function
708+ float asymmetrySquared = ssvfAsymmetry * ssvfAsymmetry;
709+ float fogMoment = (1.0 - asymmetrySquared) / (4.0 * PI * pow (1.0 + asymmetrySquared - 2.0 * ssvfAsymmetry * theta, 1.5 ));
710+ result += fogMoment;
711+ }
712+ }
713+
714+ // step
715+ currentPosition += step ;
716+ }
717+ result = smoothstep (0.0 , 1.0 , result / (ssvfSteps * SHADOW_CASCADE_LEVELS)) * lightColors[lightIndex] * lightBrightnesses[lightIndex] * ssvfIntensity;
718+ }
719+ return result;
720+ }
721+
604722void main()
605723{
606724 // ensure fragment was written
@@ -634,19 +752,20 @@ void main()
634752 {
635753 // per-light radiance
636754 vec3 lightOrigin = lightOrigins[i];
755+ float lightCutoff = lightCutoffs[i];
637756 int lightType = lightTypes[i];
638757 bool lightDirectional = lightType == 2 ;
758+ bool lightCascaded = lightType == 3 ;
639759 vec3 l, h, radiance;
640760 float intensity = 0.0 ;
641- if (! lightDirectional)
761+ if (! lightDirectional && ! lightCascaded )
642762 {
643763 vec3 d = lightOrigin - position.xyz;
644764 l = normalize (d);
645765 h = normalize (v + l);
646766 float distanceSquared = dot (d, d);
647767 float distance = sqrt (distanceSquared);
648- float cutoff = lightCutoffs[i];
649- float cutoffScalar = 1.0 - smoothstep (cutoff * (1.0 - lightCutoffMargin), cutoff, distance );
768+ float cutoffScalar = 1.0 - smoothstep (lightCutoff * (1.0 - lightCutoffMargin), lightCutoff, distance );
650769 float attenuation = 1.0 / (ATTENUATION_CONSTANT + lightAttenuationLinears[i] * distance + lightAttenuationQuadratics[i] * distanceSquared);
651770 float angle = acos (dot (l, - lightDirections[i]));
652771 float halfConeInner = lightConeInners[i] * 0.5 ;
@@ -672,9 +791,10 @@ void main()
672791 {
673792 switch (lightType)
674793 {
675- case 0 : { shadowScalar = computeShadowScalarPoint(position, lightOrigin, shadowIndex - SHADOW_TEXTURES_MAX ); break ; } // point
794+ case 0 : { shadowScalar = computeShadowScalarPoint(position, lightOrigin, shadowIndex); break ; } // point
676795 case 1 : { shadowScalar = computeShadowScalarSpot(position, lightConeOuters[i], shadowIndex); break ; } // spot
677- default : { shadowScalar = computeShadowScalarDirectional(position, shadowIndex); break ; } // directional
796+ case 2 : { shadowScalar = computeShadowScalarDirectional(position, shadowIndex); break ; } // directional
797+ default : { shadowScalar = computeShadowScalarCascaded(position, lightCutoff, shadowIndex); break ; } // cascaded
678798 }
679799 }
680800
@@ -713,7 +833,8 @@ void main()
713833 {
714834 case 0 : { fogAccum.rgb += computeFogAccumPoint(position, i); break ; } // point
715835 case 1 : { fogAccum.rgb += computeFogAccumSpot(position, i); break ; } // spot
716- default : { fogAccum.rgb += computeFogAccumDirectional(position, i); break ; } // directional
836+ case 2 : { fogAccum.rgb += computeFogAccumDirectional(position, i); break ; } // directional
837+ default : { fogAccum.rgb += computeFogAccumCascaded(position, i); break ; } // cascaded
717838 }
718839 }
719840 }
0 commit comments