diff --git a/app-proto/full-interface/src/inversive.frag b/app-proto/full-interface/src/inversive.frag index b7fb100..339be00 100644 --- a/app-proto/full-interface/src/inversive.frag +++ b/app-proto/full-interface/src/inversive.frag @@ -63,15 +63,13 @@ vec3 sRGB(vec3 color) { // --- shading --- -struct taggedFrag { - int id; - vec4 color; - float highlight; +struct Fragment { vec3 pt; vec3 normal; + vec4 color; }; -taggedFrag sphere_shading(vecInv v, vec3 pt, vec3 base_color, float highlight, int id) { +Fragment sphere_shading(vecInv v, vec3 pt, vec3 base_color) { // the expression for normal needs to be checked. it's supposed to give the // negative gradient of the lorentz product between the impact point vector // and the sphere vector with respect to the coordinates of the impact @@ -81,11 +79,26 @@ taggedFrag sphere_shading(vecInv v, vec3 pt, vec3 base_color, float highlight, i float incidence = dot(normal, light_dir); float illum = mix(0.4, 1.0, max(incidence, 0.0)); - return taggedFrag(id, vec4(illum * base_color, opacity), highlight, pt, normal); + return Fragment(pt, normal, vec4(illum * base_color, opacity)); +} + +float intersection_dist(Fragment a, Fragment b) { + float intersection_sin = length(cross(a.normal, b.normal)); + vec3 disp = a.pt - b.pt; + return max( + abs(dot(a.normal, disp)), + abs(dot(b.normal, disp)) + ) / intersection_sin; } // --- ray-casting --- +struct TaggedDepth { + float depth; + float dimming; + int id; +}; + // if `a/b` is less than this threshold, we approximate `a*u^2 + b*u + c` by // the linear function `b*u + c` const float DEG_THRESHOLD = 1e-9; @@ -127,36 +140,29 @@ void main() { // cast rays through the spheres const int LAYER_MAX = 12; - taggedFrag frags [LAYER_MAX]; + TaggedDepth top_hits [LAYER_MAX]; int layer_cnt = 0; for (int id = 0; id < sphere_cnt; ++id) { // find out where the ray hits the sphere vec2 hit_depths = sphere_cast(sphere_list[id], dir); - // insertion-sort the fragments we hit into the fragment list + // insertion-sort the points we hit into the hit list float dimming = 1.; for (int side = 0; side < 2; ++side) { - float hit_z = -hit_depths[side]; - if (0. > hit_z) { + float depth = hit_depths[side]; + if (depth > 0.) { for (int layer = layer_cnt; layer >= 0; --layer) { - if (layer < 1 || frags[layer-1].pt.z >= hit_z) { - // we're not as close to the screen as the fragment - // before the empty slot, so insert here + if (layer < 1 || top_hits[layer-1].depth <= depth) { + // we're not as close to the screen as the hit before + // the empty slot, so insert here if (layer < LAYER_MAX) { - frags[layer] = sphere_shading( - sphere_list[id], - hit_depths[side] * dir, - dimming * color_list[id], - highlight_list[id], - id - ); + top_hits[layer] = TaggedDepth(depth, dimming, id); } break; } else { - // we're closer to the screen than the fragment before - // the empty slot, so move that fragment into the empty - // slot - frags[layer] = frags[layer-1]; + // we're closer to the screen than the hit before the + // empty slot, so move that hit into the empty slot + top_hits[layer] = top_hits[layer-1]; } } layer_cnt = min(layer_cnt + 1, LAYER_MAX); @@ -182,35 +188,22 @@ void main() { return; } - // highlight intersections and cusps - for (int i = layer_cnt-1; i >= 1; --i) { - // intersections - taggedFrag frag0 = frags[i]; - taggedFrag frag1 = frags[i-1]; - float ixn_sin = length(cross(frag0.normal, frag1.normal)); - vec3 disp = frag0.pt - frag1.pt; - float ixn_dist = max( - abs(dot(frag1.normal, disp)), - abs(dot(frag0.normal, disp)) - ) / ixn_sin; - float max_highlight = max(frags[i].highlight, frags[i-1].highlight); - float ixn_highlight = 0.5 * max_highlight * (1. - smoothstep(2./3.*ixn_threshold, 1.5*ixn_threshold, ixn_dist)); - frags[i].color = mix(frags[i].color, vec4(1.), ixn_highlight); - frags[i-1].color = mix(frags[i-1].color, vec4(1.), ixn_highlight); - - // cusps - float cusp_cos = abs(dot(dir, frag0.normal)); - float cusp_threshold = 2.*sqrt(ixn_threshold * sphere_list[frag0.id].lt.s); - float highlight = frags[i].highlight; - float cusp_highlight = highlight * (1. - smoothstep(2./3.*cusp_threshold, 1.5*cusp_threshold, cusp_cos)); - frags[i].color = mix(frags[i].color, vec4(1.), cusp_highlight); - } - // composite the sphere fragments vec3 color = vec3(0.); - for (int i = layer_cnt-1; i >= layer_threshold; --i) { - vec4 frag_color = frags[i].color; - color = mix(color, frag_color.rgb, frag_color.a); + int layer = layer_cnt - 1; + TaggedDepth hit; + for (; layer >= layer_threshold; --layer) { + // shade the current fragment + hit = top_hits[layer]; + Fragment frag = sphere_shading( + sphere_list[hit.id], + hit.depth * dir, + hit.dimming * color_list[hit.id] + ); + float highlight = highlight_list[hit.id]; + + // composite the current fragment + color = mix(color, frag.color.rgb, frag.color.a); } outColor = vec4(sRGB(color), 1.); } \ No newline at end of file