yum-archive/2ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/2ner
0bdd125
master
1#ifndef __HARNACK_TRACING_INC 2#define __HARNACK_TRACING_INC 3 4#include "cnlohr.cginc" 5#include "globals.cginc" 6#include "interpolators.cginc" 7 8#if defined(_HARNACK_TRACING) 9 10#define MAX_ITERATIONS 100 11#define UNIT_SHIFT 5.0 12#define WALL_THICKNESS 0.1 13 14#define SPHERE_RADIUS 0.49 15#define OUTER_RADIUS (SPHERE_RADIUS + 0.5) 16 17float gyroid(float3 pos); 18float3 gyroid_gradient(float3 pos); 19bool harnackTrace(float3 ro, float3 rd, out float t, out float3 pos, out float3 normal, float tMax); 20bool intersectSphere(float3 ro, float3 rd, float3 center, float radius, out float t0, out float t1); 21float getRadius(float3 p); 22float getMaxStep4D(float fx, float R, float levelset, float shift); 23bool closeToLevelset(float f, float levelset, float tol, float gradNorm); 24bool betweenLevelsets(float f, float loBound, float hiBound, float tol, float gradNorm); 25 26struct HarnackTracingOutput { 27 float4 color; 28 float3 worldPos; // intersection point in world space 29 float3 normal; // normal in world space 30}; 31 32// Gyroid function implementation 33float gyroid(float3 pos) { 34 float timeOffset = 2.0 * PI * _Harnack_Tracing_Gyroid_Speed * _Time.y; 35 float3 p = _Harnack_Tracing_Gyroid_Scale * pos + float3(0.0, timeOffset, 0.0); 36 return sin(p.x) * cos(p.y) + sin(p.y) * cos(p.z) + sin(p.z) * cos(p.x); 37} 38 39// Gradient of gyroid function 40float3 gyroid_gradient(float3 pos) { 41 float timeOffset = 2.0 * PI * _Harnack_Tracing_Gyroid_Speed * _Time.y; 42 float3 p = _Harnack_Tracing_Gyroid_Scale * pos + float3(0.0, timeOffset, 0.0); 43 return float3( 44 cos(p.x) * cos(p.y) - sin(p.z) * sin(p.x), 45 cos(p.y) * cos(p.z) - sin(p.x) * sin(p.y), 46 cos(p.z) * cos(p.x) - sin(p.y) * sin(p.z) 47 ) * _Harnack_Tracing_Gyroid_Scale; 48} 49 50// Get radius from point to outer boundary 51float getRadius(float3 p) { 52 return OUTER_RADIUS - length(p); 53} 54 55// Calculate if point is close to levelset 56bool closeToLevelset(float f, float levelset, float tol, float gradNorm) { 57 return abs(f - levelset) < tol; 58} 59 60// Calculate if point is between levelsets (for wall thickness) 61bool betweenLevelsets(float f, float loBound, float hiBound, float tol, float gradNorm) { 62 return max(loBound - f, f - hiBound) < tol; 63} 64 65// Sphere intersection test 66bool intersectSphere(float3 ro, float3 rd, float3 center, float radius, out float t0, out float t1) { 67 float3 oc = ro - center; 68 float b = dot(oc, rd); 69 float c = dot(oc, oc) - radius * radius; 70 float h = b * b - c; 71 72 if (h < 0.0) return false; 73 74 h = sqrt(h); 75 t0 = -b - h; 76 t1 = -b + h; 77 78 return true; 79} 80 81// Calculate maximum step size for Harnack tracing 82float getMaxStep4D(float fx, float R, float levelset, float shift) { 83 float a = (fx + shift) / (levelset + shift); 84 float u = pow(3.0 * sqrt(3.0 * pow(a, 3.0) + 81.0 * pow(a, 2.0)) + 27.0 * a, 1.0 / 3.0); 85 return R * abs(u / 3.0 - a / u - 1.0); 86} 87 88// Main Harnack tracing function 89bool harnackTrace(float3 ro, float3 rd, out float t, out float3 pos, out float3 normal, float tMax) { 90 t = 0.0; 91 float levelset = sin(_Harnack_Tracing_Gyroid_Speed * _Time.y); 92 93 // Early sphere intersection test 94 float t0, t1; 95 if (!intersectSphere(ro, rd, float3(0,0,0), SPHERE_RADIUS, t0, t1) || tMax < 0.0) 96 return false; 97 98 // Optimize bounds 99 t = max(t0, 0.0); 100 tMax = min(t1, tMax); 101 102 // Check immediate intersection 103 pos = ro + t0 * rd; 104 float val = gyroid(pos); 105 float3 gradF = gyroid_gradient(pos); 106 if (betweenLevelsets(val, levelset - WALL_THICKNESS, levelset + WALL_THICKNESS, 0.025, length(gradF))) { 107 normal = normalize(gradF); 108 return true; 109 } 110 111 float t_overstep = 0.0; 112 113 [loop] 114 for (int iters = 0; iters < MAX_ITERATIONS && t < tMax; iters++) { 115 pos = ro + t * rd + t_overstep * rd; 116 117 val = gyroid(pos); 118 gradF = gyroid_gradient(pos); 119 120 float offset_levelset = clamp(val, levelset - WALL_THICKNESS, levelset + WALL_THICKNESS); 121 122 float R = getRadius(pos); 123 float shift = exp(sqrt(2.0) * R) * UNIT_SHIFT; 124 float r = getMaxStep4D(val, R, offset_levelset, shift); 125 126 if (r >= t_overstep && closeToLevelset(val, offset_levelset, 0.025, length(gradF))) { 127 normal = normalize(gradF); 128 return true; 129 } 130 131 float stepSize = (r >= t_overstep) ? t_overstep + r : 0.0; 132 t_overstep = (r >= t_overstep) ? r * 0.75 : 0.0; 133 t += stepSize; 134 } 135 136 normal = float3(0,0,0); 137 return false; 138} 139 140HarnackTracingOutput HarnackTracing(v2f i) { 141 HarnackTracingOutput o = (HarnackTracingOutput)0; 142 143#if defined(_HARNACK_TRACING_GYROID) 144 float3 worldRo = _WorldSpaceCameraPos; 145 float3 ro = mul(unity_WorldToObject, float4(worldRo, 1.0)).xyz; 146 147 float3 worldRd = normalize(i.worldPos - worldRo); 148 float3 rd = normalize(mul((float3x3)unity_WorldToObject, worldRd)); 149 150 float t; 151 float3 pos; 152 float3 normal; 153 154 bool hit = harnackTrace(ro, rd, t, pos, normal, 100.0); 155 156 if (hit) { 157 o.color = 1; 158 o.worldPos = mul(unity_ObjectToWorld, float4(pos, 1.0)).xyz; 159 o.normal = normalize(mul(transpose((float3x3)unity_WorldToObject), normal)); 160 } 161#endif 162 163 return o; 164} 165 166#endif // _HARNACK_TRACING 167 168#endif // __HARNACK_TRACING_INC