yum-archive/Tooner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum-archive/Tooner

yumAdd some options to ds2_11 (terrain gimmick)358c53e

master
30.0 KiB971 linesraw
1#include "atrix256.cginc"
2#include "audiolink.cginc"
3#include "cnlohr.cginc"
4#include "globals.cginc"
5#include "fog.cginc"
6#include "interpolators.cginc"
7#include "iq_sdf.cginc"
8#include "math.cginc"
9#include "noise.cginc"
10#include "oklab.cginc"
11#include "poi.cginc"
12
13#ifndef __DOWNSTAIRS_02_INC
14#define __DOWNSTAIRS_02_INC
15
16#if defined(_GIMMICK_DS2)
17
18// All gimmicks here return this structure.
19struct Gimmick_DS2_Output {
20  float4 albedo;
21  float3 emission;
22  float3 normal;
23  float3 worldPos;
24  float4 fog;
25  float metallic;
26  float roughness;
27};
28
29
30float ds2_00_distance_from_sphere(float3 p, float3 c, float r)
31{
32  return length(p - c) - r;
33}
34
35float ds2_00_map(float3 p, out float which)
36{
37  float t = _Time.y;
38  float theta = sin(_Time[0]) / 2;
39  float2x2 rot = float2x2(
40    cos(theta), -sin(theta),
41    sin(theta), cos(theta));
42
43  which = 0;
44  float dist = 1000 * 1000 * 1000;
45  #define Y_STEPS 5
46  for (int y = 0; y < Y_STEPS; y++)
47  {
48    const int yy = y - Y_STEPS/2;
49    #define X_STEPS 5
50    for (int x = 0; x < X_STEPS; x++)
51    {
52      const int xx = x - X_STEPS/2;
53      float2 pp = float2(xx * 2, yy * 2);
54      pp = mul(rot, pp);
55      float radius = cos((x + y + _Time[0]) * 3.14159) * 0.5 + 1;
56      float sphere = ds2_00_distance_from_sphere(p, float3(pp.x, pp.y, 0.0), radius);
57      which = lerp(which, y * Y_STEPS + x, sphere < dist);
58      dist = min(dist, sphere);
59      dist += sin(5.0 * pp.x) * sin(5.0 * pp.y) * 0.5;
60    }
61  }
62
63  return dist;
64}
65
66float3 ds2_00_calc_normal(in float3 p)
67{
68  const float3 small_step = float3(1E-4, 0.0, 0.0);
69
70  float which;
71  float center = ds2_00_map(p, which);
72  float gradient_x = ds2_00_map(p + small_step.xyy, which) - center;
73  float gradient_y = ds2_00_map(p + small_step.yxy, which) - center;
74  float gradient_z = ds2_00_map(p + small_step.yyx, which) - center;
75
76  float3 normal = float3(gradient_x, gradient_y, gradient_z);
77
78  return normalize(normal);
79}
80
81bool __ds2_00_march(float3 ro, float3 rd, inout float3 normal, out float which)
82{
83  float total_distance_traveled = 0.0;
84  const float MINIMUM_HIT_DISTANCE = 0.001;
85  const float MAXIMUM_TRACE_DISTANCE = 1000.0;
86
87  #define DS2_00_MARCH_STEPS 10
88  float distance_to_closest;
89  float3 current_position;
90  for (int i = 0; i < DS2_00_MARCH_STEPS; i++)
91  {
92      current_position = ro + total_distance_traveled * rd;
93
94      distance_to_closest = ds2_00_map(current_position, which);
95
96      if (distance_to_closest < MINIMUM_HIT_DISTANCE) 
97      {
98        break;
99      }
100
101      if (total_distance_traveled > MAXIMUM_TRACE_DISTANCE)
102      {
103          break;
104      }
105      total_distance_traveled += distance_to_closest;
106  }
107
108  if (distance_to_closest < MINIMUM_HIT_DISTANCE) {
109    normal = ds2_00_calc_normal(current_position);
110    return true;
111  }
112
113  return false;
114}
115
116Gimmick_DS2_Output Gimmick_DS2_00(v2f i)
117{
118  float2 uv = i.uv0;
119  uv *= 2;
120  uv -= 1;
121  float2 warping_speed_vector = normalize(float2(97, 101));
122  const float t = _Time[0] * 10;
123  const float warping_strength_anim = smoothstep(0, 1, sin(t*0.31));
124  for (uint ii = 0; ii < _Gimmick_DS2_00_Domain_Warping_Octaves; ii++)
125  {
126      float2 noise = _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, uv * _Gimmick_DS2_00_Domain_Warping_Scale + _Time[0] * _Gimmick_DS2_00_Domain_Warping_Speed * warping_speed_vector, 0);
127      uv += noise * _Gimmick_DS2_00_Domain_Warping_Strength * warping_strength_anim;
128  }
129
130  float3 camera_position = float3(0.0, 0.0, -5.0);
131  float3 ro = camera_position;
132  float3 rd = float3(uv.x, uv.y, 1.0);
133
134  float3 normal;
135  float which;
136  bool hit = __ds2_00_march(ro, rd, normal, which);
137
138  float3 shaded_color = LRGBtoOKLCH(float3(1, .05, .12));
139  shaded_color[0] += smoothstep(-1, 1, sin(t*2.3 + which * TAU * 1.1)) * .5;
140  shaded_color[2] += smoothstep(-1, 1, sin(t*2.9 + which * TAU * 1.1)) * .05;
141  shaded_color = OKLCHtoLRGB(shaded_color);
142  
143  shaded_color *= hit;
144
145  Gimmick_DS2_Output o;
146  o.albedo = float4(shaded_color * 5, 1.0);
147  o.emission = shaded_color;
148  o.fog = 0;
149  o.normal = normal;
150  o.metallic = 0;
151  o.roughness = 1;
152  o.worldPos = i.worldPos;
153  return o;
154}
155
156float ds2_01_map(float3 p)
157{
158  p.z -= _Gimmick_DS2_01_Radius;
159  return distance_from_sphere(p, _Gimmick_DS2_01_Radius);
160}
161
162float3 ds2_01_nudge_p(float3 p, float3 e)
163{
164  return p + sin(e*TAU+_Time[0]*4) * _Gimmick_DS2_01_Radius * .5;
165}
166
167float ds2_01_map_dr(
168    float3 p,
169    float3 period,
170    float3 count,
171    out float3 which
172    )
173{
174  which = round(p / period);
175  // Direction to nearest neighboring cell.
176  float3 min_d = p - period * which;
177  float3 o = sign(min_d);
178
179  float d = 1E9;
180  float3 which_tmp = which;
181  for (uint xi = 0; xi < 2; xi++)
182  for (uint yi = 0; yi < 2; yi++)
183  {
184    float3 rid = which + float3(xi, yi, 0) * o;
185    rid = clamp(rid, ceil(-(count)*0.5), floor((count-1)*0.5));
186    float3 r = p - period * rid;
187    float3 e = float3(
188        rand3(rid / 100.0),
189        rand3(rid / 100.0 + 1),
190        rand3(rid / 100.0 + 2));
191    r = ds2_01_nudge_p(r, e);
192    float cur_d = ds2_01_map(r);
193    which_tmp = cur_d < d ? rid : which_tmp;
194    d = min(d, cur_d);
195  }
196
197  which = which_tmp;
198  return d;
199}
200
201
202float3 ds2_01_calc_normal(float3 p, float3 period, float3 count)
203{
204  const float3 small_step = float3(1E-5, 0.0, 0.0);
205  float3 which;
206  float center = ds2_01_map_dr(p, period, count, which);
207  return normalize(float3(
208    ds2_01_map_dr(p + small_step.xyz, period, count, which) - center,
209    ds2_01_map_dr(p + small_step.zxy, period, count, which) - center,
210    ds2_01_map_dr(p + small_step.yzx, period, count, which) - center
211  ));
212}
213
214Gimmick_DS2_Output Gimmick_DS2_01(inout v2f i)
215{
216  float3 camera_position = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
217  float3 ro = i.objPos;
218  float3 rd = normalize(i.objPos - camera_position);
219
220  float2 warping_speed_vector = normalize(float2(97, 101));
221  for (uint ii = 0; ii < _Gimmick_DS2_01_Domain_Warping_Octaves; ii++)
222  {
223      float2 noise = _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, ro.xy * _Gimmick_DS2_01_Domain_Warping_Scale + _Time[0] * _Gimmick_DS2_01_Domain_Warping_Speed * warping_speed_vector, 0);
224      ro.xy += noise * _Gimmick_DS2_01_Domain_Warping_Strength;
225  }
226
227#define DS2_01_MARCH_STEPS 8
228  float total_distance_traveled = 0.0;
229  const float MINIMUM_HIT_DISTANCE = 1E-4;
230  const float MAXIMUM_TRACE_DISTANCE = .1;
231  float distance_to_closest;
232  float3 which;
233  for (uint ii = 0; ii < DS2_01_MARCH_STEPS; ii++)
234  {
235    float3 current_position = ro + total_distance_traveled * rd;
236    distance_to_closest = ds2_01_map_dr(current_position, 
237      _Gimmick_DS2_01_Period.xyz, _Gimmick_DS2_01_Count.xyz, which);
238    total_distance_traveled += distance_to_closest;
239    if (distance_to_closest < MINIMUM_HIT_DISTANCE || 
240        total_distance_traveled > MAXIMUM_TRACE_DISTANCE) {
241      break;
242    }
243  }
244
245  float3 normal = i.normal;
246  bool hit = distance_to_closest < MINIMUM_HIT_DISTANCE;
247  float3 color = LRGBtoOKLCH(float3(0.7, 0, 0));
248  color[0] += _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, ((which.xy / _Gimmick_DS2_01_Count.xy) * 1.5 + _Time[0] * .5) / 10, 0);
249  color[2] = ign(which.xy + _Gimmick_DS2_01_Count.xy*2) * TAU * .1;
250  color[2] += _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, ((which.xy / _Gimmick_DS2_01_Count.xy) * 1.5 + _Time[0] * .5) / 100, 0) * 5;
251  color = OKLCHtoLRGB(color);
252  if (hit) {
253    normal = ds2_01_calc_normal(ro + total_distance_traveled * rd, 
254      _Gimmick_DS2_01_Period.xyz, _Gimmick_DS2_01_Count.xyz);
255    normal = UnityObjectToWorldNormal(normal);
256  }
257
258  Gimmick_DS2_Output o;
259  o.albedo = hit ? float4(color, 1) : 0;
260  o.emission = o.albedo;
261  o.fog = 0;
262  o.normal = normal;
263  o.metallic = 0;
264  o.roughness = 0;
265  o.worldPos = i.worldPos;
266  return o;
267}
268
269// which == -10 -> capped cylinder
270// which == 1 -> cylinder
271float ds2_10_map_repeated(float3 p, out float which)
272{
273  float depth = .2;
274  float d0 = distance_from_capped_cylinder(abs(p) - float3(0, .45, depth * .5), .05, .02);
275  float d1 = distance_from_cylinder(abs(p) - float3(0, 0, depth * .5), float3(0, 0, .015));
276  which = d0 < d1 ? -10 : 1;
277  return min(d0, d1);
278}
279
280float ds2_10_map_dr(
281    float3 p,
282    float3 period,
283    float3 count,
284    out float which
285    )
286{
287  which = round(p / period);
288  float3 rid = clamp(which, ceil(-(count)*0.5), floor((count-1)*0.5));
289  float3 r = p - period * rid;
290  return ds2_10_map_repeated(r, which);
291}
292
293float ds2_10_map(float3 p, out float which)
294{
295  float depth = .2;
296  which = -10;
297
298  // Create frame for lights.
299  float d0 = distance_from_box(p - float3(0, 0, depth), float3(.50, .50, depth));
300  float d1 = distance_from_box(p - float3(0, 0, 0), float3(.45, .45, depth));
301  float d2 = op_sub(d1, d0);
302
303  // Create lights.
304  float light_spacing = .17;
305  float which_tmp;
306  float d3 = ds2_10_map_dr(p, float3(light_spacing, 1, 1), float3(5, 1, 1), which_tmp);
307  which = d3 < d2 ? which_tmp : which;
308
309  return min(d2, d3);
310}
311
312float3 ds2_10_calc_normal(float3 p)
313{
314  float3 small_step = float3(1E-5, 0.0, 0.0);
315  float which;
316  float center = ds2_10_map(p, which);
317  return normalize(float3(
318    ds2_10_map(p + small_step.xyz, which) - center,
319    ds2_10_map(p + small_step.zxy, which) - center,
320    ds2_10_map(p + small_step.yzx, which) - center
321  ));
322}
323
324Gimmick_DS2_Output Gimmick_DS2_10(inout v2f i)
325{
326  float3 camera_position = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
327  float3 ro = i.objPos;
328  float3 rd = normalize(i.objPos - camera_position);
329
330  #define DS2_10_MARCH_STEPS 30
331  float total_distance_traveled = 0.0;
332  const float MINIMUM_HIT_DISTANCE = 1E-3;
333  const float MAXIMUM_TRACE_DISTANCE = 1;
334  float distance_to_closest;
335  float which;
336  for (uint ii = 0; ii < DS2_10_MARCH_STEPS; ii++)
337  {
338    float3 current_position = ro + total_distance_traveled * rd;
339    distance_to_closest = ds2_10_map(current_position, which);
340    total_distance_traveled += distance_to_closest;
341    if (distance_to_closest < MINIMUM_HIT_DISTANCE || 
342        total_distance_traveled > MAXIMUM_TRACE_DISTANCE) {
343      break;
344    }
345  }
346
347  float3 normal = i.normal;
348  const float3 final_position = ro + total_distance_traveled * rd;
349  bool hit = distance_to_closest < MINIMUM_HIT_DISTANCE;
350  float3 color = (which == -10 ? 0.01 : 1.5);
351  if (hit) {
352    normal = ds2_10_calc_normal(final_position);
353    normal = UnityObjectToWorldNormal(normal);
354  }
355
356  Gimmick_DS2_Output o;
357  //o.albedo = hit ? float4(color, 1) : 0;
358  o.albedo = hit ? 1 : 0;
359  o.emission = color;
360  o.fog = 0;
361  o.normal = normal;
362  o.metallic = 0;
363  o.roughness = 0.3;
364  o.worldPos = mul(unity_ObjectToWorld, float4(final_position, 1));
365  return o;
366}
367
368float ds2_02_map(float3 p)
369{
370  float edge = _Gimmick_DS2_02_Edge_Length;
371  float thickness = edge*10;
372  return distance_from_round_box(p - float3(0, 0, thickness*.99), float3(edge, edge, thickness), edge * .1);
373}
374
375float3 ds2_02_nudge_p(float3 p, float3 which)
376{
377  float noise = _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, which.xy * _Gimmick_DS2_02_Period.xy * .2 + _Time[0] * .1, 0);
378  return p - float3(0, 0, noise * .01);
379  //return p + sin(e*TAU+_Time[0]*4) * _Gimmick_DS2_02_Edge_Length * .2 - noise;
380  //return p;
381}
382
383float ds2_02_map_dr(
384    float3 p,
385    float3 period,
386    float3 count,
387    out float3 which
388    )
389{
390  which = floor(p / period);
391  // Direction to nearest neighboring cell.
392  float3 min_d = p - period * which;
393  float3 o = sign(min_d);
394
395  float d = 1E9;
396  float3 which_tmp = which;
397  for (uint xi = 0; xi < 1; xi++)
398  for (uint yi = 0; yi < 1; yi++)
399  {
400    float3 rid = which + float3(xi, yi, 0) * o;
401    rid = clamp(rid, ceil(-(count)*0.5), floor((count-1)*0.5));
402    float3 r = p - period * rid;
403    r = ds2_02_nudge_p(r, rid);
404    float cur_d = ds2_02_map(r);
405    which_tmp = cur_d < d ? rid : which_tmp;
406    d = min(d, cur_d);
407  }
408
409  which = which_tmp;
410  return d;
411}
412
413float3 ds2_02_calc_normal(float3 p)
414{
415  float3 small_step = float3(1E-5, 0.0, 0.0);
416  float3 which;
417  float center = ds2_02_map_dr(p, _Gimmick_DS2_02_Period.xyz, _Gimmick_DS2_02_Count.xyz, which);
418  return normalize(float3(
419    ds2_02_map_dr(p + small_step.xyz, _Gimmick_DS2_02_Period.xyz, _Gimmick_DS2_02_Count.xyz, which) - center,
420    ds2_02_map_dr(p + small_step.zxy, _Gimmick_DS2_02_Period.xyz, _Gimmick_DS2_02_Count.xyz, which) - center,
421    ds2_02_map_dr(p + small_step.yzx, _Gimmick_DS2_02_Period.xyz, _Gimmick_DS2_02_Count.xyz, which) - center
422  ));
423}
424
425Gimmick_DS2_Output Gimmick_DS2_02(inout v2f i)
426{
427  float3 camera_position = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
428  float3 ro = i.objPos;
429  float3 rd = normalize(i.objPos - camera_position);
430
431  float2 warping_speed_vector = normalize(float2(97, 101));
432  for (uint ii = 0; ii < _Gimmick_DS2_02_Domain_Warping_Octaves; ii++)
433  {
434      float2 noise = _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, ro.xy * _Gimmick_DS2_02_Domain_Warping_Scale + _Time[0] * _Gimmick_DS2_02_Domain_Warping_Speed * warping_speed_vector, 0);
435      ro.xy += noise * _Gimmick_DS2_02_Domain_Warping_Strength;
436  }
437
438  #define DS2_02_MARCH_STEPS 40
439  float total_distance_traveled = 0.0;
440  const float MINIMUM_HIT_DISTANCE = 1E-4;
441  const float MAXIMUM_TRACE_DISTANCE = 1E-1;
442  float distance_to_closest;
443  float3 which;
444  for (uint ii = 0; ii < DS2_02_MARCH_STEPS; ii++)
445  {
446    float3 current_position = ro + total_distance_traveled * rd;
447    distance_to_closest = ds2_02_map_dr(current_position, _Gimmick_DS2_02_Period.xyz, _Gimmick_DS2_02_Count.xyz, which);
448    total_distance_traveled += distance_to_closest;
449    if (distance_to_closest < MINIMUM_HIT_DISTANCE || 
450        total_distance_traveled > MAXIMUM_TRACE_DISTANCE) {
451      break;
452    }
453  }
454
455  bool hit = distance_to_closest < MINIMUM_HIT_DISTANCE;
456  float3 final_position = ro + total_distance_traveled * rd;
457  float3 normal = hit ? UnityObjectToWorldNormal(ds2_02_calc_normal(final_position)) : i.normal;
458
459  float3 light_dir = normalize(float3(0.5, -0.5, -0.5));
460  float3 light_color = float3(1, 1, 1);
461  float ndotl = saturate(dot(normal, light_dir));
462  float wrap_factor = 0.7;
463  float4 wrapped = pow(max(1E-4, (ndotl + wrap_factor) / (1 + wrap_factor)), 1 + wrap_factor);
464  float3 light_intensity = light_color * wrapped;
465  float3 color = hit ? 1 : 0;
466  color *= _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, which.xy * _Gimmick_DS2_02_Period.xy * .2 + _Time[0] * warping_speed_vector * .01, 0);
467  // Reinterpret greyscale as OKLCH.
468  //color = saturate(color * 2 - (sin(_Time[0] + 1) * .5 + .6));
469  color = saturate(color * 2 - 0.4);
470  float hue_noise = _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, which.xy * _Gimmick_DS2_02_Period.xy * .2 - _Time[0] * warping_speed_vector.yx * .02, 0);
471  color = OKLCHtoLRGB(float3(
472    color.x * 20,
473    color.x * 10,
474    color.x * TAU * .2 + hue_noise * 10 + _Time[0] * 10
475  ));
476
477  color = max(color, 0.005);
478  color *= light_intensity;
479
480  Gimmick_DS2_Output o;
481  o.albedo = float4(color, 1);
482  o.emission = o.albedo;
483  o.fog = 0;
484  o.normal = normal;
485  o.metallic = 0;
486  o.roughness = 0;
487  // Depth gets all fucked up unless we use i.objPos instead of ro, which is domain warped.
488  o.worldPos = mul(unity_ObjectToWorld, float4(i.objPos + rd * total_distance_traveled, 1));
489  return o;
490}
491
492float ds2_03_map(float3 p, float3 rid)
493{
494  float edge = _Gimmick_DS2_03_Edge_Length;
495  float thickness = edge * .5;
496
497  float3 pp = p - float3(0, 0, thickness*1.5);
498
499  float wave_str = 0;
500  float t = _Time[3];
501  float3 noise = (_Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, rid.xy * .001 + t *.001, 0) * 2 - 1);
502  wave_str += noise.x;
503  float4 quat = get_quaternion(normalize(noise*2-1), wave_str * PI);
504  pp = rotate_vector(pp, quat);
505
506  return distance_from_hex_prism(pp, float2(edge, thickness));
507}
508
509float3 ds2_03_nudge_p(float3 p, float3 which)
510{
511  return p - float3(_Gimmick_DS2_03_Period.x * 0.65, _Gimmick_DS2_03_Period.y * 0.5, 0);
512}
513
514float ds2_03_map_dr(
515    float3 p,
516    float3 period,
517    float3 count,
518    out float3 which
519    )
520{
521  which = floor(p / period);
522  // Direction to nearest neighboring cell.
523  float3 min_d = p - period * which;
524  float3 o = sign(min_d);
525
526  float d = 1E9;
527  float3 which_tmp = which;
528  for (uint xi = 0; xi < 1; xi++)
529  {
530    float3 rid = which + float3(xi, 0, 0) * o;
531    rid = clamp(rid, ceil(-(count)*0.5), floor((count-1)*0.5));
532    float3 r = p - period * rid;
533    r = ds2_03_nudge_p(r, rid);
534    float cur_d = ds2_03_map(r, rid);
535    which_tmp = cur_d < d ? rid : which_tmp;
536    d = min(d, cur_d);
537  }
538
539  which = which_tmp;
540  return d;
541}
542
543float3 ds2_03_calc_normal(float3 p, float3 period, float3 count)
544{
545  float3 small_step = float3(1E-5, 0.0, 0.0);
546  float3 which;
547  float center = ds2_03_map_dr(p, period, count, which);
548  return normalize(float3(
549    ds2_03_map_dr(p + small_step.xyz, period, count, which) - center,
550    ds2_03_map_dr(p + small_step.zxy, period, count, which) - center,
551    ds2_03_map_dr(p + small_step.yzx, period, count, which) - center
552  ));
553}
554
555Gimmick_DS2_Output Gimmick_DS2_03(inout v2f i)
556{
557  float3 camera_position = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
558  float3 ro = i.objPos;
559  float3 rd = normalize(i.objPos - camera_position);
560
561  #define DS2_03_MARCH_STEPS 2
562  float total_distance_traveled = 0.0;
563  const float MINIMUM_HIT_DISTANCE = 2E-4;
564  const float MAXIMUM_TRACE_DISTANCE = 1E-1;
565  const float3 period = float3(
566    _Gimmick_DS2_03_Period.x * 2,
567    _Gimmick_DS2_03_Period.yz);
568  const float3 count = float3(
569    _Gimmick_DS2_03_Count.x * 0.5,
570    _Gimmick_DS2_03_Count.yz);
571
572  const float overstep_amount = 3;
573  bool hit1;
574  float total_distance_traveled1 = 0;
575  float3 which1;
576  {
577    float distance_to_closest1;
578    for (uint ii = 0; ii < DS2_03_MARCH_STEPS; ii++)
579    {
580      float3 current_position = ro + total_distance_traveled1 * rd;
581      distance_to_closest1 = ds2_03_map_dr(current_position, period, count, which1)*overstep_amount;
582      total_distance_traveled1 += distance_to_closest1;
583      if (distance_to_closest1 < MINIMUM_HIT_DISTANCE || 
584          total_distance_traveled1 > MAXIMUM_TRACE_DISTANCE) {
585        break;
586      }
587    }
588
589    hit1 = distance_to_closest1 < MINIMUM_HIT_DISTANCE;
590  }
591
592  bool hit2;
593  float total_distance_traveled2 = 0;
594  float3 which2;
595  {
596    ro.xy += period.xy * .5;
597    float distance_to_closest2;
598    for (uint ii = 0; ii < DS2_03_MARCH_STEPS; ii++)
599    {
600      float3 current_position = ro + total_distance_traveled2 * rd;
601      distance_to_closest2 = ds2_03_map_dr(current_position, period, count, which2)*overstep_amount;
602      total_distance_traveled2 += distance_to_closest2;
603      if (distance_to_closest2 < MINIMUM_HIT_DISTANCE || 
604          total_distance_traveled2 > MAXIMUM_TRACE_DISTANCE) {
605        break;
606      }
607    }
608
609    hit2 = distance_to_closest2 < MINIMUM_HIT_DISTANCE;
610  }
611
612  float3 final_position1 = i.objPos + total_distance_traveled1 * rd;
613  float3 final_position2 = (i.objPos + float3(period.xy * .5, 0)) + total_distance_traveled2 * rd;
614
615  float3 normal1 = hit1 ? UnityObjectToWorldNormal(ds2_03_calc_normal(final_position1, period, count)) : i.normal;
616  float3 normal2 = hit2 ? UnityObjectToWorldNormal(ds2_03_calc_normal(final_position2, period, count)) : i.normal;
617
618  float3 final_position;
619  float3 normal;
620  if (hit1 && hit2) {
621    final_position = total_distance_traveled1 < total_distance_traveled2 ? final_position1 : final_position2;
622    normal = total_distance_traveled1 < total_distance_traveled2 ? normal1 : normal2;
623  } else if (hit1) {
624    final_position = final_position1;
625    normal = normal1;
626  } else if (hit2) {
627    final_position = final_position2;
628    normal = normal2;
629  } else {
630    final_position = i.objPos;
631    normal = i.normal;
632  }
633  bool hit = hit1 || hit2;
634
635  float3 final_pos_world = mul(unity_ObjectToWorld, float4(final_position, 1));
636
637  float3 light_dir = normalize(float3(0.5, -0.5, -0.5));
638  float3 light_color = float3(1, 1, 1);
639  float ndotl = saturate(dot(normal, light_dir));
640  float wrap_factor = 0.7;
641  float4 wrapped = pow(max(1E-4, (ndotl + wrap_factor) / (1 + wrap_factor)), 1 + wrap_factor);
642  float3 light_intensity = light_color * wrapped;
643
644  float3 color = hit ? light_intensity : 0;
645
646  Gimmick_DS2_Output o;
647  o.albedo = float4(color, 1);
648  //o.emission = o.albedo;
649  o.emission = 0;
650  o.fog = 0;
651  o.normal = normal;
652  o.metallic = hit;
653  o.roughness = 0.1;
654  o.worldPos = final_pos_world;
655  return o;
656}
657
658float ds2_11_height(float2 p)
659{
660  float sc = .4;
661  float sc_rcp = 2.5;
662  float2 offset = _Gimmick_DS2_11_Offset.xz * _Gimmick_DS2_11_Simulation_Scale;
663
664  float2 pp = (p - offset) * sc_rcp;
665  p /= _Gimmick_DS2_11_Simulation_Scale;
666  pp /= _Gimmick_DS2_11_Simulation_Scale;
667#define _GIMMICK_DS2_11_TEXTURE_NOISE
668#if defined(_GIMMICK_DS2_11_TEXTURE_NOISE)
669  pp *= .01;
670#endif
671
672  float h = 0;
673  float hsc = _Gimmick_DS2_11_Height_Scale;
674  float sc_hsc = sc * hsc;
675  uint octaves = _Gimmick_DS2_11_Octaves;
676  float alpha = _Gimmick_DS2_11_Alpha;
677  float alpha_rcp = 1 / alpha;
678  float alpha_i = 1;
679  float alpha_rcp_i = 1;
680  for (uint i = 0; i < octaves; i++) {
681#if defined(_GIMMICK_DS2_11_TEXTURE_NOISE)
682    float noise = _Gimmick_DS2_11_FBM.SampleLevel(linear_repeat_s, pp * alpha_rcp_i, 0);
683#else
684    float noise = perlin_noise(pp * alpha_rcp_i);
685#endif
686    h += noise * sc_hsc * alpha_i;
687    alpha_i *= alpha;
688    alpha_rcp_i *= alpha_rcp;
689  }
690  // The sum of the series k^-i is 1 / (1 - k^-1)
691  h /= 1 / (1 - alpha);
692  h *= h;
693
694#if 0
695  // `scale_factor` goes from [0, 1] based on radius. 0 at center, 1 at infinity.
696  float2 center = p;
697  float scale_factor = 1 - exp(-dot(center, center) * 16);
698  h *= scale_factor;
699  h = ((h - (1 - scale_factor) * sc_hsc * .15) + .015) * _Gimmick_DS2_11_Simulation_Scale;
700#else
701  float2 center = p;
702  float scale_factor = exp(-dot(center, center) * _Gimmick_DS2_11_Valley_Power);
703  h -= scale_factor * sc_hsc * _Gimmick_DS2_11_Valley_Depth * _Gimmick_DS2_11_Simulation_Scale;
704  h += sc_hsc * _Gimmick_DS2_11_Offset.y * _Gimmick_DS2_11_Simulation_Scale;
705#endif
706
707  h += 1;
708  h = pow(h, _Gimmick_DS2_11_Height_Power);
709  h -= 1;
710
711  return h;
712}
713
714float3 ds2_11_calc_normal(float3 p)
715{
716  float epsilon = _Gimmick_DS2_11_Normal_Epsilon * length(_WorldSpaceCameraPos - p);
717#if 0
718  // 4-point anti aliasing in an X shape with full central differences. 16 taps.
719  float3 result = 0;
720  for (uint i = 0; i < 4; i++) {
721    float2 pp = p.xz + epsilon * (float2(i % 2, (i/2) % 2) - .5) * 2;
722    result += float3(
723      ds2_11_height(pp - float2(epsilon, 0)) - ds2_11_height(pp + float2(epsilon, 0)),
724      2 * epsilon,
725      ds2_11_height(pp - float2(0, epsilon)) - ds2_11_height(pp + float2(0, epsilon))
726    );
727  }
728  return normalize(result);
729#elif 1
730  // Full central differences. 4 taps.
731  return normalize(float3(
732    ds2_11_height(p.xz - float2(epsilon, 0)) - ds2_11_height(p.xz + float2(epsilon, 0)),
733    2 * epsilon,
734    ds2_11_height(p.xz - float2(0, epsilon)) - ds2_11_height(p.xz + float2(0, epsilon))
735  ));
736#elif 0
737  // Abridged central differences along stochastic diagonal. 6 taps.
738  float noise = _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, p.xz * 1000, 0) * TAU;
739  float2 axis = float2(cos(noise), sin(noise));
740  float2 p0 = p.xz + (epsilon * axis);
741  float2 p1 = p.xz - (epsilon * axis);
742  float c0 = ds2_11_height(p0);
743  float c1 = ds2_11_height(p1);
744  float3 n0 = float3(
745    ds2_11_height(p0 - float2(epsilon, 0)) - c0,
746    epsilon,
747    ds2_11_height(p0 - float2(0, epsilon)) - c0
748  );
749  float3 n1 = float3(
750    ds2_11_height(p1 - float2(epsilon, 0)) - c1,
751    epsilon,
752    ds2_11_height(p1 - float2(0, epsilon)) - c1
753  );
754  return normalize(n0 + n1);
755#elif 0
756  // Full central differences rotated a random amount about the original point. 4 taps.
757  float3 pp = p * 64;
758  float noise = _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, pp.xz, 0) * TAU;
759  float2 axis = float2(cos(noise), sin(noise));
760  float2 p0 = p.xz + (epsilon * axis) * 1.20710678 * 0;
761  float3 n0 = float3(
762    ds2_11_height(p0 - float2(epsilon, 0)) - ds2_11_height(p0 + float2(epsilon, 0)),
763    2 * epsilon,
764    ds2_11_height(p0 - float2(0, epsilon)) - ds2_11_height(p0 + float2(0, epsilon))
765  );
766  return normalize(n0);
767#elif 1
768  // Abridged central differences along diagonal oriented tangent to circle centered at the origin. 6 taps.
769  float2 n2 = normalize(p.xz);
770  float2 ortho = float2(-n2.y, n2.x);
771  float2 p0 = p.xz + (epsilon * .7071 * ortho);
772  float2 p1 = p.xz - (epsilon * .7071 * ortho);
773  float c0 = ds2_11_height(p0);
774  float c1 = ds2_11_height(p1);
775  float3 n0 = float3(
776    ds2_11_height(p0 - float2(epsilon, 0)) - c0,
777    epsilon,
778    ds2_11_height(p0 - float2(0, epsilon)) - c0
779  );
780  float3 n1 = float3(
781    ds2_11_height(p1 - float2(epsilon, 0)) - c1,
782    epsilon,
783    ds2_11_height(p1 - float2(0, epsilon)) - c1
784  );
785  return normalize(n0 + n1);
786#elif 0
787  // Abridged central differences along diagonal oriented normal to circle centered at the origin. 6 taps.
788  float2 n2 = normalize(p.xz);
789  float2 p0 = p.xz + (epsilon * .5 * n2);
790  float2 p1 = p.xz - (epsilon * .5 * n2);
791  float c0 = ds2_11_height(p0);
792  float c1 = ds2_11_height(p1);
793  float3 n0 = normalize(float3(
794    c0 - ds2_11_height(p0 - float2(epsilon, 0)),
795    epsilon,
796    c0 - ds2_11_height(p0 - float2(0, epsilon))
797  ));
798  float3 n1 = normalize(float3(
799    c1 - ds2_11_height(p1 - float2(epsilon, 0)),
800    epsilon,
801    c1 - ds2_11_height(p1 - float2(0, epsilon))
802  ));
803  return normalize(n0 + n1);
804#else
805  // Abridged central differences. 3 taps.
806  float center = ds2_11_height(p.xz);
807  return normalize(float3(
808    ds2_11_height(p.xz - float2(epsilon, 0)) - center,
809    2 * epsilon,
810    ds2_11_height(p.xz - float2(0, epsilon)) - center
811  ));
812#endif
813}
814
815Gimmick_DS2_Output Gimmick_DS2_11(inout v2f i, ToonerData tdata)
816{
817  float3 camera_position = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
818  float3 rd = normalize(i.objPos - camera_position);
819  float3 ro = camera_position;
820
821  // Raytrace to intersection with sphere of radius _Gimmick_DS2_11_March_Initial_Offset centered at the object space origin.
822  {
823    float r = _Gimmick_DS2_11_March_Initial_Offset * _Gimmick_DS2_11_Simulation_Scale;
824    float3 L = ro; // Sphere center is at the origin
825    float b = 2.0 * dot(rd, L);
826    float c = dot(L, L) - r * r;
827    float discriminant = b * b - 4.0 * c;
828
829    // If discriminant is negative, the ray does not intersect the sphere
830    if (discriminant > 0.0)
831    {
832      // Compute the two points of intersection
833      float sqrt_discriminant = sqrt(discriminant);
834      float t0 = (-b - sqrt_discriminant) * 0.5;
835      float t1 = (-b + sqrt_discriminant) * 0.5;
836
837      // Choose the nearest positive t
838      float t_sphere = (t0 > 0.0) ? t0 : ((t1 > 0.0) ? t1 : -1.0);
839
840      // If both t0 and t1 are negative, the sphere is behind the ray origin
841      if (t_sphere > 0.0)
842      {
843          ro += rd * t_sphere;
844      }
845    }
846  }
847
848  // 180 degrees is pi radians
849  // (d/180)*pi
850  [branch]
851  if (dot(rd, UnityObjectToWorldNormal(float3(0, 1, 0))) > _Gimmick_DS2_11_Early_Exit_Cutoff_Cos_Theta) {
852    return (Gimmick_DS2_Output)0;
853  }
854
855  [branch]
856  if (_Gimmick_DS2_11_Distance_Culling_Enable) {
857    float activation_y = _Gimmick_DS2_11_Activation_Y;
858    [branch]
859    if (getCenterCamPos().y > activation_y) {
860      return (Gimmick_DS2_Output)0;
861    }
862  }
863
864  float perspective_divide = 1.0 / i.pos.w;
865  float2 screen_uv = i.screenPos.xy * perspective_divide * _ScreenParams.xy * _Gimmick_DS2_Noise_TexelSize.xy;
866  const float noise = _Gimmick_DS2_Noise.SampleLevel(point_repeat_s, screen_uv, 0);
867  const float frame = ((float) AudioLinkData(ALPASS_GENERALVU + int2(1, 0)).x);
868  const float tnoise = frac(noise + frame * PHI);
869
870  float t = 0.0;
871  float dt0 = _Gimmick_DS2_11_March_Initial_Step_Size * _Gimmick_DS2_11_Simulation_Scale;
872  float dt = dt0;
873  // last height, last y
874  float lh = 0;
875  float ly = 0;
876
877  // https://iquilezles.org/articles/terrainmarching/
878  bool hit = false;
879  float3 p;
880  float h;
881  for (uint ii = 0; ii < _Gimmick_DS2_11_March_Steps; ii++) {
882    p = ro + rd * t;
883    h = ds2_11_height(p.xz);
884    if (p.y < h) {
885      break;
886    }
887    t += dt;
888    dt = dt0 * (ii+1);
889    lh = h;
890    ly = p.y;
891  }
892  [branch]
893  if (p.y < h) {
894    hit = true;
895    t = t - dt + dt * (lh - ly) / (p.y - ly - h + lh);
896
897    // Backtrack to find a closer intersection point using binary search
898    float t0 = t;
899    //p = ro + rd * t;
900    //float t1 = t + dt * sign(h - ds2_11_height(p.xz));
901    float t1 = t + dt * .5;
902    for (uint j = 0; j < _Gimmick_DS2_11_March_Backtrack_Steps; j++) {
903      float tm = (t0 + t1) * 0.5;
904      float3 pm = ro + rd * tm;
905      float hm = ds2_11_height(pm.xz);
906      t1 = (pm.y < hm) ? tm : t1;
907      t0 = (pm.y < hm) ? t0 : tm;
908    }
909    t = t1;  // Refined intersection time
910    p = ro + rd * t;
911  }
912
913  float3 final_pos = ro + t * rd + (1 - hit) * rd * 1E2;
914  float3 normal = UnityObjectToWorldNormal(ds2_11_calc_normal(final_pos));
915  float3 final_pos_world = mul(unity_ObjectToWorld, float4(final_pos, 1));
916  float4 final_color = 1;
917
918  float snowline_noise = 0;
919  float alpha = 0.3;
920  float alpha_rcp = 1 / alpha;
921  for (uint ii = 0; ii < _Gimmick_DS2_11_Snowline_Octaves; ii++) {
922    snowline_noise += _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, final_pos.xz * _Gimmick_DS2_11_Snowline_Noise_Scale * pow(alpha_rcp, ii), 0) * pow(alpha, ii);
923  }
924  float snowline = (snowline_noise - _Gimmick_DS2_11_Snowline) * _Gimmick_DS2_11_Snowline_Width;
925  float rockline_noise = 0;
926  for (uint ii = 0; ii < _Gimmick_DS2_11_Rockline_Octaves; ii++) {
927    rockline_noise += _Gimmick_DS2_Noise.SampleLevel(linear_repeat_s, final_pos.xz * _Gimmick_DS2_11_Rockline_Noise_Scale * pow(alpha_rcp, ii), 0) * pow(alpha, ii);
928  }
929  float rockline = (rockline_noise - _Gimmick_DS2_11_Rockline) * _Gimmick_DS2_11_Rockline_Width;
930
931  final_color.rgb = lerp(
932    _Gimmick_DS2_11_Grass_Color,
933    _Gimmick_DS2_11_Rock_Color,
934    saturate(final_pos_world.y - rockline));
935
936  final_color.rgb = lerp(
937    final_color.rgb,
938    _Gimmick_DS2_11_Snow_Color,
939    saturate(final_pos_world.y - snowline));
940
941  final_color *= hit;
942
943  float4 fog = 0;
944  [branch]
945  if (_Gimmick_DS2_11_Fog_Enable) {
946    fog = apply_fog(
947        length(final_pos_world.xyz - _WorldSpaceCameraPos.xyz),
948        _Gimmick_DS2_11_Fog_Density,
949        UnityObjectToWorldNormal(rd),
950        normalize(_Gimmick_DS2_11_Fog_Sun_Direction),
951        _Gimmick_DS2_11_Fog_Sun_Color,
952        _Gimmick_DS2_11_Fog_Sun_Exponent,
953        _Gimmick_DS2_11_Fog_Sun_Color_2_Enable,
954        _Gimmick_DS2_11_Fog_Sun_Color_2,
955        _Gimmick_DS2_11_Fog_Sun_Exponent_2,
956        _Gimmick_DS2_11_Fog_Color) * hit;
957  }
958
959  Gimmick_DS2_Output o;
960  o.albedo = final_color;
961  o.emission = 0;
962  o.fog = fog;
963  o.normal = normal;
964  o.metallic = 0;
965  o.roughness = 1;
966  o.worldPos = final_pos_world;
967  return o;
968}
969
970#endif  // _GIMMICK_DS2
971#endif  // __DOWNSTAIRS_02_INC