yum-archive/2ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum-archive/2ner

yumbegin sketching out harnack tracer0bdd125

master
5.3 KiB168 linesraw
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