yum-archive/Tooner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/Tooner
358c53e
master
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