ToriaAssets/Sources/Shaders/EnviroVolumeCloudsCore.cginc

351 lines
11 KiB
HLSL
Raw Normal View History

2026-05-07 10:14:44 +02:00
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
uniform float4x4 _InverseProjection;
uniform float4x4 _InverseRotation;
uniform float4x4 _InverseProjection_SP;
uniform float4x4 _InverseRotation_SP;
uniform sampler2D _MainTex;
uniform float4 _MainTex_TexelSize;
uniform sampler3D _Noise;
uniform sampler3D _DetailNoise;
uniform sampler2D _WeatherMap;
uniform sampler2D _CurlNoise;
uniform float4 _CloudsParameter;
uniform float4 _Steps;
uniform float4 _CloudsLighting; //x = ExtinctionCoef, y = HgPhaseFactor, z = Silver_intensity, w = Silver_spread
uniform float4 _CloudsLightingExtended; // x = EdgeDarkness, y = AmbientSkyColorIntensity, z = _Tonemapping, w = _CloudsExposure
uniform float4 _CloudsErosionIntensity; //x = Base, y = Detail
uniform float _BaseNoiseUV;
uniform float _DetailNoiseUV;
uniform float4 _CloudDensityScale;
uniform float _LightIntensity;
uniform float _AmbientSkyColorIntensity;
uniform float4 _CloudsCoverageSettings; //x = _GlobalCoverage, y = Bottom Coverage Mod, z = Top coverage mod, w = Clouds Up Morph Intensity
uniform float _GlobalCoverage;
uniform float4 _LightColor;
uniform float4 _MoonLightColor;
uniform float4 _AmbientLightColor;
uniform float4 _CloudsAnimation;
uniform float3 _LightDir;
uniform float _stepsInDepth;
uniform float _LODDistance;
uniform float _gameTime;
uniform float3 _CameraPosition;
////
uniform float4 _Randomness;
////
const float env_inf = 1e10;
uint intersectRaySphere(
float3 rayOrigin,
float3 rayDir, // must be normalized
float3 sphereCenter,
float sphereRadius,
out float2 t)
{
float3 l = rayOrigin - sphereCenter;
float a = 1.0f; // dot(rayDir, rayDir) where rayDir is normalized
float b = 2.0f * dot(rayDir, l);
float c = dot(l, l) - sphereRadius * sphereRadius;
float discriminate = b * b - 4.0f * a * c;
if (discriminate < 0.0f)
{
t.x = t.y = 0.0f;
return 0u;
}
else if (abs(discriminate) - 0.00005f <= 0.0f)
{
t.x = t.y = -0.5f * b / a;
return 1u;
}
else
{
float q = b > 0.0f ? -0.5f * (b + sqrt(discriminate)) : -0.5f * (b - sqrt(discriminate));
float h1 = q / a;
float h2 = c / q;
t.x = min(h1, h2);
t.y = max(h1, h2);
if (t.x < 0.0f)
{
t.x = t.y;
if (t.x < 0.0f)
{
return 0u;
}
return 1u;
}
return 2u;
}
}
float rand(float2 co) {
float a = 12.9898;
float b = 78.233;
float c = 43758.5453;
float dt = dot(co.xy, float2(a, b));
float sn = fmod(dt, 3.14);
return 2.0 * frac(sin(sn) * c) - 1.0;
}
float2 ResolveInside(float3 cameraPos, float3 cameraDir, float maxDistance)
{
const float3 up = float3(0, 1, 0);
maxDistance = min(_CloudsParameter.w, maxDistance);
float bottom = (_CloudsParameter.x - cameraPos.y);
float top = ((_CloudsParameter.x + _CloudsParameter.y) - cameraPos.y);
float horizon = dot(cameraDir, up);
float bottomDist = max(0, bottom / horizon);
float topDist = max(0, top / horizon);
float startDist = min(bottomDist, topDist);
float endDist = max(bottomDist, topDist);
startDist = min(maxDistance, startDist);
endDist = min(maxDistance, endDist);
return float2(startDist, endDist);
}
// Realtime Volumetric Rendering Course Notes by Patapom (page 15)
float exponential_integral(float z) {
return 0.5772156649015328606065 + log(1e-4 + abs(z)) + z * (1.0 + z * (0.25 + z * ((1.0 / 18.0) + z * ((1.0 / 96.0) + z * (1.0 / 600.0))))); // For x!=0
}
// Realtime Volumetric Rendering Course Notes by Patapom (page 15)
float3 CalculateAmbientLighting(float altitude, float extinction_coeff, float3 skyColor)
{
float ambient_term = 0.6 * saturate(1.0 - altitude);
float3 isotropic_scattering_top = (skyColor.rgb * 0.25) * max(0.0, exp(ambient_term) - ambient_term * exponential_integral(ambient_term));
ambient_term = -extinction_coeff * altitude;
float3 isotropic_scattering_bottom = skyColor.rgb * 1.0 * max(0.0, exp(ambient_term) - ambient_term * exponential_integral(ambient_term)) * 1.5;
isotropic_scattering_top *= saturate(altitude);
return (isotropic_scattering_top)+(isotropic_scattering_bottom);
}
float HenryGreenstein(float cosTheta, float g) {
float k = 3.0 / (8.0 * 3.1415926f) * (1.0 - g * g) / (2.0 + g * g);
return k * (1.0 + cosTheta * cosTheta) / pow(abs(1.0 + g * g - 2.0 * g * cosTheta), 1.5);
}
float Remap(float org_val, float org_min, float org_max, float new_min, float new_max)
{
return new_min + saturate(((org_val - org_min) / (org_max - org_min))*(new_max - new_min));
}
float4 GetHeightGradient(float cloudType)
{
const float4 CloudGradient1 = float4(0.0, 0.05, 0.1, 0.25);
const float4 CloudGradient2 = float4(0.0, 0.05, 0.4, 0.8);
const float4 CloudGradient3 = float4(0.0, 0.05, 0.6, 1.0);
float a = 1.0 - saturate(cloudType * 2.0);
float b = 1.0 - abs(cloudType - 0.5) * 2.0;
float c = saturate(cloudType - 0.5) * 2.0;
return CloudGradient1 * a + CloudGradient2 * b + CloudGradient3 * c;
}
float GradientStep(float a, float4 gradient)
{
return smoothstep(gradient.x, gradient.y, a) - smoothstep(gradient.z, gradient.w, a);
}
float3 GetWeather(float3 pos)
{
float2 uv = pos.xz * 0.00001 + 0.5;
return tex2Dlod(_WeatherMap, float4(uv, 0.0, 0.0));
}
float GetSamplingHeight(float3 pos, float3 center)
{
return (length(pos - center) - (_CloudsParameter.w + _CloudsParameter.x)) * _CloudsParameter.z;
}
float3 ScreenSpaceDither(float2 vScreenPos, float lum)
{
float d = dot(float2(131.0, 312.0), vScreenPos.xy + _Time.y);
float3 vDither = float3(d, d, d);
vDither.rgb = frac(vDither.rgb / float3(103.0, 71.0, 97.0)) - float3(0.5, 0.5, 0.5);
return (vDither.rgb / 15.0) * 1.0 * lum;
}
float GetRaymarchEnd(float sceneDepth, float3 dir)
{
float raymarchEnd = 0.0f;
#if ENVIRO_DEPTHBLENDING
if (sceneDepth == 1.0f)
{
raymarchEnd = 1e7;
}
else
{
raymarchEnd = length(dir);
// raymarchEnd -= _ProjectionParams.y;
}
#else
raymarchEnd = 1e8;
#endif
return raymarchEnd;
}
float set_range_clamped(float value, float low, float high) {
float ranged_value = clamp(value, low, high);
ranged_value = (ranged_value - low) / (high - low);
return saturate(ranged_value);
}
float get_fade_term(float3 sample_pos) {
float distance = length(sample_pos.xy);
return saturate((distance - 5000) / 20000.0);
}
float get_altitude_scalar(float cloud_type) {
return lerp(8.0, 2.0, cloud_type);
}
float HeightAlter(float percent_height, float weather) {
float cloud_anvil_amount = 0.5;
float global_coverage = 0.5;
// Round bottom a bit
float ret_val = saturate(Remap(percent_height, 0.0, 0.07, 0.0, 1.0));
// Round top a lot
float stop_height = saturate(weather + 0.12);
ret_val *= saturate(Remap(percent_height, stop_height * 0.2, stop_height, 1.0, 0.0));
// Apply anvil ( cumulonimbus /" giant storm" clouds)
ret_val = pow(ret_val, saturate(Remap(percent_height, 0.65, 0.95, 1.0, (1 - cloud_anvil_amount * global_coverage))));
return ret_val;
}
float DensityAlter(float percent_height) {
float cloud_anvil_amount = 1.0;
// Have density be generally increasing over height
float ret_val = percent_height;
// Reduce density at base
ret_val *= saturate(Remap(percent_height, 0.0, 0.2, 0.0, 1.0));
ret_val *= 2;
// Reduce density for the anvil ( cumulonimbus clouds)
ret_val *= lerp(1, saturate(Remap(pow(percent_height, 0.5) , 0.4, 0.95, 1.0, 0.2)), cloud_anvil_amount);
// Reduce density at top to make better transition
ret_val *= saturate(Remap(percent_height, 0.9, 1.0, 1.0, 0.0));
return ret_val;
}
// Sample Cloud Density
float CalculateCloudDensity(float3 pos, float3 PlanetCenter, float3 weather, float mip, float dist, bool details)
{
const float baseFreq = 1e-5;
// Get Height fraction
float height = GetSamplingHeight(pos, PlanetCenter);
// wind settings
float cloud_top_offset = 20.0;
float3 wind_direction = float3(_CloudsAnimation.z, 0.0, _CloudsAnimation.w);
// skew in wind direction
pos += height * wind_direction * cloud_top_offset;
float mip1 = mip + (1-dist) * (3.5 * _LODDistance);
float4 coord = float4(pos * baseFreq * _BaseNoiseUV, mip1);
// Animate Wind
coord.xyz += float3(_CloudsAnimation.x, _CloudsErosionIntensity.w, _CloudsAnimation.y);
float4 baseNoise = 0;
baseNoise = tex3Dlod(_Noise, coord);
float low_freq_fBm = (baseNoise.g * 0.625) + (baseNoise.b * 0.25) + (baseNoise.a * 0.125);
float base_cloud = Remap(baseNoise.r, -(1.0 - low_freq_fBm) * _CloudsErosionIntensity.x, 1.0, 0.0, 1.0);
float heightGradient = GradientStep(height, GetHeightGradient(weather.b));
base_cloud *= heightGradient;
float cloud_coverage = saturate(1 - weather.r);
float densAlter = DensityAlter(1-height);
cloud_coverage = pow(cloud_coverage, densAlter);
//cloud_coverage = pow(cloud_coverage, Remap(height, 0.7, 0.8, 1.0, lerp(1.0, 0.5, 1.0)));
float cloudDensity = Remap(base_cloud, cloud_coverage, 1.0, 0.0, 1.0);
cloudDensity *= saturate(1-cloud_coverage);
//DETAIL
[branch]
if (details)
{
float mip2 = mip + (1-dist) * (_LODDistance);
coord = float4(pos * baseFreq * _DetailNoiseUV, mip2);
#ifdef ENVIRO_CURLNOISE
float2 curl_noise = tex2Dlod(_CurlNoise, float4 (coord.xy * 1.25, 0.0, 1.0)).rg;
coord.xy += curl_noise.rg * (1 - height);
#endif
coord.xyz += float3(_CloudsAnimation.x, _CloudsErosionIntensity.w, _CloudsAnimation.y);
float3 detailNoise = tex3Dlod(_DetailNoise, coord).rgb;
float high_freq_fBm = (detailNoise.r * 0.625) + (detailNoise.g * 0.25) + (detailNoise.b * 0.125);
float high_freq_noise_modifier = lerp(high_freq_fBm, 1.0f - high_freq_fBm, saturate((height) * 20));
cloudDensity = Remap(cloudDensity, high_freq_noise_modifier * _CloudsErosionIntensity.y, 1.0, 0.0, 1.0);
}
return cloudDensity;
}
// Lighting Energy Function
float GetLightEnergy(float3 p, float height_fraction, float dl, float ds_loded, float phase_probability, float cos_angle, float step_size, float brightness, float3 weather)
{
brightness *= 300 * weather.g;
float sc = lerp(dl * 0.25, _CloudsCoverageSettings.y, 0.75);
float s1 = lerp(_CloudsErosionIntensity.z * 0.75, _CloudsErosionIntensity.z, cos_angle);
float prim_att = exp(-s1 * dl);
float sec_att = exp(-s1 * sc) * 0.7;
float attenuation_probability = max(Remap(cos_angle, 0.5, 1.0, sec_att, sec_att * 0.25), prim_att) ;
float vertical_probability = pow(Remap(height_fraction, 0.07, 0.3, 0.1, 1.0), 0.8);
float depth = _CloudsLightingExtended.x * pow(lerp(0.25, ds_loded * 1.5, Remap(height_fraction,0.0,1.0,0.5,1.0)), Remap(height_fraction, 0.1, 1.0, 0.5, 0.75));
float in_scatter = depth;
in_scatter = 0.05 + saturate(in_scatter);
in_scatter = saturate(lerp(in_scatter, 0.45, cos_angle - 0.1));
float light_energy = attenuation_probability * in_scatter * vertical_probability * phase_probability * brightness;
return light_energy;
}
static const float shadowSampleDistance[6] = {0.0, 0.5, 1.0, 1.5, 2.0, 6.0};
static const float LightingInfluence[6] = { { 1.0f },{ 2.0f },{ 3.0f },{ 4.0f },{ 6.0f },{ 8.0f } };
// Lighting Sample Function
float GetDensityAlongRay(float3 pos, float3 PlanetCenter, float3 LightDirection, float3 weather, float dist)
{
float opticalDepth = 0.0;
[loop]
for (int i = 0; i < 6; i++)
{
float3 samplePoint = pos + LightDirection * shadowSampleDistance[i] * (512 * _CloudDensityScale.y);
float mip_offset = i * 0.5;
//if (opticalDepth < 0.3)
opticalDepth += CalculateCloudDensity(samplePoint, PlanetCenter, weather, mip_offset, dist, true) * LightingInfluence[i];
//else
// opticalDepth += CalculateCloudDensity(samplePoint, PlanetCenter, weather, mip_offset, dist, false) * LightingInfluence[i];
}
return opticalDepth;
}