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; }