381 lines
12 KiB
HLSL
381 lines
12 KiB
HLSL
#ifndef VOLUMETRIC_FOG_2_RAYMARCH
|
|
#define VOLUMETRIC_FOG_2_RAYMARCH
|
|
|
|
#if defined(_LIGHT_LAYERS)
|
|
static uint meshRenderingLayers;
|
|
#endif
|
|
|
|
void SetJitter(float2 uv) {
|
|
|
|
float2 screenSize = lerp(_ScreenParams.xy, _VFRTSize.xy, _VFRTSize.z);
|
|
float2 pixelPos = uv * screenSize;
|
|
|
|
#if defined(FOG_BLUE_NOISE)
|
|
float2 noiseUV = pixelPos * _BlueNoise_TexelSize.xy;
|
|
jitter = SAMPLE_TEXTURE2D(_BlueNoise, sampler_BlueNoise_PointRepeat, noiseUV).r;
|
|
#else
|
|
//Jitter = frac(dot(float2(2.4084507, 3.2535211), (scrPos.xy / scrPos.w) * _ScreenParams.xy));
|
|
const float3 magic = float3( 0.06711056, 0.00583715, 52.9829189 );
|
|
jitter = frac( magic.z * frac( dot( pixelPos, magic.xy ) ) );
|
|
#endif
|
|
}
|
|
|
|
|
|
inline float3 ProjectOnPlane(float3 v, float3 planeNormal) {
|
|
// assume plane normal has a modulus of 1
|
|
float dt = dot(v, planeNormal);
|
|
return v - planeNormal * dt;
|
|
}
|
|
|
|
inline float3 GetRayStart(float3 wpos) {
|
|
float3 cameraPosition = GetCameraPositionWS();
|
|
#if defined(ORTHO_SUPPORT)
|
|
float3 cameraForward = UNITY_MATRIX_V[2].xyz;
|
|
float3 rayStart = ProjectOnPlane(wpos - cameraPosition, cameraForward) + cameraPosition;
|
|
return lerp(cameraPosition, rayStart, unity_OrthoParams.w);
|
|
#else
|
|
return cameraPosition;
|
|
#endif
|
|
}
|
|
|
|
inline half Brightness(half3 color) {
|
|
return max(color.r, max(color.g, color.b));
|
|
}
|
|
|
|
|
|
half4 SampleDensity(float3 wpos) {
|
|
|
|
wpos.y -= BOUNDS_VERTICAL_OFFSET;
|
|
float3 boundsCenter = _BoundsCenter;
|
|
float3 boundsExtents = _BoundsExtents;
|
|
|
|
#if VF2_SURFACE
|
|
SurfaceApply(boundsCenter, boundsExtents);
|
|
#endif
|
|
|
|
#if VF2_DETAIL_NOISE
|
|
#if !defined(USE_WORLD_SPACE_NOISE)
|
|
wpos.xyz -= boundsCenter;
|
|
#endif
|
|
half detail = tex3Dlod(_DetailTex, float4(wpos * DETAIL_SCALE - _DetailWindDirection, 0)).a;
|
|
half4 density = _DetailColor;
|
|
if (USE_BASE_NOISE) {
|
|
#if defined(USE_WORLD_SPACE_NOISE)
|
|
wpos.y -= boundsCenter.y;
|
|
#endif
|
|
wpos.y /= boundsExtents.y;
|
|
density = tex2Dlod(_NoiseTex, float4(wpos.xz * _NoiseScale - _WindDirection.xz, 0, 0));
|
|
density.a -= abs(wpos.y);
|
|
}
|
|
density.a += (detail + DETAIL_OFFSET) * DETAIL_STRENGTH;
|
|
#else
|
|
#if defined(USE_WORLD_SPACE_NOISE) || VF2_CONSTANT_DENSITY
|
|
wpos.y -= boundsCenter.y;
|
|
#else
|
|
wpos.xyz -= boundsCenter;
|
|
#endif
|
|
wpos.y /= boundsExtents.y;
|
|
#if VF2_CONSTANT_DENSITY
|
|
half4 density = half4(_DetailColor.rgb, 1.0);
|
|
#else
|
|
half4 density = tex2Dlod(_NoiseTex, float4(wpos.xz * _NoiseScale - _WindDirection.xz, 0, 0));
|
|
#endif
|
|
density.a -= abs(wpos.y);
|
|
#endif
|
|
|
|
return density;
|
|
}
|
|
|
|
|
|
#define dot2(x) dot(x,x)
|
|
|
|
void AddFog(float3 rayStart, float3 wpos, float2 uv, half energyStep, half4 baseColor, inout half4 sum) {
|
|
|
|
half4 density = SampleDensity(wpos);
|
|
|
|
float3 rotatedWPos = wpos;
|
|
#if defined(FOG_ROTATION)
|
|
rotatedWPos = Rotate(rotatedWPos);
|
|
#endif
|
|
|
|
#if VF2_VOIDS
|
|
density.a -= ApplyFogVoids(rotatedWPos);
|
|
#endif
|
|
|
|
#if defined(FOG_BORDER)
|
|
#if VF2_SHAPE_SPHERE
|
|
float3 delta = wpos - _BoundsCenter;
|
|
float distSqr = dot2(delta);
|
|
float border = 1.0 - saturate( (distSqr - BORDER_START_SPHERE) / BORDER_SIZE_SPHERE );
|
|
density.a *= border * border;
|
|
#else
|
|
float2 dist2 = abs(wpos.xz - _BoundsCenter.xz);
|
|
float2 border2 = saturate( (dist2 - BORDER_START_BOX) / BORDER_SIZE_BOX );
|
|
float border = 1.0 - max(border2.x, border2.y);
|
|
density.a *= border * border;
|
|
#endif
|
|
#endif
|
|
|
|
#if VF2_DISTANCE
|
|
density.a -= ApplyFogDistance(rayStart, wpos);
|
|
#endif
|
|
|
|
UNITY_BRANCH
|
|
if (density.a > 0) {
|
|
half4 fgCol = baseColor * half4((1.0 - density.a * _DeepObscurance).xxx, density.a);
|
|
#if VF2_RECEIVE_SHADOWS
|
|
if (loop_t < loop_shadowMaxDistance) {
|
|
half shadowAtten = GetLightAttenuation(rotatedWPos);
|
|
fgCol.rgb *= lerp(1.0, shadowAtten, SHADOW_INTENSITY);
|
|
#if defined(FOG_SHADOW_CANCELLATION)
|
|
fgCol.a *= lerp(1.0, shadowAtten, SHADOW_CANCELLATION);
|
|
#endif
|
|
}
|
|
#endif
|
|
#if VF2_NATIVE_LIGHTS
|
|
#if USE_FORWARD_PLUS && !defined(FOG_FORWARD_PLUS_IGNORE_CLUSTERING)
|
|
// additional directional lights
|
|
#if defined(FOG_FORWARD_PLUS_ADDITIONAL_DIRECTIONAL_LIGHTS)
|
|
for (uint lightIndex = 0; lightIndex < URP_FP_DIRECTIONAL_LIGHTS_COUNT; lightIndex++) {
|
|
Light light = GetAdditionalLight(lightIndex, rotatedWPos, 1.0.xxxx);
|
|
#if defined(_LIGHT_LAYERS)
|
|
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
fgCol.rgb += light.color * (light.distanceAttenuation * light.shadowAttenuation * _NativeLightsMultiplier);
|
|
}
|
|
}
|
|
#endif
|
|
// clustered lights
|
|
{
|
|
uint lightIndex;
|
|
ClusterIterator _urp_internal_clusterIterator = ClusterInit(uv, rotatedWPos, 0);
|
|
[loop] while (ClusterNext(_urp_internal_clusterIterator, lightIndex)) {
|
|
lightIndex += URP_FP_DIRECTIONAL_LIGHTS_COUNT;
|
|
Light light = GetAdditionalLight(lightIndex, rotatedWPos, 1.0.xxxx);
|
|
#if defined(_LIGHT_LAYERS)
|
|
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
fgCol.rgb += light.color * (light.distanceAttenuation * light.shadowAttenuation * _NativeLightsMultiplier);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
#if USE_FORWARD_PLUS
|
|
uint additionalLightCount = min(URP_FP_PROBES_BEGIN, 8); // more than 8 lights is too slow for raymarching
|
|
#else
|
|
uint additionalLightCount = GetAdditionalLightsCount();
|
|
#endif
|
|
for (uint i = 0; i < additionalLightCount; ++i) {
|
|
#if UNITY_VERSION >= 202030
|
|
Light light = GetAdditionalLight(i, rotatedWPos, 1.0.xxxx);
|
|
#else
|
|
Light light = GetAdditionalLight(i, rotatedWPos);
|
|
#endif
|
|
#if defined(_LIGHT_LAYERS)
|
|
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
fgCol.rgb += light.color * (light.distanceAttenuation * light.shadowAttenuation * _NativeLightsMultiplier);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if UNITY_VERSION >= 202310 && defined(VF2_APV)
|
|
fgCol.rgb += GetAPVColor(wpos);
|
|
#endif
|
|
|
|
#if VF2_LIGHT_COOKIE
|
|
half3 cookieColor = SampleMainLightCookie(wpos);
|
|
fgCol.rgb *= cookieColor;
|
|
#if defined(V2F_LIGHT_COOKIE_CANCELLATION)
|
|
fgCol.a *= Brightness(cookieColor);
|
|
#endif
|
|
#endif
|
|
|
|
#if VF2_DEPTH_GRADIENT
|
|
fgCol *= ApplyDepthGradient(rayStart, wpos);
|
|
#endif
|
|
|
|
#if VF2_HEIGHT_GRADIENT
|
|
fgCol *= ApplyHeightGradient(wpos);
|
|
#endif
|
|
|
|
fgCol.rgb *= density.rgb * fgCol.aaa;
|
|
#if VF2_FOW
|
|
fgCol *= ApplyFogOfWar(rotatedWPos);
|
|
#endif
|
|
|
|
fgCol *= energyStep;
|
|
sum += fgCol * (1.0 - sum.a);
|
|
}
|
|
}
|
|
|
|
#pragma warning (disable : 3571) // disable pow() negative warning
|
|
|
|
half SimpleDiffusionIntensity(half cosTheta, half power) {
|
|
return pow(cosTheta, power);
|
|
}
|
|
|
|
half HenyeyGreenstein(half cosTheta, half g) {
|
|
half g2 = g * g;
|
|
half denom = 1.0 + g2 - 2.0 * g * cosTheta;
|
|
return (1.0 - g2) / (4.0 * 3.14159265 * pow(denom, 1.5));
|
|
}
|
|
|
|
half MiePhase(half cosTheta, half g) {
|
|
half g2 = g * g;
|
|
half denom = 1.0 + g2 - 2.0 * g * cosTheta;
|
|
return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cosTheta * cosTheta) / pow(denom, 1.5);
|
|
}
|
|
|
|
half GetDiffusionIntensity(float3 viewDir) {
|
|
half cosTheta = max(dot(viewDir, _SunDir.xyz), 0);
|
|
#if VF2_DIFFUSION_SMOOTH
|
|
half diffusion = HenyeyGreenstein(cosTheta, LIGHT_DIFFUSION_POWER);
|
|
#elif VF2_DIFFUSION_STRONG
|
|
half diffusion = MiePhase(cosTheta, LIGHT_DIFFUSION_POWER);
|
|
#else
|
|
half diffusion = SimpleDiffusionIntensity(cosTheta, LIGHT_DIFFUSION_POWER);
|
|
#endif
|
|
return diffusion * LIGHT_DIFFUSION_INTENSITY;
|
|
}
|
|
|
|
half3 GetDiffusionColor(float3 viewDir, float t1) {
|
|
half diffusion = GetDiffusionIntensity(viewDir);
|
|
half3 diffusionColor = _LightColor.rgb * (1.0 + diffusion * saturate(dot2(t1 / LIGHT_DIFFUSION_DEPTH_ATTEN)));
|
|
return diffusionColor;
|
|
}
|
|
|
|
half4 GetFogColor(float3 rayStart, float3 viewDir, float2 uv, float t0, float t1) {
|
|
|
|
float len = t1 - t0;
|
|
float rs = MIN_STEPPING + max(log(len), 0) * FOG_STEPPING; // stepping ratio with atten detail with distance
|
|
half3 diffusionColor = GetDiffusionColor(viewDir, t1);
|
|
half4 lightColor = half4(diffusionColor, 1.0);
|
|
|
|
float3 wpos = rayStart + viewDir * t0;
|
|
float3 endPos = rayStart + viewDir * t1;
|
|
|
|
#if VF2_SURFACE
|
|
SurfaceComputeEndPoints(wpos, endPos);
|
|
#endif
|
|
|
|
rs = max(rs, 1.0 / MAX_ITERATIONS);
|
|
|
|
viewDir *= rs;
|
|
|
|
half energyStep = min(1.0, _Density * rs);
|
|
half4 sum = half4(0,0,0,0);
|
|
|
|
#if VF2_RECEIVE_SHADOWS
|
|
loop_shadowMaxDistance = (SHADOW_MAX_DISTANCE - t0) / len;
|
|
#endif
|
|
|
|
// Set the global variable before the loop
|
|
#if defined(_LIGHT_LAYERS)
|
|
meshRenderingLayers = GetMeshRenderingLayer();
|
|
#endif
|
|
|
|
// normalize raystep
|
|
rs /= len;
|
|
|
|
// Use this Unroll macro to support WebGL. Increase 50 value if needed.
|
|
#if defined(WEBGL_COMPATIBILITY_MODE)
|
|
UNITY_UNROLLX(50)
|
|
#elif VF2_LIGHT_COOKIE
|
|
UNITY_LOOP
|
|
#endif
|
|
for (loop_t = 0; loop_t < 1.0; loop_t += rs) {
|
|
AddFog(rayStart, wpos, uv, energyStep, lightColor, sum);
|
|
if (sum.a > 0.99) {
|
|
break;
|
|
}
|
|
wpos += viewDir;
|
|
}
|
|
if (sum.a > 0.99) {
|
|
sum.a = 1;
|
|
} else {
|
|
energyStep = _Density * len * (rs - (loop_t-1.0));
|
|
energyStep = min(1.0, energyStep);
|
|
AddFog(rayStart, endPos, uv, energyStep, lightColor, sum);
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
|
|
half4 ComputeFog(float3 wpos, float2 uv) {
|
|
|
|
float3 rayStart = GetRayStart(wpos);
|
|
float3 ray = wpos - rayStart;
|
|
float t1 = length(ray);
|
|
|
|
#if defined(FOG_ROTATION)
|
|
float3 rayStartNonRotated = rayStart;
|
|
float3 rayDirNonRotated = ray / t1;
|
|
rayStart = RotateInv(rayStart);
|
|
ray = mul((float3x3)_InvRotMatrix, ray);
|
|
float3 rayDir = ray / t1;
|
|
#else
|
|
float3 rayDir = ray / t1;
|
|
float3 rayStartNonRotated = rayStart;
|
|
float3 rayDirNonRotated = rayDir;
|
|
#endif
|
|
|
|
#if VF2_SHAPE_SPHERE
|
|
float t0;
|
|
SphereIntersection(rayStart, rayDir, t0, t1);
|
|
#else
|
|
float t0 = BoxIntersection(rayStart, rayDir);
|
|
#endif
|
|
|
|
#if defined(FOG_MAX_DISTANCE_XZ)
|
|
float slope = 1.0001 - abs(rayDir.y);
|
|
FOG_MAX_LENGTH /= slope;
|
|
#endif
|
|
|
|
SetJitter(uv);
|
|
|
|
t1 = min(t1, FOG_MAX_LENGTH); // max distance
|
|
|
|
float jiterring = jitter * JITTERING;
|
|
t0 += jiterring;
|
|
t1 += jiterring;
|
|
|
|
CLAMP_RAY_DEPTH(rayStartNonRotated, uv, t1); // clamp to geometry
|
|
|
|
#if VF2_DEPTH_PEELING
|
|
CLAMP_RAY_START(rayStartNonRotated, uv, t0); // clamp to start of transparent objects
|
|
#endif
|
|
|
|
if (t0 >= t1) return 0;
|
|
|
|
half4 fogColor = GetFogColor(rayStart, rayDir, uv, t0, t1);
|
|
|
|
// dither
|
|
#if !VF2_DEPTH_PEELING // dithering shouldn't be used when depth peeling is enabled
|
|
fogColor.rgb = max(0, fogColor.rgb - jitter * DITHERING);
|
|
#endif
|
|
|
|
// alpha
|
|
fogColor *= _LightColor.a;
|
|
|
|
#if VF2_POINT_LIGHTS
|
|
AddPointLights(rayStartNonRotated, rayDirNonRotated, fogColor, t0, t1 - t0);
|
|
#endif
|
|
|
|
#if defined(FOG_MAX_DISTANCE_XZ)
|
|
float fallOffFactor = FOG_MAX_LENGTH * FOG_MAX_LENGTH_FALLOFF + 1.0;
|
|
half maxDistanceFallOff = (FOG_MAX_LENGTH - t0) / fallOffFactor;
|
|
#else
|
|
half maxDistanceFallOff = (FOG_MAX_LENGTH - t0) / FOG_MAX_LENGTH_FALLOFF_PRECOMPUTED;
|
|
#endif
|
|
fogColor *= saturate(maxDistanceFallOff * maxDistanceFallOff * maxDistanceFallOff * maxDistanceFallOff);
|
|
|
|
return fogColor;
|
|
}
|
|
|
|
#endif // VOLUMETRIC_FOG_2_RAYMARCH |