mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 16:36:15 +03:00
261 lines
9.2 KiB
GLSL
261 lines
9.2 KiB
GLSL
#version 460
|
|
|
|
/*
|
|
** Physical based render code, develop by engineer: qiutanguu.
|
|
*/
|
|
|
|
#extension GL_EXT_samplerless_texture_functions : enable
|
|
#extension GL_GOOGLE_include_directive : enable
|
|
#extension GL_KHR_shader_subgroup_arithmetic : enable
|
|
#extension GL_KHR_shader_subgroup_basic : enable
|
|
|
|
#include "SDSM_Common.glsl"
|
|
#include "RayCommon.glsl"
|
|
#include "PoissonDisk.glsl"
|
|
|
|
// Current don't use depth gather. Fetch is enough.
|
|
#define SHADOW_DEPTH_GATHER 0
|
|
#include "ShadowCommon.glsl"
|
|
|
|
layout (set = 1, binding = 0) uniform UniformView { ViewData viewData; };
|
|
layout (set = 2, binding = 0) uniform UniformFrame { FrameData frameData; };
|
|
|
|
#define COMMON_SAMPLER_SET 3
|
|
#include "CommonSamplerSet.glsl"
|
|
|
|
#define BLUE_NOISE_TEXTURE_SET 4
|
|
#define BLUE_NOISE_BUFFER_SET 5
|
|
#include "BlueNoiseCommon.glsl"
|
|
|
|
// We use blue noise offset sample position.
|
|
#define BLUE_NOISE_OFFSET 1
|
|
|
|
#if BLUE_NOISE_OFFSET
|
|
// 8 tap taa blue noise. maybe 4 or 2 is enough.
|
|
const uint kShadowSampleCount = 8;
|
|
#else
|
|
// 12 tap poisson disk.
|
|
const uint kShadowSampleCount = 12;
|
|
#define poissonDisk kPoissonDisk_12
|
|
#endif
|
|
|
|
float shadowPcf(
|
|
texture2D shadowDpeth,
|
|
in const DirectionalLightInfo light,
|
|
vec3 shadowCoord,
|
|
vec2 texelSize,
|
|
uint cascadeId,
|
|
float perCascadeEdge,
|
|
vec2 screenPos,
|
|
ivec2 colorSize)
|
|
{
|
|
const float compareDepth = shadowCoord.z;
|
|
|
|
vec2 scaleRange = 1.0f - 1.0f / cascadeInfos[0].cascadeScale.xy;
|
|
scaleRange = (cascadeInfos[cascadeId].cascadeScale.xy / cascadeInfos[0].cascadeScale.xy - 1.0f / cascadeInfos[0].cascadeScale.xy) / scaleRange;
|
|
|
|
vec2 filterSize = smoothstep(0.0f, 1.0f, scaleRange) * (light.maxFilterSize - light.shadowFilterSize) + light.shadowFilterSize;
|
|
|
|
// When cacade increment, shadow map texel mapping size also increase.
|
|
// We need to reduce soft shadow size to keep shading result same.
|
|
const vec2 scaleOffset = texelSize * filterSize;
|
|
|
|
float occluders = 0.0;
|
|
float occluderDistSum = 0.0;
|
|
|
|
// Offset retarget for new seeds each frame
|
|
uvec2 offset = uvec2(vec2(0.754877669, 0.569840296) * (frameData.frameIndex.x % frameData.jitterPeriod) * uvec2(colorSize));
|
|
uvec2 offsetId = uvec2(screenPos) + offset;
|
|
offsetId.x = offsetId.x % colorSize.x;
|
|
offsetId.y = offsetId.y % colorSize.y;
|
|
|
|
float taaOffset = interleavedGradientNoise(screenPos, frameData.frameIndex.x % frameData.jitterPeriod);
|
|
float taaAngle = taaOffset * 3.14159265359 * 2.0f;
|
|
|
|
|
|
for (uint i = 0; i < kShadowSampleCount; i++)
|
|
{
|
|
#if BLUE_NOISE_OFFSET
|
|
vec2 offsetUv;
|
|
offsetUv.x = -1.0 + 2.0 * samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d(offsetId.x, offsetId.y, i, 0u);
|
|
offsetUv.y = -1.0 + 2.0 * samplerBlueNoiseErrorDistribution_128x128_OptimizedFor_2d2d2d2d(offsetId.x, offsetId.y, i, 1u);
|
|
offsetUv *= scaleOffset;
|
|
#else
|
|
float s = sin(taaAngle);
|
|
float c = cos(taaAngle);
|
|
vec2 offsetUv = scaleOffset * vec2(
|
|
poissonDisk[i].x * c + poissonDisk[i].y * s,
|
|
poissonDisk[i].x * -s + poissonDisk[i].y * c);
|
|
#endif
|
|
|
|
// Build sample uv.
|
|
vec2 sampleUv = shadowCoord.xy + offsetUv;
|
|
sampleUv.x = clamp(sampleUv.x, perCascadeEdge * cascadeId, perCascadeEdge * (cascadeId + 1));
|
|
sampleUv.y = clamp(sampleUv.y, 0.0f, 1.0f);
|
|
|
|
#if SHADOW_DEPTH_GATHER
|
|
vec4 depths = textureGather(sampler2D(shadowDpeth, pointClampEdgeSampler), sampleUv, 0);
|
|
for(uint j = 0; j < 4; j ++)
|
|
{
|
|
float dist = depths[j] - compareDepth;
|
|
float occluder = step(0.0, dist); // reverse z.
|
|
|
|
// Collect occluders.
|
|
occluders += occluder;
|
|
occluderDistSum += dist * occluder;
|
|
}
|
|
#else
|
|
float depthShadow = texture(sampler2D(shadowDpeth, pointClampEdgeSampler), sampleUv).r;
|
|
{
|
|
float dist = depthShadow - compareDepth;
|
|
float occluder = step(0.0, dist); // reverse z.
|
|
|
|
// Collect occluders.
|
|
occluders += occluder;
|
|
occluderDistSum += dist * occluder;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return contactHardenPCFKernal(light, occluders, occluderDistSum, compareDepth, kShadowSampleCount);
|
|
}
|
|
|
|
|
|
|
|
layout(local_size_x = 8, local_size_y = 8) in;
|
|
void main()
|
|
{
|
|
ivec2 depthSize = textureSize(inDepth, 0);
|
|
|
|
uvec2 groupThreadId = remap8x8(gl_LocalInvocationIndex);
|
|
uvec2 dispatchId = groupThreadId + gl_WorkGroupID.xy * 8;
|
|
ivec2 workPos = ivec2(dispatchId);
|
|
|
|
if(workPos.x >= depthSize.x || workPos.y >= depthSize.y)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Non shadow-area pre-return.
|
|
if(!isShadingModelValid(texelFetch(inGbufferA, workPos, 0).a))
|
|
{
|
|
imageStore(imageShadowMask, workPos, vec4(1.0f));
|
|
return;
|
|
}
|
|
|
|
const vec2 uv = (vec2(workPos) + vec2(0.5f)) / vec2(depthSize);
|
|
|
|
// Evaluate soft shadow.
|
|
const vec4 inGbufferBValue = texelFetch(inGbufferB, workPos, 0);
|
|
vec3 N = normalize(inGbufferBValue.rgb);
|
|
|
|
const float deviceZ = texelFetch(inDepth, workPos, 0).r;
|
|
vec3 worldPos = getWorldPos(uv, deviceZ, viewData);
|
|
|
|
const DirectionalLightInfo light = frameData.directionalLight;
|
|
float safeNoL = clamp(dot(N, normalize(-light.direction)), 0.0, 1.0);
|
|
|
|
// First find active cascade.
|
|
uint activeCascadeId = 0;
|
|
vec3 shadowCoord;
|
|
|
|
// Loop to find suitable cascade.
|
|
for(uint cascadeId = 0; cascadeId < light.cascadeCount; cascadeId ++)
|
|
{
|
|
// Perspective divide to get ndc position.
|
|
shadowCoord = projectPos(worldPos, cascadeInfos[cascadeId].viewProj);
|
|
|
|
// Check current cascade is valid in range.
|
|
if(onRange(shadowCoord.xyz, vec3(light.cascadeBorderAdopt), vec3(1.0f - light.cascadeBorderAdopt)))
|
|
{
|
|
break;
|
|
}
|
|
activeCascadeId ++;
|
|
}
|
|
|
|
// Out of shadow area return lit.
|
|
if(activeCascadeId == light.cascadeCount)
|
|
{
|
|
imageStore(imageShadowMask, workPos, vec4(1.0f));
|
|
return;
|
|
}
|
|
|
|
const float shadowTexelSize = 1.0f / float(light.perCascadeXYDim);
|
|
const vec3 offsetPos = biasNormalOffset(N, safeNoL, shadowTexelSize); // Offset position align normal direction.
|
|
const float perCascadeOffsetUV = 1.0f / light.cascadeCount;
|
|
|
|
// Final shadow result.
|
|
float shadowResult = 1.0f;
|
|
|
|
// Main cascsade shadow compute.
|
|
{
|
|
vec3 shadowPosOnAltas = projectPos(worldPos + offsetPos, cascadeInfos[activeCascadeId].viewProj);
|
|
|
|
// Also add altas bias.
|
|
shadowPosOnAltas.x = (shadowPosOnAltas.x + float(activeCascadeId)) * perCascadeOffsetUV;
|
|
|
|
// Apply shadow depth bias.
|
|
shadowPosOnAltas.z += autoBias(safeNoL, activeCascadeId + 1.0f);
|
|
|
|
// Final evaluate shadow.
|
|
shadowResult = shadowPcf(inSDSMShadowDepth, light, shadowPosOnAltas, vec2(shadowTexelSize), activeCascadeId, perCascadeOffsetUV, vec2(workPos), depthSize);
|
|
}
|
|
|
|
// Cascade edge mix.
|
|
const vec2 ndcPosAbs = abs(shadowCoord.xy);
|
|
float cascadeFadeEdge = (max(ndcPosAbs.x, ndcPosAbs.y) - light.cascadeEdgeLerpThreshold) * 4.0f;
|
|
if(cascadeFadeEdge > 0.0f && activeCascadeId < light.cascadeCount - 1)
|
|
{
|
|
// Mix to next cascade.
|
|
const uint lerpCascadeId = activeCascadeId + 1;
|
|
|
|
// Project to next cascasde position.
|
|
vec4 lerpShadowProjPos = cascadeInfos[lerpCascadeId].viewProj * vec4(worldPos + offsetPos, 1.0f);
|
|
lerpShadowProjPos.xyz /= lerpShadowProjPos.w;
|
|
|
|
// Clamp to [0,1]
|
|
lerpShadowProjPos.xy = lerpShadowProjPos.xy * 0.5f + 0.5f;
|
|
lerpShadowProjPos.y = 1.0f - lerpShadowProjPos.y;
|
|
|
|
// Altas bias.
|
|
lerpShadowProjPos.x = (lerpShadowProjPos.x + float(lerpCascadeId)) * perCascadeOffsetUV;
|
|
|
|
// Shadow depth bias.
|
|
lerpShadowProjPos.z += autoBias(safeNoL, lerpCascadeId + 1.0f);
|
|
|
|
// Evaluate next cascade shadow value.
|
|
float lerpShadowValue = shadowPcf(inSDSMShadowDepth, light, lerpShadowProjPos.xyz, vec2(shadowTexelSize), lerpCascadeId, perCascadeOffsetUV, vec2(workPos), depthSize);
|
|
|
|
// Mix shadow.
|
|
cascadeFadeEdge = smoothstep(0.0f, 1.0f, cascadeFadeEdge);
|
|
shadowResult = mix(shadowResult, lerpShadowValue, cascadeFadeEdge);
|
|
}
|
|
|
|
// SDSM keep high accurate shadow when camera move near, and may see some visual artifact which cause by contact shadow.
|
|
// So current don't need this tech here.
|
|
#if 0
|
|
// Maybe we need these tech in the future, so keep here as one reference.
|
|
// Do screen space ray trace shadow to fill depth bias leaking problem.
|
|
// Note from: https://panoskarabelas.com/posts/screen_space_shadows/
|
|
// Note from: Unreal engine4 contact shadow.
|
|
const bool bShouldRayTraceShadow = shadowResult > 1e-3f;
|
|
if(bShouldRayTraceShadow)
|
|
{
|
|
float rayTraceShadow = 1.0f - screenSpaceContactShadow(
|
|
inDepth,
|
|
pointClampEdgeSampler,
|
|
viewData,
|
|
interleavedGradientNoise(vec2(workPos), frameData.frameIndex.x % frameData.jitterPeriod)
|
|
, 8
|
|
, worldPos
|
|
, normalize(-light.direction)
|
|
, 0.25
|
|
);
|
|
|
|
shadowResult = min(rayTraceShadow, shadowResult);
|
|
}
|
|
#endif
|
|
|
|
imageStore(imageShadowMask, workPos, vec4(shadowResult));
|
|
} |