ToriaAssets/Sources/Shaders/EnviroVolumeLightCore.cginc

343 lines
13 KiB
HLSL
Raw Normal View History

2026-05-07 10:14:44 +02:00
#include "UnityCG.cginc"
#include "UnityDeferredLibrary.cginc"
half4 _MainTex_ST;
half4 _CameraDepthTexture_ST;
sampler3D _NoiseTexture;
sampler2D _DitherTexture;
float4x4 _WorldViewProj;
float4x4 _WorldViewProj_SP;
float4x4 _MyLightMatrix0;
float4x4 _MyWorld2Shadow;
float4x4 _LeftWorldFromView;
float4x4 _RightWorldFromView;
float4x4 _LeftViewFromScreen;
float4x4 _RightViewFromScreen;
float3 _CameraForward;
// x: scattering coef, y: extinction coef, z: range w: skybox extinction coef
float4 _VolumetricLight;
// x: 1 - g^2, y: 1 + g^2, z: 2*g, w: 1/4pi
float4 _MieG;
float _MaxRayLength;
int _SampleCount;
// x: scale, y: intensity, z: intensity offset
float4 _NoiseData;
// x: x velocity, y: z velocity
float4 _NoiseVelocity;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline fixed4 GetCascadeWeights_SplitSpheres(float3 wpos)
{
float3 fromCenter0 = wpos.xyz - unity_ShadowSplitSpheres[0].xyz;
float3 fromCenter1 = wpos.xyz - unity_ShadowSplitSpheres[1].xyz;
float3 fromCenter2 = wpos.xyz - unity_ShadowSplitSpheres[2].xyz;
float3 fromCenter3 = wpos.xyz - unity_ShadowSplitSpheres[3].xyz;
float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));
fixed4 weights = float4(distances2 < unity_ShadowSplitSqRadii);
weights.yzw = saturate(weights.yzw - weights.xyz);
return weights;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline float4 GetCascadeShadowCoord(float4 wpos, fixed4 cascadeWeights)
{
float3 sc0 = mul(unity_WorldToShadow[0], wpos).xyz;
float3 sc1 = mul(unity_WorldToShadow[1], wpos).xyz;
float3 sc2 = mul(unity_WorldToShadow[2], wpos).xyz;
float3 sc3 = mul(unity_WorldToShadow[3], wpos).xyz;
float4 shadowMapCoordinate = float4(sc0 * cascadeWeights[0] + sc1 * cascadeWeights[1] + sc2 * cascadeWeights[2] + sc3 * cascadeWeights[3], 1);
#if defined(UNITY_REVERSED_Z)
float noCascadeWeights = 1 - dot(cascadeWeights, float4(1, 1, 1, 1));
shadowMapCoordinate.z += noCascadeWeights;
#endif
return shadowMapCoordinate;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
UNITY_DECLARE_SHADOWMAP(_CascadeShadowMapTexture);
float GetLightAttenuation(float3 wpos)
{
float atten = 0;
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)
atten = 1;
#if defined (SHADOWS_DEPTH)
// sample cascade shadow map
float4 cascadeWeights = GetCascadeWeights_SplitSpheres(wpos);
bool inside = dot(cascadeWeights, float4(1, 1, 1, 1)) < 4;
float4 samplePos = GetCascadeShadowCoord(float4(wpos, 1), cascadeWeights);
atten = inside ? UNITY_SAMPLE_SHADOW(_CascadeShadowMapTexture, samplePos.xyz) : 1.0f;
atten = _LightShadowData.r + atten * (1 - _LightShadowData.r);
#endif
#elif defined (SPOT)
float3 tolight = _LightPos.xyz - wpos;
half3 lightDir = normalize(tolight);
float4 uvCookie = mul(_MyLightMatrix0, float4(wpos, 1));
// negative bias because http://aras-p.info/blog/2010/01/07/screenspace-vs-mip-mapping/
atten = tex2Dbias(_LightTexture0, float4(uvCookie.xy / uvCookie.w, 0, -8)).w;
atten *= uvCookie.w < 0;
float att = dot(tolight, tolight) * _LightPos.w;
atten *= tex2D(_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;
#if defined(SHADOWS_DEPTH)
float4 shadowCoord = mul(_MyWorld2Shadow, float4(wpos, 1));
atten *= saturate(UnitySampleShadowmap(shadowCoord));
#endif
#elif defined (POINT) || defined (POINT_COOKIE)
float3 tolight = wpos - _LightPos.xyz;
half3 lightDir = -normalize(tolight);
float att = dot(tolight, tolight) * _LightPos.w;
atten = tex2D(_LightTextureB0, att.rr).UNITY_ATTEN_CHANNEL;
atten *= UnityDeferredComputeShadow(tolight, 0, float2(0, 0));
#if defined (POINT_COOKIE)
atten *= texCUBEbias(_LightTexture0, float4(mul(_MyLightMatrix0, half4(wpos, 1)).xyz, -8)).w;
#endif //POINT_COOKIE
#endif
return atten;
}
float GetLightAttenuationDir(float3 wpos)
{
float atten = 1;
// sample cascade shadow map
float4 cascadeWeights = GetCascadeWeights_SplitSpheres(wpos);
bool inside = dot(cascadeWeights, float4(1, 1, 1, 1)) < 4;
float4 samplePos = GetCascadeShadowCoord(float4(wpos, 1), cascadeWeights);
atten = inside ? UNITY_SAMPLE_SHADOW(_CascadeShadowMapTexture, samplePos.xyz) : 1.0f;
atten = _LightShadowData.r + atten * (1 - _LightShadowData.r);
return atten;
}
void ApplyHeightFog(float3 wpos, inout float density)
{
//#ifdef HEIGHT_FOG
density *= exp(-(wpos.y + 1) * 0.1);
//#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
float GetDensityFog(float3 wpos)
{
float density = 1;
#ifdef NOISE
float noise = tex3D(_NoiseTexture, frac(wpos * _NoiseData.x + float3(_Time.y * _NoiseVelocity.x, 0, _Time.y * _NoiseVelocity.y)));
noise = saturate(noise - _NoiseData.z) * _NoiseData.y;
density *= saturate(noise);
#endif
ApplyHeightFog(wpos, density);
return density;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
float MieScattering(float cosAngle, float4 g)
{
return g.w * (g.x / (pow(g.y - g.z * cosAngle, 1.5)));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
float4 RayMarch(float2 screenPos, float3 rayStart, float3 rayDir, float rayLength)
{
#ifdef DITHER_4_4
float2 interleavedPos = (fmod(floor(screenPos.xy), 4.0));
#if UNITY_SINGLE_PASS_STEREO
float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy;
#endif
float offset = tex2D(_DitherTexture, interleavedPos / 4.0 + float2(0.5 / 4.0, 0.5 / 4.0)).w;
#else
float2 interleavedPos = (fmod(floor(screenPos.xy), 8.0));
#if UNITY_SINGLE_PASS_STEREO
float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy;
#endif
float offset = tex2D(_DitherTexture, interleavedPos / 8.0 + float2(0.5 / 8.0, 0.5 / 8.0)).w;
#endif
int stepCount = _SampleCount;
float stepSize = rayLength / stepCount;
float3 step = rayDir * stepSize;
float3 currentPosition = rayStart + step * offset;
float4 vlight = 0;
float cosAngle;
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)
float extinction = 0;
cosAngle = dot(_LightDir.xyz, -rayDir);
#else
// we don't know about density between camera and light's volume, assume 0.5
float extinction = length(_WorldSpaceCameraPos - currentPosition) * _VolumetricLight.y * 0.5;
#endif
[loop]
for (int i = 0; i < stepCount; ++i)
{
float density = 0;
float atten = GetLightAttenuation(currentPosition);
density = GetDensityFog(currentPosition);
float scattering = _VolumetricLight.x * stepSize * density;
extinction += _VolumetricLight.y * stepSize * density;// +scattering;
float4 light = atten * scattering * exp(-extinction);
#if !defined (DIRECTIONAL) && !defined (DIRECTIONAL_COOKIE)
// phase functino for spot and point lights
float3 tolight = normalize(currentPosition - _LightPos.xyz);
cosAngle = dot(tolight, -rayDir);
light *= MieScattering(cosAngle, _MieG);
#endif
vlight += light;
currentPosition += step;
}
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)
// apply phase function for dir light
vlight *= MieScattering(cosAngle, _MieG);
#endif
// apply light's color
vlight *= _LightColor;
vlight = max(0, vlight);
#if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE) // use "proper" out-scattering/absorption for dir light
vlight.w = exp(-extinction);
#else
vlight.w = 0;
#endif
return vlight;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* float4 RayMarchDir(float2 screenPos, float3 rayStart, float3 rayDir, float rayLength)
{
#ifdef DITHER_4_4
float2 interleavedPos = (fmod(floor(screenPos.xy), 4.0));
#if UNITY_SINGLE_PASS_STEREO
float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy;
#endif
float offset = tex2D(_DitherTexture, interleavedPos / 4.0 + float2(0.5 / 4.0, 0.5 / 4.0)).w;
#else
float2 interleavedPos = (fmod(floor(screenPos.xy), 8.0));
#if UNITY_SINGLE_PASS_STEREO
float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
interleavedPos = (interleavedPos - scaleOffset.zw) / scaleOffset.xy;
#endif
float offset = tex2D(_DitherTexture, interleavedPos / 8.0 + float2(0.5 / 8.0, 0.5 / 8.0)).w;
#endif
int stepCount = _SampleCount;
float stepSize = rayLength / stepCount;
float3 step = rayDir * stepSize;
float3 currentPosition = rayStart + step * offset;
float4 vlight = 0;
float cosAngle;
float extinction = 0;
cosAngle = dot(_LightDir.xyz, -rayDir);
[loop]
for (int i = 0; i < stepCount; ++i)
{
float atten = GetLightAttenuationDir(currentPosition);
float density = GetDensityFog(currentPosition);
float scattering = _VolumetricLight.x * stepSize * density;
extinction += _VolumetricLight.y * stepSize * density;// +scattering;
float4 light = atten * scattering * exp(-extinction);
vlight += light;
currentPosition += step;
}
vlight *= MieScattering(cosAngle, _MieG);
// apply light's color
vlight *= _LightColor;
vlight = max(0, vlight);
vlight.w = exp(-extinction);
return vlight;
}
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
float2 RayConeIntersect(in float3 f3ConeApex, in float3 f3ConeAxis, in float fCosAngle, in float3 f3RayStart, in float3 f3RayDir)
{
float inf = 10000;
f3RayStart -= f3ConeApex;
float a = dot(f3RayDir, f3ConeAxis);
float b = dot(f3RayDir, f3RayDir);
float c = dot(f3RayStart, f3ConeAxis);
float d = dot(f3RayStart, f3RayDir);
float e = dot(f3RayStart, f3RayStart);
fCosAngle *= fCosAngle;
float A = a*a - b*fCosAngle;
float B = 2 * (c*a - d*fCosAngle);
float C = c*c - e*fCosAngle;
float D = B*B - 4 * A*C;
if (D > 0)
{
D = sqrt(D);
float2 t = (-B + sign(A)*float2(-D, +D)) / (2 * A);
bool2 b2IsCorrect = c + a * t > 0 && t > 0;
t = t * b2IsCorrect + !b2IsCorrect * (inf);
return t;
}
else // no intersection
return inf;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
float RayPlaneIntersect(in float3 planeNormal, in float planeD, in float3 rayOrigin, in float3 rayDir)
{
float NdotD = dot(planeNormal, rayDir);
float NdotO = dot(planeNormal, rayOrigin);
float t = -(NdotO + planeD) / NdotD;
if (t < 0)
t = 100000;
return t;
}