2021-01-10 22:20:35 +03:00
|
|
|
$input v_texcoord0
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright 2021 elven cache. All rights reserved.
|
2022-01-15 22:59:06 +03:00
|
|
|
* License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
|
2021-01-10 22:20:35 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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);
|
|
|
|
}
|