yum/3ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum/3ner
ecd59e8
master
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