yum-archive/2ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/2ner
a54b415
master
1#ifndef __FOG_INC 2#define __FOG_INC 3 4#include "audiolink.cginc" 5#include "cnlohr.cginc" 6#include "interpolators.cginc" 7#include "globals.cginc" 8#include "LightVolumes.cginc" 9 10#if defined(_RAYMARCHED_FOG) 11 12struct FogParams { 13 float3 color; 14 float direct_light_intensity; 15 float indirect_light_intensity; 16 float steps; 17 float y_cutoff; 18 texture2D dithering_noise; 19 float4 dithering_noise_texelsize; 20 texture3D density_noise; 21 float4 density_noise_scale; 22 float3 velocity; 23 // Physical description of the medium (all in metres or unit-less) 24 float mean_free_path; // ⟨s⟩ = 1 / σ_t 25 float albedo; // ω = σ_s / σ_t (0 … 1) 26 float g; // Henyey-Greenstein anisotropy (−1 … 1) 27 float height_scale; // H where ρ(y)=ρ₀·exp(−y/H) 28 float height_offset; 29 float turbulence; // Strength of noise modulation (0 … 1) 30 float step_size; 31 float step_growth; 32#if defined(_RAYMARCHED_FOG_EMITTER_TEXTURE) 33 texture2D emitter_texture; 34 float4 emitter_texture_texelsize; 35 float3 emitter_world_pos; 36 float3 emitter_normal; 37 float3 emitter_tangent; 38 float3 emitter_normal_x_tangent; 39 float2 emitter_scale; // [tangent scale in meters, bitangent scale in meters] 40 float2 emitter_scale_rcp; 41 float emitter_luminance; 42 float emitter_intensity; 43#endif 44#if defined(_RAYMARCHED_FOG_EMITTER_TEXTURE_WARPING) 45 float emitter_texture_warping_octaves; 46 float emitter_texture_warping_strength; 47 float emitter_texture_warping_scale; 48 float emitter_texture_warping_speed; 49#endif 50#if defined(_RAYMARCHED_FOG_DENSITY_EXPONENT) 51 float density_exponent; 52#endif 53}; 54 55#if defined(_RAYMARCHED_FOG_EMITTER_TEXTURE) 56// Returns weighted color 57float3 getEmitterData(FogParams p, float3 pp) 58{ 59 // Using identity a_parallel_to_b = (dot(a, b) / dot(b, b)) * b 60 // float3 along_tangent = dot(p - em_loc, em_tangent) * em_tangent; 61 // float3 along_normal_x_tangent = dot(p - em_loc, em_normal_x_tangent) * 62 // em_normal_x_tangent; 63 // Given that em_tangent and em_normal_x_tangent are normalized, and the fact 64 // that we really want uvs, we can simplify this: 65 float2 uv = float2(dot(pp - p.emitter_world_pos, p.emitter_normal_x_tangent), dot(pp - p.emitter_world_pos, p.emitter_tangent)); 66 uv *= p.emitter_scale_rcp; 67 uv *= 0.5; 68 uv += 0.5; 69 70 #if defined(_RAYMARCHED_FOG_EMITTER_TEXTURE_WARPING) 71 for (uint ii = 0; ii < p.emitter_texture_warping_octaves; ++ii) { 72 uv += p.dithering_noise.SampleLevel(bilinear_repeat_s, 73 uv * p.emitter_texture_warping_scale + _Time[0] * p.emitter_texture_warping_speed, 0).rgb 74 * p.emitter_texture_warping_strength; 75 } 76 #endif 77 78 bool in_range = uv.x < 1 && uv.y < 1 && uv.x > 0 && uv.y > 0; 79 [branch] 80 if (!in_range) { 81 return 0; 82 } 83 84 float4 c = p.emitter_texture.SampleLevel(linear_clamp_s, uv, 0); 85 return lerp(0, c.rgb, c.a); 86} 87#endif 88 89// --------------------------------------------------------------------------- 90// Henyey–Greenstein phase function 91static const float INV_FOUR_PI = 0.079577471545947667884f; // 1/(4π) 92 93inline float PhaseHG(float cosTheta, float g) 94{ 95 float g2 = g * g; 96 return INV_FOUR_PI * (1.0 - g2) / pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5); 97} 98 99struct FogResult { 100 float4 color; 101 float depth; 102}; 103 104float3 aces_filmic(float3 x) { 105 float a = 2.51f; 106 float b = 0.03f; 107 float c = 2.43f; 108 float d = 0.59f; 109 float e = 0.14f; 110 return saturate((x*(a*x+b))/(x*(c*x+d)+e)); 111} 112 113FogResult raymarched_fog(v2f i, f2f f, FogParams p) 114{ 115 float3 ro = _WorldSpaceCameraPos; 116 float3 rd = f.viewDir; 117 118 const float ro_epsilon = 1E-3; 119 ro += rd * ro_epsilon; 120 121 float4 clipPos = UnityObjectToClipPos(i.objPos); 122 float2 screen_uv = ComputeScreenPos(clipPos) / clipPos.w; 123 float zDepthFromMap = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screen_uv); 124 125 float linearZ = 126 GetLinearZFromZDepth_WorksWithMirrors(zDepthFromMap, screen_uv); 127 128 // Get intersection with plane at elevation y. 129 float plane_y = p.y_cutoff; 130 float distance_to_y = 1E5; 131 if (abs(rd.y) > 1E-6) { 132 float t = (plane_y - ro.y) / rd.y; 133 if (t > 0) { 134 distance_to_y = min(t, 1E5); 135 } 136 } 137 linearZ = min(linearZ, distance_to_y); 138 linearZ -= ro_epsilon; 139 140 float dither = p.dithering_noise.SampleLevel(point_repeat_s, 141 screen_uv * _ScreenParams.xy * p.dithering_noise_texelsize.xy, 0).r; 142 143 const float frame = ((float) AudioLinkData(ALPASS_GENERALVU + int2(1, 0)).x); 144 dither = frac(dither + PHI * frame); 145 146 // ----------------------------------------------------------------------- 147 // Loop-invariant values 148 float inv_mean_free_path = 1.0 / max(p.mean_free_path, 1e-4); 149 float turb_lo = 1.0 - 0.5 * p.turbulence; 150 float turb_hi = 1.0 + 0.5 * p.turbulence; 151 float3 time_offset = _Time[0] * p.velocity; 152 153 // Golden-ratio LCG seed 154 float dither_seq = frac(dither + PHI); 155 156 // Exponential stepping parameters 157 float step_size = p.step_size; 158 float step_growth = p.step_growth; 159 160 float3 pp = ro; 161 float max_dist = linearZ; 162 163 float T = 1; // Transmittance 164 float3 L = 0; // Accumulated radiance 165 float traveled = 0; 166 167 [loop] 168 for (uint ii = 0; ii < p.steps && traveled < max_dist; ++ii) 169 { 170 // Apply dithering to this step 171 float cur_dither = dither_seq; 172 float dithered_step = step_size * (cur_dither + 0.5); 173 float remaining = max_dist - traveled; 174 remaining = max(remaining, 0.1); 175 dithered_step = min(dithered_step, remaining); 176 177 // Advance position 178 pp += dithered_step * rd; 179 traveled += dithered_step; 180 181 // --- Density ---------------------------------------------------------- 182 float3 noise_coord = (pp + time_offset) * p.density_noise_scale.xyz; 183 float noise_sample = p.density_noise.SampleLevel(bilinear_repeat_s, noise_coord, 0).r; 184 float fbm_f = 2.0f; 185 float fbm_a = 0.5f; 186 noise_sample += p.density_noise.SampleLevel(bilinear_repeat_s, noise_coord * fbm_f, 0).r * fbm_a; 187 noise_sample *= 0.66666666f; 188 189 #if defined(_RAYMARCHED_FOG_DENSITY_EXPONENT) 190 // The expected value (EV) of `noise_sample` is 0.5. If we set it to 1.0f 191 // then exponentiate, the EV will remain closer to 0.5f. 192 noise_sample += 0.5f; 193 noise_sample = pow(noise_sample, p.density_exponent); 194 noise_sample -= 0.5f; 195 #endif 196 197 float noise_factor = lerp(turb_lo, turb_hi, noise_sample); 198 199 float height_factor = exp(-max(pp.y - p.height_offset, 0.0) / p.height_scale); 200 201 float sigma_t = noise_factor * height_factor * inv_mean_free_path; 202 float sigma_s = sigma_t * p.albedo; 203 204 // Analytic integration over the segment 205 float exp_term = exp(-sigma_t * dithered_step); 206 207 // --- Incoming radiance ------------------------------------------------ 208 float3 L_in; 209 210 // No need for directional SH coefficients. Skipping them saves 2 3D texture reads. 211 float3 l00 = LightVolumeSH_L0(pp); 212 float3 l01r = 0; 213 float3 l01g = 0; 214 float3 l01b = 0; 215 216 float3 indirect = LightVolumeEvaluate(float3(0, 1, 0), l00, l01r, l01g, l01b); 217 218 // Direct from the dominant realtime light 219 float3 to_light = (_WorldSpaceLightPos0.w == 0.0) ? normalize(_WorldSpaceLightPos0.xyz) 220 : normalize(_WorldSpaceLightPos0.xyz - pp); 221 float phase = PhaseHG(dot(to_light, rd), p.g); 222 float3 direct = _LightColor0.rgb * phase; 223 224 L_in = (direct * p.direct_light_intensity + 225 indirect * p.indirect_light_intensity) * p.color; 226 227#if defined(_RAYMARCHED_FOG_EMITTER_TEXTURE) 228 // 1. emitted radiance of the pixel ------------------------------------ 229 float3 Le = getEmitterData(p, pp) * p.emitter_luminance; // [W·sr⁻¹·m⁻²] 230 231 // 2. direction and phase term ----------------------------------------- 232 float3 w_e = normalize(p.emitter_world_pos - pp); // to pixel centre 233 float phase_e = PhaseHG(dot(w_e, rd), p.g); // same HG phase 234 235 // 3. pixel's apparent solid angle (flat-quadrilateral approx) --------- 236 float dist2 = dot(p.emitter_world_pos - pp, 237 p.emitter_world_pos - pp); 238 float pixel_area = 239 4.0f * p.emitter_scale.x * p.emitter_scale.y * 240 p.emitter_texture_texelsize.x * p.emitter_texture_texelsize.y; 241 float solid_ang = pixel_area / dist2; // Δω ≈ A / r² 242 243 // 4. additive in-scattered radiance from the display ------------------ 244 float3 L_em = Le * solid_ang * phase_e * p.emitter_intensity; 245 246 // Use baked luminance as a cheap proxy for shadowing from terrain. 247 float indirect_brightness = luminance(indirect); 248 L_in += L_em * indirect_brightness * indirect_brightness; 249#endif 250 251 // --- Accumulate radiance --------------------------------------------- 252 float scattering_integral = (sigma_s / sigma_t) * (1.0 - exp_term); 253 L += T * scattering_integral * L_in; 254 255 // Update transmittance 256 T *= exp_term; 257 258 // Early exit if virtually opaque 259 if (T < 1e-7) 260 break; 261 262 // Advance LCG for the next step 263 dither_seq += PHI; 264 if (dither_seq >= 1.0) dither_seq -= 1.0; 265 266 // Grow step size exponentially 267 step_size *= step_growth; 268 } 269 270 float4 color; 271 color.rgb = L; 272 color.a = 1 - T; // Alpha for proper compositing 273 274 FogResult r; 275 r.color = color; 276 277 //r.color.rgb = saturate(log(linearZ) / 5.0); 278 //r.color.rgb = float3(screen_uv, 0); 279 //r.color.a = d; 280 r.depth = 0.0001; // Very small depth value to render in front 281 return r; 282} 283 284#endif // _RAYMARCHED_FOG 285#endif // __FOG_INC