yum/3ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum/3ner

yumrandom touchups idk i just got hereecd59e8

master
8.4 KiB300 linesraw
1#ifndef __RAY_MARCHING_INC
2#define __RAY_MARCHING_INC
3
4#if defined(_RAY_MARCHING)
5
6#include "math.cginc"
7#include "ray_marching_maps.hlsl"
8#include "texture_utils.cginc"
9#include "vertex.cginc"
10
11struct RayMarchResult {
12  float3 objPos;
13  float3 objNorm;
14  float3 objTan;
15};
16
17float3 getObjPos(v2f i)
18{
19#if defined(_VERTEX_DEFORMATION) && (defined(_VERTEX_DEFORMATION_FRAGMENT_NORMALS) || defined(_VERTEX_DEFORMATION_TESSELLATION))
20  return i.objPos_orig;
21#else
22  return i.objPos;
23#endif
24}
25
26void GetRoRd(v2f i, out float3 ro, out float3 rd) {
27  ro = getObjPos(i);
28  float3 rd_world = i.worldPos.xyz - _WorldSpaceCameraPos;
29  rd = normalize(mul(unity_WorldToObject, rd_world));
30#if defined(_RAY_MARCHING_SCALING)
31  float3 scale = float3(
32    _Ray_Marching_Scaling_Factor_X,
33    _Ray_Marching_Scaling_Factor_Y,
34    _Ray_Marching_Scaling_Factor_Z);
35  ro *= scale;
36#endif  // _RAY_MARCHING_SCALING
37}
38
39float map(float3 p) {
40    float d = 1e9;
41#if defined(_RAY_MARCHING_BALL)
42    {
43      float r = _Ray_Marching_Ball_Radius;
44      d = min(d, map_ball(p, _Ray_Marching_Ball_Radius));
45    }
46#endif
47#if defined(_RAY_MARCHING_HEXAGON)
48    {
49      float r = _Ray_Marching_Hexagon_Radius;
50      float h = _Ray_Marching_Hexagon_Height;
51      d = min(d, map_hexagon(p, float2(r, h)));
52    }
53#endif
54  return d;
55}
56
57#if defined(_RAY_MARCHING_HEX_GRID)
58float domain_repeat_hex_grid(inout float3 p) {
59  const float gridCount = max(_Ray_Marching_Hex_Grid_Count, 1.0f);
60  const float invGridCount = 1.0f / gridCount;
61  const float3 hex = cart_to_hex(p.xy);
62  const float3 scaledHex = hex * gridCount;
63  // Cell ID.
64  const float3 id = round_hex(scaledHex);
65  // Coordinates within the current cell.
66  const float3 local = (scaledHex - id) * invGridCount;
67  const float3 cubeId = float3(id.y, id.z, -id.y - id.z);
68  const float limit = max((gridCount - 1.0f) * 0.5f, 0.0f);
69  const float3 cubeLimit = float3(limit, limit, limit);
70
71  float3 bestPos = p;
72  float d = 1e9;
73  if (!any(abs(cubeId) > cubeLimit)) {
74    float3 pp = p;
75    pp.xy = hex_to_cart(local);
76    const float d_cur = map(pp);
77    if (d_cur < d) {
78      d = d_cur;
79      bestPos = pp;
80    }
81  }
82
83#if defined(_RAY_MARCHING_CORRECT_REPETITION)
84  const float3 cubeOffsets[6] = {
85      float3( 1.0f, -1.0f,  0.0f),
86      float3( 1.0f,  0.0f, -1.0f),
87      float3( 0.0f,  1.0f, -1.0f),
88      float3(-1.0f,  1.0f,  0.0f),
89      float3(-1.0f,  0.0f,  1.0f),
90      float3( 0.0f, -1.0f,  1.0f),
91  };
92
93  for (int idx = 0; idx < 6; ++idx) {
94    const float3 cube = cubeId + cubeOffsets[idx];
95
96    if (any(abs(cube) > cubeLimit)) {
97      continue;
98    }
99
100    const float3 rid = float3(cube.x + cube.y, cube.x, cube.y);
101    const float3 neighbourHex = (scaledHex - rid) * invGridCount;
102    float3 neighbour = p;
103    neighbour.xy = hex_to_cart(neighbourHex);
104    const float d_cur = map(neighbour);
105    if (d_cur < d) {
106      d = d_cur;
107      bestPos = neighbour;
108    }
109  }
110#endif  // _RAY_MARCHING_CORRECT_REPETITION
111
112  p = bestPos;
113  return d;
114}
115#endif  // _RAY_MARCHING_HEX_GRID
116
117float domain_repeat(inout float3 p) {
118  float d;
119#if defined(_RAY_MARCHING_CART_GRID)
120  {
121    const float3 count = float3(
122        _Ray_Marching_Cart_Grid_Count_X,
123        _Ray_Marching_Cart_Grid_Count_Y,
124        _Ray_Marching_Cart_Grid_Count_Z);
125    const float3 span = float3(
126        _Ray_Marching_Cart_Grid_Span_X,
127        _Ray_Marching_Cart_Grid_Span_Y,
128        _Ray_Marching_Cart_Grid_Span_Z) * 2.0f;
129    const float3 period = span / count;
130    const float3 half_period = period * 0.5f;
131    // count = 2
132    // which = 0 -> ok
133    // which = 1 -> ok
134    // which = -1 -> not ok
135    const float3 max_allowed = count / 2;
136    const float3 min_allowed = -count / 2 + 0.5;
137    const float3 offset = float3(
138        count.x % 2 == 0 ? half_period.x : 0,
139        count.y % 2 == 0 ? half_period.y : 0,
140        count.z % 2 == 0 ? half_period.z : 0);
141    const float3 shifted = p + offset;
142    float3 which = floor((shifted + half_period) / period);
143    if (any(which > max_allowed || which < min_allowed)) {
144      p = 1e6;
145    } else {
146      p = glsl_mod(shifted + half_period, period) - half_period;
147    }
148    d = map(p);
149  }
150#elif defined(_RAY_MARCHING_HEX_GRID)
151  d = domain_repeat_hex_grid(p);
152#else
153  d = map(p);
154#endif
155  return d;
156}
157
158bool get_one_hit(float3 ro, float3 rd, out float3 hitPos, out float hitD) {
159  const float kMinDist = _Ray_Marching_Min_Dist;
160  const float kMaxDist = _Ray_Marching_Max_Dist;
161  const uint kMaxIter = _Ray_Marching_Max_Iter;
162  float d_cur;
163  float d_acc = 0;
164  for (uint ii = 0; ii < kMaxIter; ++ii) {
165    float3 p = ro + rd * d_acc;
166
167    d_cur = domain_repeat(p);
168
169#if defined(_RAY_MARCHING_OVERSTEP)
170    d_cur *= (d_cur > 0 ? _Ray_Marching_Overstepping_Factor : 1.0f);
171#endif
172
173    d_acc += d_cur;
174    if (abs(d_cur) < kMinDist) {
175      hitPos = p;
176      hitD = d_acc;
177      return true;
178    }
179    if (d_acc > kMaxDist) {
180      break;
181    }
182  }
183
184  return false;
185}
186
187bool get_hit(float3 ro, float3 rd, out float3 hitPos, out float hitD) {
188#if defined(_RAY_MARCHING_CART_INSTANCING)
189  const float3 span = float3(
190      _Ray_Marching_Cart_Instancing_Span_X,
191      _Ray_Marching_Cart_Instancing_Span_Y,
192      _Ray_Marching_Cart_Instancing_Span_Z) * 2.0f;
193  const float3 count = float3(
194      _Ray_Marching_Cart_Instancing_Count_X,
195      _Ray_Marching_Cart_Instancing_Count_Y,
196      _Ray_Marching_Cart_Instancing_Count_Z);
197  const float3 count_minus_1 = count - 1.0f;
198  const float3 offset = span / count;
199  const float3 midpoint = offset * count_minus_1 * 0.5f;
200#if defined(_RAY_MARCHING_CART_INSTANCING_OFFSETS)
201  const float x_every_y = _Ray_Marching_Cart_Instancing_Offsets_X_Every_Y;
202  const float x_every_z = _Ray_Marching_Cart_Instancing_Offsets_X_Every_Z;
203  const float y_every_x = _Ray_Marching_Cart_Instancing_Offsets_Y_Every_X;
204  const float y_every_z = _Ray_Marching_Cart_Instancing_Offsets_Y_Every_Z;
205  const float z_every_x = _Ray_Marching_Cart_Instancing_Offsets_Z_Every_X;
206  const float z_every_y = _Ray_Marching_Cart_Instancing_Offsets_Z_Every_Y;
207  const float3 max_offset = float3(
208    count_minus_1.y * x_every_y + count_minus_1.z * x_every_z,
209    count_minus_1.x * y_every_x + count_minus_1.z * y_every_z,
210    count_minus_1.x * z_every_x + count_minus_1.y * z_every_y
211  );
212  const float3 offset_midpoint = max_offset * 0.5f;
213#endif  // _RAY_MARCHING_CART_INSTANCING_OFFSETS
214
215  hitD = 1e9;
216  for (uint xi = 0; xi < count.x; ++xi)
217  for (uint yi = 0; yi < count.y; ++yi)
218  for (uint zi = 0; zi < count.z; ++zi) {
219    float3 hitPos_tmp;
220    float hitD_tmp;
221    float3 cur_pos = ro + offset * float3(xi, yi, zi) - midpoint;
222#if defined(_RAY_MARCHING_CART_INSTANCING_OFFSETS)
223    const float3 cur_offset = float3(
224        x_every_y * yi + x_every_z * zi,
225        y_every_x * xi + y_every_z * zi,
226        z_every_x * xi + z_every_y * yi);
227    cur_pos += cur_offset - offset_midpoint;
228#endif  // _RAY_MARCHING_CART_INSTANCING_OFFSETS
229    if (!get_one_hit(cur_pos, rd, hitPos_tmp, hitD_tmp)) {
230      continue;
231    }
232    if (hitD_tmp < hitD) {
233      hitPos = hitPos_tmp;
234      hitD = hitD_tmp;
235    }
236  }
237  return hitD != 1e9;
238#else  // !_RAY_MARCHING_CART_INSTANCING
239  return get_one_hit(ro, rd, hitPos, hitD);
240#endif  // _RAY_MARCHING_CART_INSTANCING
241}
242#endif  // _RAY_MARCHING
243
244void ray_march(inout v2f i, bool is_fragment) {
245#if defined(_RAY_MARCHING)
246  float3 ro, rd;
247  GetRoRd(i, ro, rd);
248
249#if defined(_VERTEX_DEFORMATION)
250  // TODO optimize, we don't need to pass in `dummy`.
251  {
252    float3 dummy = 0;
253    float3 tmp_pos = ro;
254    undeform_normal(tmp_pos, dummy, rd);
255    rd = normalize(rd);
256  }
257#endif
258
259  float3 hit_pos;
260  float hit_d;
261  // Fragment shader can discard pixels, and here it's desired.
262  // Vertex shader very well may not have hit anything, but that doesn't mean
263  // that the fragment shader can't finish the job.
264  if (!get_hit(ro, rd, hit_pos, hit_d) && is_fragment) {
265    discard;
266  }
267
268  float3 lclPos = hit_pos;
269  float3 lclNorm = 0;
270  float3 lclTan = 0;
271  // TODO loop should say which primitive was hit, and be dispatched here.
272#if defined(_RAY_MARCHING_BALL)
273  {
274    float r = _Ray_Marching_Ball_Radius;
275    map_ball_normal(r, lclPos, lclNorm, lclTan);
276  }
277#elif defined(_RAY_MARCHING_HEXAGON)
278  {
279    float r = _Ray_Marching_Hexagon_Radius;
280    float h = _Ray_Marching_Hexagon_Height;
281    map_hexagon_normal(float2(r, h), lclPos, lclNorm, lclTan);
282  }
283#endif
284
285#if defined(_VERTEX_DEFORMATION)
286  if (is_fragment) {
287    float3 tmp_pos = ro;
288    deform_normal(tmp_pos, lclNorm, lclTan);
289  }
290#endif
291
292  i.objPos = lclPos;
293  if (is_fragment) {
294    i.normal = lclNorm;
295    i.tangent.xyz = lclTan;
296  }
297#endif  // _RAY_MARCHING
298}
299
300#endif  // __RAY_MARCHING_INC