yum-archive/2ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum-archive/2ner

yumPush eyeVec into fragmenta54b415

master
9.3 KiB285 linesraw
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