// -------------------------------------------------------------------------------- // // Defines #define MAX_DISTANCE 65536.0 #define NEAR_INFINITE 18446744073709551616.0 #define FLT_SMALL 0.001 // -------------------------------------------------------------------------------- // // OBS Default uniform float4x4 ViewProj; // Inputs uniform texture2d _sdf; uniform texture2d _image; uniform float _inner_min; uniform float _inner_max; uniform float2 _inner_offset; uniform float4 _inner_color; uniform float _outer_min; uniform float _outer_max; uniform float2 _outer_offset; uniform float4 _outer_color; uniform float _threshold; sampler_state sdfSampler { Filter = Linear; AddressU = Clamp; AddressV = Clamp; }; sampler_state sdfSampler1_1 { Filter = Linear; AddressU = Border; AddressV = Border; BorderColor = 000000FF; }; sampler_state imageSampler { Filter = Linear; AddressU = Clamp; AddressV = Clamp; }; struct VertDataIn { float4 pos : POSITION; float2 uv : TEXCOORD0; }; struct VertDataOut { float4 pos : POSITION; float2 uv : TEXCOORD0; }; VertDataOut VSDefault(VertDataIn v_in) { VertDataOut vert_out; vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); vert_out.uv = v_in.uv; return vert_out; } float4 PS_SDFShadow_v1(VertDataOut v_in) : TARGET { float4 final = _image.Sample(imageSampler, v_in.uv); if (_inner_max > 0 && final.a >= _threshold) { float inner_dist = _sdf.Sample(sdfSampler, v_in.uv + _inner_offset).g; float range = (_inner_max - _inner_min); float str = clamp(inner_dist - _inner_min, 0, range) / range; final = lerp(_inner_color, final, str); } if (_outer_max > 0 && final.a < _threshold) { float outer_dist = _sdf.Sample(sdfSampler, v_in.uv + _outer_offset).r; float range = (_outer_max - _outer_min); float str = clamp(outer_dist - _outer_min, 0, range) / range; final = lerp(_outer_color, float4(_outer_color.r, _outer_color.g, _outer_color.b, 0.0), str); } return final; } float4 PS_SDFShadow_v1_1(VertDataOut v_in) : TARGET { // V1.1: // - No longer clipping off image using _threshold. // - Inverse Gradient supported (max < min) // - Negative Gradient support (min/max < 0) // Support for inverse Gradient (Outer Shadow): Maximum < Minimum // // Min=4.00, Max=0.00: // Expected: Max is fully visible, Min and higher is fully shadowed. // d=0.5 // d = d - 4.00 (= -3.5) // d = d / 4.00 (= -0.875, 87.5%) // d is 87.5% visible // Normal: // Min=0.00, Max=4.00: // d=0.5 // d = d - 0.00 (= 0.5) // d = d / 4.00 (= 0.125, 12.5%) // d is 12.5% visible float4 final = float4(0., 0., 0., 0.); float4 base = _image.Sample(imageSampler, v_in.uv); //! Outer Shadow // Are we allowed to draw an outer shadow? float2 outer_sdf = _sdf.Sample(sdfSampler1_1, v_in.uv + _outer_offset).rg * float2(MAX_DISTANCE, MAX_DISTANCE); if ((_outer_color.a > 0.) && (outer_sdf.r < MAX_DISTANCE)) { // Calculate the true distance value: // - If we are outside, this will be positive. // - If we are inside, this will be negative. float sdf_distance = outer_sdf.r - outer_sdf.g; // Calculate the delta. float delta = _outer_max - _outer_min; float t1 = sdf_distance - _outer_min; float t2 = clamp(t1 / delta, 0., 1.); final = lerp(final, _outer_color, (1. - t2) * _outer_color.a); } // Base Image if (base.a > 0.) { float3 rgb = base.rgb / base.a; final = lerp(final, base, base.a); } //! Inner Shadow // Are we allowed to draw an inner shadow? float2 inner_sdf = _sdf.Sample(sdfSampler1_1, v_in.uv + _inner_offset).rg * float2(MAX_DISTANCE, MAX_DISTANCE); if ((_inner_color.a > 0.) && (inner_sdf.g < MAX_DISTANCE) && (inner_sdf.r <= FLT_SMALL)) { // Calculate the true distance value: // - If we are outside, this will be positive. // - If we are inside, this will be negative. float sdf_distance = inner_sdf.g; // Calculate the delta. float delta = _inner_max - _inner_min; float t1 = sdf_distance - _inner_min; float t2 = clamp(t1 / delta, 0., 1.); // Inner shadow does not affect alpha of image. final.rgb = lerp(final.rgb, _inner_color.rgb, (1. - t2) * _inner_color.a); } return final; } technique Draw { pass { vertex_shader = VSDefault(v_in); pixel_shader = PS_SDFShadow_v1_1(v_in); } }