Files
UnrealEngine/Engine/Shaders/Private/CapsuleLight.ush
2025-05-18 13:04:45 +08:00

128 lines
3.4 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
float SmoothMin( float a, float b, float k )
{
float h = saturate( 0.5 + (0.5 / k) * (b - a) );
return lerp( b, a, h ) - k * (h - h*h);
}
float SmoothMax( float a, float b, float k )
{
return SmoothMin( a, b, -k );
}
float SmoothClamp( float x, float Min, float Max, float k )
{
return SmoothMin( SmoothMax( x, Min, k ), Max, k );
//return min( max( x, Min ), Max );
}
struct FCapsuleLight
{
float3 LightPos[2];
float Length;
float Radius;
float SoftRadius;
float DistBiasSqr;
};
void ClipToHorizon( inout float3 Line0, inout float3 Line1, float3 N )
{
float NoP0 = dot( N, Line0 );
float NoP1 = dot( N, Line1 );
if( NoP0 < 0 ) Line0 = ( Line0 * NoP1 - Line1 * NoP0 ) / ( NoP1 - NoP0 );
if( NoP1 < 0 ) Line1 = ( -Line0 * NoP1 + Line1 * NoP0 ) / ( -NoP1 + NoP0 );
}
// Closest point on line segment to point
float3 ClosestPointLineToPoint( float3 Line0, float3 Line1, float Length )
{
float3 Line01 = Line1 - Line0;
return Line0 + Line01 * saturate( -dot( Line01, Line0 ) / Pow2( Length ) );
//return Line0 + Line01 * SmoothClamp( -dot( Line01, Line0 ) / Pow2( Length ), 0, 1, 0.5 );
}
// Closest point on line segment to ray
float3 ClosestPointLineToRay( float3 Line0, float3 Line1, float Length, float3 R )
{
float3 L0 = Line0;
float3 L1 = Line1;
float3 Line01 = Line1 - Line0;
// Shortest distance
float A = Square( Length );
float B = dot( R, Line01 );
float t = saturate( dot( Line0, B*R - Line01 ) / (A - B*B) );
return Line0 + t * Line01;
}
float3 SmallestAnglePointOnLineToRay( float3 Line0, float3 Line1, float Length, float3 R )
{
float3 L0 = Line0;
float3 L1 = Line1;
float3 Line01 = Line1 - Line0;
float A = Square( Length );
float B = 2 * dot( Line0, Line01 );
float C = dot( Line0, Line0 );
float D = dot( R, Line0 );
float E = dot( R, Line01 );
float t = saturate( (B*D - 2*C*E) / (B*E - 2*A*D) );
return Line0 + t * Line01;
}
float3 LineIrradiance( float3 N, float3 Line0, float3 Line1, float DistanceBiasSqr, out float CosSubtended, out float BaseIrradiance, out float NoL )
{
float LengthSqr0 = dot( Line0, Line0 );
float LengthSqr1 = dot( Line1, Line1 );
float InvLength0 = rsqrt( LengthSqr0 );
float InvLength1 = rsqrt( LengthSqr1 );
float InvLength01 = InvLength0 * InvLength1;
CosSubtended = dot( Line0, Line1 ) * InvLength01;
BaseIrradiance = InvLength01 / ( CosSubtended * 0.5 + 0.5 + DistanceBiasSqr * InvLength01 );
NoL = 0.5 * ( dot(N, Line0) * InvLength0 + dot(N, Line1) * InvLength1 );
float3 VectorIrradiance = ( BaseIrradiance * 0.5 ) * ( Line0 * InvLength0 + Line1 * InvLength1 );
return VectorIrradiance;
}
// Alpha is half of angle of spherical cap
float SphereHorizonCosWrap( float NoL, float SinAlphaSqr )
{
#if 1
float SinAlpha = sqrt( SinAlphaSqr );
if( NoL < SinAlpha )
{
NoL = max( NoL, -SinAlpha );
#if 0
// Accurate sphere irradiance
float CosBeta = NoL;
float SinBeta = sqrt( 1 - CosBeta * CosBeta );
float TanBeta = SinBeta / CosBeta;
float x = sqrt( 1 / SinAlphaSqr - 1 );
float y = -x / TanBeta;
float z = SinBeta * sqrt(1 - y*y);
NoL = NoL * acos(y) - x * z + atan( z / x ) / SinAlphaSqr;
NoL /= PI;
#else
// Hermite spline approximation
// Fairly accurate with SinAlpha < 0.8
// y=0 and dy/dx=0 at -SinAlpha
// y=SinAlpha and dy/dx=1 at SinAlpha
NoL = Pow2( SinAlpha + NoL ) / ( 4 * SinAlpha );
#endif
}
#else
NoL = saturate( ( NoL + SinAlphaSqr ) / ( 1 + SinAlphaSqr ) );
#endif
return NoL;
}