#ifndef SLB_SHADOW_H #define SLB_SHADOW_H #include "slb_common.h" #include "slb_shadow_settings.h" #ifdef SLB_GLSL /// As always dummy float SLB_Shadow_Load(float2 uv) { return texelFetch(s_smap, int2(frac(uv)*float(SMAP_size)), 0).x; } float SLB_Shadow_LoadOffset(float2 uv, int2 offset) { return texelFetch(s_smap, int2(frac(uv)*float(SMAP_size)) + offset, 0).x; } float SLB_Shadow_SampleCmp(float2 uv, float z) { return textureLod(s_smap, uv, 0.0).x; } float4 SLB_Shadow_Gather(float2 uv) { return textureGather(s_smap, uv); } float4 SLB_Shadow_GatherOffset(float2 uv, int2 offset) { return textureGatherOffset(s_smap, uv, offset); } float4 SLB_Shadow_GatherCmp(float2 uv, float z) { return textureGather(s_smap, uv); } #else float SLB_Shadow_Load(float2 uv) { return s_smap.Load(int3(uv*float(SMAP_size) - 0.25, 0)); } float SLB_Shadow_LoadOffset(float2 uv, int2 offset) { return s_smap.Load(int3(uv*float(SMAP_size) - 0.25, 0), offset); } float SLB_Shadow_SampleCmp(float2 uv, float z) { return s_smap.SampleCmpLevelZero(smp_smap, uv, z).x; } float4 SLB_Shadow_Gather(float2 uv) { return s_smap.Gather(smp_nofilter, uv); } float4 SLB_Shadow_GatherOffset(float2 uv, int2 offset) { return s_smap.Gather(smp_nofilter, uv, offset); } float4 SLB_Shadow_GatherCmp(float2 uv, float z) { return s_smap.GatherCmp(smp_smap, uv, z); } #endif float SLB_Shadow_Linear(float4 tc) { return SLB_Shadow_SampleCmp(tc.xy, tc.z); } /// Taken from https://www.shadertoy.com/view/XsfGDn float SLB_Shadow_Nice(float4 tc) { float2 uv = tc.xy; float2 textureResolution = float2(float(SMAP_size), float(SMAP_size)); uv = uv*textureResolution + 0.5; float2 iuv = floor(uv); float2 fuv = frac(uv); uv = iuv + fuv*fuv*(3.0-2.0*fuv); uv = (uv - 0.5)/textureResolution; return SLB_Shadow_SampleCmp(uv, tc.z); } /// Taken from http://www.java-gaming.org/index.php?topic=35123.0 float SLB_Shadow_Bicubic(float4 tc) { float2 texCoords = tc.xy; float2 texSize = float2(float(SMAP_size), float(SMAP_size)); float2 invTexSize = 1.0 / texSize; texCoords = texCoords * texSize - 0.5; float2 fxy = frac(texCoords); texCoords -= fxy; float4 xcubic = SLB_Cubic_For_Bicubic(fxy.x); float4 ycubic = SLB_Cubic_For_Bicubic(fxy.y); float4 c = texCoords.xxyy + float2(-0.5, +1.5).xyxy; float4 s = float4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); float4 offset = c + float4(xcubic.yw, ycubic.yw) / s; offset *= invTexSize.xxyy; float sample0 = SLB_Shadow_SampleCmp(offset.xz, tc.z); float sample1 = SLB_Shadow_SampleCmp(offset.yz, tc.z); float sample2 = SLB_Shadow_SampleCmp(offset.xw, tc.z); float sample3 = SLB_Shadow_SampleCmp(offset.yw, tc.z); float sx = s.x / (s.x + s.y); float sy = s.z / (s.z + s.w); return lerp(lerp(sample3, sample2, sx), lerp(sample1, sample0, sx), sy); } #if 0 #include "slb_shadow_normal.h" #endif #include "slb_shadow_optimized_pcf.h" float SLB_Shadow_CHS(float4 tc, float noise) { float light_width = slb_shadow_settings_pcss_light_width; float average_blocker_depth = 0.0; { float average_blocker_div = 1e-6; float blocker_count = 0.0; int blocker_count_max = 0; for(int i = -3; i <= 3; i += 2) { for(int j = -3; j <= 3; j += 2) { float4 gather = SLB_Shadow_GatherOffset(tc.xy, int2(i, j)); float4 weight = max(float4(0.0,0.0,0.0,0.0), tc.z - gather); weight /= weight + 0.025; #ifdef SLB_GLSL weight = 1.0 - gather; #endif average_blocker_depth += dot(gather, weight); average_blocker_div += dot(weight, float4(1.0, 1.0, 1.0, 1.0)); blocker_count += float(weight.x > 1e-6) + float(weight.y > 1e-6) + float(weight.z > 1e-6) + float(weight.w > 1e-6); blocker_count_max += 4; } } SLB_BRANCH if(blocker_count < 0.5 || blocker_count > float(blocker_count_max) - 0.5) { #if 1 /// Toggles debug return 1.0 - blocker_count/float(blocker_count_max); #else return 0.0; } else { return 100.0; #endif } average_blocker_depth /= average_blocker_div; } float penumbra_size = light_width * (tc.z - average_blocker_depth)/average_blocker_depth; float lightness = SLB_Shadow_OptimizedPCF(tc); float scurve = clamp(3.0/float(SMAP_size)/penumbra_size, 1.0, 100.0); lightness = saturate((lightness*scurve + 0.5) - 0.5*scurve); return lightness; } float SLB_Shadow_VogelFilter(float4 tc, float noise, float filter_size, float filter_size_real, const int filter_samples, bool linear_sample, float radial_bias) { const float compare_window = 0.0001; float lightness = 0.0; float div = 1e-6; /// Migitate division by zero if(linear_sample) { float2x2 rot_mat = SLB_Rotate2Matrix(noise*SLB_TAU); SLB_UNROLL(filter_samples) for(int i = 0; i < filter_samples; i++) { float2 offset = SLB_VogelDisk_Sample(i, filter_samples); float offset_length = length(offset); offset = mul(offset, rot_mat); offset *= filter_size; float biased_z = tc.z - radial_bias*offset_length*filter_size_real; float2 uv = tc.xy + offset; float notblocker; { float4 depths = SLB_Shadow_Gather(uv); float4 notblockers = saturate((depths - biased_z + compare_window)/compare_window); #ifdef SLB_GLSL notblockers = depths; #endif float s1 = notblockers.x; float s2 = notblockers.y; float s3 = notblockers.z; float s4 = notblockers.w; float2 fract = frac(uv*float(SMAP_size)+0.5); notblocker = lerp(lerp(s4, s3, fract.x), lerp(s1, s2, fract.x), fract.y); lightness += notblocker; div += 1.0; } } } else { float2x2 rot_mat = SLB_Rotate2Matrix(noise*SLB_TAU); SLB_UNROLL(filter_samples) for(int i = 0; i < filter_samples; i++) { float2 offset = SLB_VogelDisk_Sample(i, filter_samples); float offset_length = length(offset); offset = mul(offset, rot_mat); offset *= filter_size; float biased_z = tc.z - radial_bias*offset_length*filter_size_real; float2 uv = tc.xy + offset; float depth = SLB_Shadow_Load(uv); float notblocker = saturate((depth - biased_z)/compare_window); float weight = float(depth > 1e-6); #ifdef SLB_GLSL notblocker = depth; weight = 1.0; #endif lightness += notblocker * weight; div += weight; } } lightness /= div; return lightness; } float SLB_Shadow_PCF(float4 tc, float noise) { SLB_STATIC_CONST int filter_samples = max(1, int(slb_shadow_settings_pcss_filter_samples*slb_shadow_pcss_filter_samples_multiplier)); float pcss_size_min = slb_shadow_settings_pcss_size_min / float(SMAP_size) * slb_shadow_pcss_size_min_multiplier; float filter_size = pcss_size_min; #if defined(SLB_SHADOW_SPOT) SLB_STATIC_CONST float radial_bias = 2.0; #elif defined(SLB_SHADOW_OMNI) SLB_STATIC_CONST float radial_bias = 1.0; #else SLB_STATIC_CONST float radial_bias = 0.3; #endif float lightness = SLB_Shadow_VogelFilter(tc, noise, filter_size, filter_size/2.0, filter_samples, true, radial_bias); lightness = SLB_QuarticRational1D(lightness); return lightness; } float SLB_Shadow_VogelCHS(float4 tc, float noise) { SLB_STATIC_CONST int filter_samples = max(1, int(slb_shadow_settings_pcss_filter_samples*slb_shadow_pcss_filter_samples_multiplier)); SLB_STATIC_CONST float light_width = slb_shadow_settings_pcss_light_width; float pcss_size_min = slb_shadow_settings_pcss_size_min / float(SMAP_size) * slb_shadow_pcss_size_min_multiplier; float filter_size = pcss_size_min; SLB_STATIC_CONST int biases = 4; const float compare_window = 0.0001; const float filter_bias[biases] = {0.0, 0.1, 0.2, 0.3}; float average_blocker_depth = 0.0; float average_blocker_div = 1e-6; /// Migitate division by zero float lightnesses[biases] = {0.0, 0.0, 0.0, 0.0}; float lightnesses_div[biases] = {1e-6, 1e-6, 1e-6, 1e-6}; /// Migitate division by zero float2x2 rot_mat = SLB_Rotate2Matrix(noise*SLB_TAU); SLB_UNROLL(filter_samples) for(int i = 0; i < filter_samples; i++) { float2 offset = SLB_VogelDisk_Sample(i, filter_samples); float offset_length = length(offset); offset = mul(offset, rot_mat); offset *= filter_size; float2 uv = tc.xy + offset; float4 depths = SLB_Shadow_Gather(uv); SLB_UNROLL(biases) for(int j = 0; j < biases; j++) { float biased_z = tc.z - filter_bias[j]*offset_length*filter_size; { float4 notblockers = saturate((depths - biased_z + compare_window)/compare_window); #ifdef SLB_GLSL notblockers = depths; #endif float s1 = notblockers.x; float s2 = notblockers.y; float s3 = notblockers.z; float s4 = notblockers.w; float2 fract = frac(uv*float(SMAP_size)+0.5); float notblocker = lerp(lerp(s4, s3, fract.x), lerp(s1, s2, fract.x), fract.y); lightnesses[j] += notblocker; lightnesses_div[j] += 1.0; } { float4 blockers = float4(float(biased_z - 0.0001*float(j-1) > depths.x), float(biased_z - 0.0001*float(j-1) > depths.y), float(biased_z - 0.0001*float(j-1) > depths.z), float(biased_z - 0.0001*float(j-1) > depths.w)); average_blocker_depth += dot(depths, blockers); average_blocker_div += dot(float4(1.0,1.0,1.0,1.0), blockers); } } } SLB_UNROLL(biases) for(int i = 0; i < biases; i++) { lightnesses[i] /= lightnesses_div[i]; } average_blocker_depth /= average_blocker_div; float penumbra_size = light_width * (tc.z - average_blocker_depth)/average_blocker_depth; /// Choose bias float lightness = lightnesses[0]; SLB_UNROLL(biases-1) for(int i = 1; i < biases; i++) { if(penumbra_size > float(i)/float(biases-1)*filter_size) { lightness = lightnesses[i]; } } float scurve = clamp(filter_size/penumbra_size, 1.0, 2.0*float(filter_samples)/(filter_size*float(SMAP_size))); lightness = saturate((lightness*scurve + 0.5) - 0.5*scurve); return lightness; } float SLB_Shadow_PCSS(float4 tc, float noise) { /// Settings SLB_STATIC_CONST int blocker_samples = max(1, int(slb_shadow_settings_pcss_blocker_samples * slb_shadow_pcss_blocker_samples_multiplier)); SLB_STATIC_CONST int filter_samples = max(1, int(slb_shadow_settings_pcss_filter_samples * slb_shadow_pcss_filter_samples_multiplier)); SLB_STATIC_CONST int filter_small_samples = max(1, int(slb_shadow_settings_pcss_filter_small_samples * slb_shadow_pcss_filter_small_samples_multiplier)); float pcss_size = slb_shadow_settings_pcss_size / float(2048) * slb_shadow_pcss_size_multiplier; float pcss_size_min = slb_shadow_settings_pcss_size_min / float(SMAP_size) * slb_shadow_pcss_size_min_multiplier; float light_width = slb_shadow_settings_pcss_light_width; float filter_size_real; float filter_size; #if defined(SLB_SHADOW_SPOT) SLB_STATIC_CONST float radial_bias = 2.0; #elif defined(SLB_SHADOW_OMNI) SLB_STATIC_CONST float radial_bias = 1.0; #else SLB_STATIC_CONST float radial_bias = 0.3; #endif { /// Blocker search float blocker_size = pcss_size; float average_blocker_depth = tc.z * 1e-6; float average_blocker_div = 1e-6; /// Migitate division by zero float2x2 rot_mat = SLB_Rotate2Matrix(noise*SLB_TAU); SLB_UNROLL(blocker_samples) for(int i = 0; i < blocker_samples; i++) { float2 offset = SLB_VogelDisk_Sample(i, blocker_samples); offset *= sqrt(length(offset)); float offset_length = length(offset); offset = mul(offset, rot_mat); offset *= blocker_size; float depth = SLB_Shadow_Load(tc.xy + offset); float biased_z = tc.z - radial_bias*offset_length*blocker_size; float blocker = float(biased_z > depth && depth > 1e-6); blocker *= 1.0/(offset_length + 1e-9); average_blocker_depth += depth * blocker; average_blocker_div += blocker; } average_blocker_depth /= average_blocker_div; /// Calculate penumbra size filter_size = light_width * (tc.z - average_blocker_depth)/average_blocker_depth; #if defined(SLB_SHADOW_SPOT) || defined(SLB_SHADOW_OMNI) filter_size *= 2 - average_blocker_depth; #endif #ifdef SLB_GLSL filter_size += 1; #endif filter_size = min(pcss_size, filter_size); #ifdef SLB_GLSL // filter_size *= frac(timers.y/10.0); // filter_size *= cos(timers.y/5.0)*0.5 + 0.5; // filter_size *= abs(frac(timers.y/2.0/8.0)*2.0 - 1.0)*1.0 + 0.0; // filter_size *= noise; // filter_size *= 0.0; // if(sin(tc.x*100.0) > 0.5) { // filter_size = 2.9/float(SMAP_size); // } else { // filter_size = 3.000/float(SMAP_size); // } #endif filter_size_real = max(pcss_size_min/1000.0, filter_size); filter_size = max(pcss_size_min, filter_size); } float lightness; SLB_BRANCH if(filter_size_real < pcss_size_min) { if (slb_shadow_pcss_early_quit) { float lightness = SLB_Shadow_VogelFilter(tc, noise, filter_size*1.25, filter_size_real*1.25, 8, false, radial_bias); float scurve = clamp(filter_size/filter_size_real, 1.0, 2/(pcss_size_min*float(SMAP_size))); lightness = saturate((lightness*scurve + 0.5) - 0.5*scurve); SLB_BRANCH if(abs(lightness - 0.5) > 0.45) #if 1 { return lightness; } #else { return 0.0; } else { return 10.0; } #endif } lightness = SLB_Shadow_VogelFilter(tc, noise, filter_size, filter_size_real, filter_small_samples, true, radial_bias); } else { lightness = SLB_Shadow_VogelFilter(tc, noise, filter_size, filter_size_real, filter_samples, false, radial_bias); } float scurve = clamp(filter_size/filter_size_real, 1.0, 2.0*float(filter_small_samples)/(pcss_size_min*float(SMAP_size))); lightness = saturate((lightness*scurve + 0.5) - 0.5*scurve); return lightness; } float SLB_Shadow(float4 tc, float2 pos2d) { float noise; if(slb_shadow_animated_noise) { uint noise_offset = SLB_PCG(asuint(timers.y)); uint2 noise_uv = uint2(pos2d+0.5) + uint2(noise_offset, noise_offset); noise = SLB_PhiNoise2_float(noise_uv); } else { noise = SLB_Morton_OwenHash4_Bluenoise_float(uint2(pos2d+0.5)); } tc.xyz /= tc.w; float lightness = 1.0; if(slb_shadow_settings_method == slb_shadow_method_linear) { lightness = SLB_Shadow_Linear(tc); } else if (slb_shadow_settings_method == slb_shadow_method_nice) { lightness = SLB_Shadow_Nice(tc); } else if (slb_shadow_settings_method == slb_shadow_method_bicubic) { lightness = SLB_Shadow_Bicubic(tc); } else if (slb_shadow_settings_method == slb_shadow_method_optimized_pcf) { lightness = SLB_CubicRational1D(SLB_Shadow_OptimizedPCF(tc)); } else if (slb_shadow_settings_method == slb_shadow_method_chs) { lightness = SLB_Shadow_CHS(tc, noise); } else if (slb_shadow_settings_method == slb_shadow_method_pcf) { lightness = SLB_Shadow_PCF(tc, noise); } else if (slb_shadow_settings_method == slb_shadow_method_vogel_chs) { lightness = SLB_Shadow_VogelCHS(tc, noise); } else if (slb_shadow_settings_method == slb_shadow_method_pcss) { lightness = SLB_Shadow_PCSS(tc, noise); } return lightness; } #endif /// SLB_SHADOW_H