bgfx/examples/44-sss/fs_screen_space_shadows.sc

112 lines
3.0 KiB
Scala

$input v_texcoord0
/*
* Copyright 2021 elven cache. All rights reserved.
* License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
*/
#include "../common/common.sh"
#include "parameters.sh"
SAMPLER2D(s_depth, 0);
#define DEPTH_EPSILON 1e-4
// from assao sample, cs_assao_prepare_depths.sc
vec3 NDCToViewspace( vec2 pos, float viewspaceDepth )
{
vec3 ret;
ret.xy = (u_ndcToViewMul * pos.xy + u_ndcToViewAdd) * viewspaceDepth;
ret.z = viewspaceDepth;
return ret;
}
float ShadertoyNoise (vec2 uv) {
return fract(sin(dot(uv.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
void main()
{
vec2 texCoord = v_texcoord0;
float linearDepth = texture2D(s_depth, texCoord).x;
vec3 viewSpacePosition = NDCToViewspace(texCoord, linearDepth);
vec3 lightStep = normalize(u_lightPosition - viewSpacePosition);
// screen space radius not usable directly. convert value given in pixels,
// to world units. this is important later when comparing depth in world units
float radius = u_shadowRadius;
if (0.0 < u_useScreenSpaceRadius)
{
// is there a better way to do this calculation?
float radiusTexCoordX = u_shadowRadius * u_viewTexel.x + texCoord.x;
float radiusPositionX = u_ndcToViewMul.x * radiusTexCoordX + u_ndcToViewAdd.x;
radius = abs(radiusPositionX * linearDepth - viewSpacePosition.x);
}
lightStep *= (radius / u_shadowSteps);
vec3 samplePosition = viewSpacePosition;
float random = ShadertoyNoise(gl_FragCoord.xy + vec2(314.0, 159.0)*u_frameIdx);
float initialOffset = (0.0 < u_useNoiseOffset) ? (0.5+random) : 1.0;
samplePosition += initialOffset * lightStep;
mat4 viewToProj = mat4(
u_viewToProj0,
u_viewToProj1,
u_viewToProj2,
u_viewToProj3
);
float occluded = 0.0;
float softOccluded = 0.0;
float firstHit = u_shadowSteps;
for (int i = 0; i < int(u_shadowSteps); ++i, samplePosition += lightStep)
{
vec3 psSamplePosition = instMul(viewToProj, vec4(samplePosition, 1.0)).xyw;
psSamplePosition.xy *= (1.0/psSamplePosition.z);
vec2 sampleCoord = psSamplePosition.xy * 0.5 + 0.5;
sampleCoord.y = 1.0 - sampleCoord.y;
// using texture2Dlod because dx9 compiler doesn't like
// gradient instructions within this loop
float sampleDepth = texture2DLod(s_depth, sampleCoord, 0).x;
float delta = (samplePosition.z - sampleDepth);
if (DEPTH_EPSILON < delta && delta < radius)
{
firstHit = min(firstHit, float(i));
// for hard, soft occlusion
occluded += 1.0;
// for very soft occlusion
softOccluded += saturate(radius - delta);
}
}
float shadow;
if (1.5 < u_contactShadowsMode)
{
// very soft occlusion, includes distance falloff above
shadow = softOccluded * (1.0 - (firstHit / u_shadowSteps));
shadow = 1.0 - saturate(shadow);
shadow = shadow*shadow;
}
else if (0.5 < u_contactShadowsMode)
{
// soft occlusion
shadow = occluded * (1.0 - (firstHit / u_shadowSteps));
shadow = 1.0 - saturate(shadow);
shadow = shadow*shadow;
}
else // == 0
{
// hard occlusion
shadow = 0.0 < occluded ? 0.0 : 1.0;
}
gl_FragColor = vec4_splat(shadow);
}