
iSurfer Part 3: Inside the OpenGL ES 2.0 Fragment Shader Ray Tracer — Root Isolation, Safeguarded Newton-Bisection & Two-Sided Shading
Table of Contents (7 sections)▸
In Part 1 of this series, we examined why polygon meshes (Marching Cubes) break down at the conical singularities and self-intersections of algebraic surfaces . In Part 2, we built the recursive-descent parser and AST-to-GLSL compiler that JIT-compiles user equations into native GPU functions float f(vec3 p) and vec3 grad_f(vec3 p) on the fly.
In this final installment (Part 3), we step inside the OpenGL ES 2.0 fragment shader itself to trace a single pixel's ray from the camera lens through a bounding sphere, isolate the closest root of , refine it to sub-pixel precision without diverging at singular points, and shade the resulting two-sided mathematical manifold.
1. Ray Generation & Bounding-Sphere Clipping ()#
Algebraic surfaces like a hyperboloid () or Whitney's Umbrella () extend to infinity in . To inspect them as tangible 3D sculptures—just like the physical plaster models of 19th-century German mathematics collections and Oberwolfach's SURFER—we intersect the infinite surface with a clipping sphere of radius :
For each screen pixel with normalized device coordinates , the vertex and fragment stages transform the camera eye position and pixel plane into object-space ray origin and normalized ray direction ().
Before evaluating the user's polynomial a single time, the fragment shader solves the quadratic ray-sphere intersection :
glslbool intersectBoundingSphere(vec3 ro, vec3 rd, float radius, out float tEnter, out float tExit) { float b = dot(ro, rd); float c = dot(ro, ro) - radius * radius; float disc = b * b - c; if (disc < 0.0) return false; // Ray misses the bounding sphere completely! float sqrtDisc = sqrt(disc); tEnter = max(0.0, -b - sqrtDisc); tExit = -b + sqrtDisc; return tExit > tEnter; }
This simple test provides two massive wins on a mobile tile-based GPU:
- Early Background Culling: Every pixel outside the silhouette of the bounding sphere immediately exits (
discardor writes background color) in 6 ALU instructions without running the raymarcher. - Tightly Bounded Search Interval: The search for roots of is strictly confined to the finite chord of length at most .
2. Root Isolation Along the Ray: Finding the First Sign Change#
Along the bounded ray segment , a degree- algebraic surface can intersect the viewing ray up to times. For occlusion to be physically correct, we must find the smallest root (the surface sheet closest to the camera).
For low-degree surfaces (), closed-form algebraic formulas exist (the quadratic formula for , Cardano's formula for cubics , and Ferrari's resolvent cubic for quartics —all of which we later implemented as specialized fast-paths in SurfZar).
For general degree- equations () and arbitrary user expressions, the shader partitions into marching steps of size and scans forward from to :
glslfloat tPrev = tEnter; float fPrev = f(ro + rd * tPrev); float rayHitSign = sign(fPrev); // Tracks whether ray approaches from f > 0 or f < 0 bool foundBracket = false; float tA = tEnter, tB = tEnter; for (int i = 1; i <= MAX_STEPS; i++) { float tCurr = tEnter + float(i) * dt; float fCurr = f(ro + rd * tCurr); // Intermediate Value Theorem: opposite signs guarantee a root in [tPrev, tCurr] if (fPrev * fCurr <= 0.0) { tA = tPrev; tB = tCurr; foundBracket = true; break; // Stop at the FIRST (closest to camera) intersection! } tPrev = tCurr; fPrev = fCurr; }
By breaking out of the loop at the first sign change , front-to-back hidden-surface occlusion happens naturally.
3. Why Pure Newton-Raphson Explodes at Singularities—and How Safeguarded Newton-Bisection Solves It#
Once the marching loop isolates a bracket where and have opposite signs, the naive textbook approach is to run Newton-Raphson iterations:
On an algebraic surface, unconstrained Newton-Raphson produces severe visual artifacts along two critical regions:
- Silhouette Grazing Edges (): Along the silhouette rim of any curved surface, the viewing ray is perpendicular to the surface normal , so the directional derivative approaches zero. Dividing by launches tens of units outside the bracket , causing the pixel to either miss the surface (leaving a sparkly black halo around silhouettes) or jump onto a completely different sheet behind the surface!
- Singular Points (): At a conical double point or self-intersection, all three partial derivatives vanish simultaneously, making .
The Safeguarded Hybrid Bisection + Newton Solver#
To combine the unconditional stability of Bisection with the quadratic convergence speed of Newton-Raphson, we implemented a Safeguarded Hybrid Root Refiner:
glslfloat refineRootHybrid(vec3 ro, vec3 rd, float a, float b, float fa, float fb) { float t = 0.5 * (a + b); for (int iter = 0; iter < 10; iter++) { vec3 p = ro + rd * t; float ft = f(p); // Maintain the tight sign-change bracket [a, b] if (fa * ft <= 0.0) { b = t; fb = ft; } else { a = t; fa = ft; } // Evaluate directional derivative g'(t) = dot(grad_f(p), rd) float dfdt = dot(grad_f(p), rd); float newtonCandidate = t - ft / dfdt; // Safeguard: only accept the Newton step if derivative is non-degenerate // AND the candidate stays strictly inside theshrinking bracket (a, b)! if (abs(dfdt) > 1e-4 && newtonCandidate > a && newtonCandidate < b) { t = newtonCandidate; } else { // Fall back to guaranteed Bisection midpoint t = 0.5 * (a + b); } } return t; }
Look at why this 25-line function is rock-solid on the GPU:
- Unconditional Loop Unrolling: Because the loop bound (
10iterations) is a compile-time constant with no dynamicbreak, the OpenGL ES 2.0 shader compiler unrolls it cleanly without warp divergence. - Guaranteed Bracket Contraction: Even right on top of one of the 65 conical singularities of the Barth Sextic where
abs(dfdt) <= 1e-4, the solver smoothly falls back tot = 0.5 * (a + b), shrinking the bracket by beyond the initial step size ! - Quadratic Polish on Smooth Sheets: Everywhere else on the surface, the Newton step lands strictly inside
(a, b)and converges to full 23-bit IEEEhighp floatmantissa precision in just 3–4 iterations.
4. Two-Sided Manifold Orientation (rayHitSign) & Shading#
An implicit surface divides into two distinct half-spaces:
- The Positive Side (): Where the gradient vector points outward.
- The Negative Side (): Where points inward.
When viewing a clipped algebraic surface (or slicing through an internal chamber), the camera sees both the exterior () and interior () faces of the polynomial sheet. Following the visual tradition of Oberwolfach's SURFER, coloring the two sides with contrasting materials immediately reveals the global topology and orientation of the manifold:
glslvec3 hitPos = ro + rd * tStar; vec3 rawGrad = grad_f(hitPos); vec3 N = normalize(rawGrad); // Orient normal toward the approaching camera ray bool isFrontFace = (rayHitSign > 0.0); if (dot(N, rd) > 0.0) { N = -N; } // Select Side A (f > 0) vs Side B (f < 0) base albedo vec3 baseColor = isFrontFace ? uColorFront : uColorBack;
Notice a subtle detail here: why do we determine isFrontFace from rayHitSign (the sign of at the start of the marching interval before hitting the surface) rather than from dot(rawGrad, rd)?
Because on thin sheets or near singular creases, small floating-point inaccuracies in hitPos can cause rawGrad to jitter near zero, producing "zebra stripe" color flickering between Front and Back colors! Using the approaching sign rayHitSign guarantees that the positive and negative sides of the surface never flicker.
5. From 2013 iOS Thesis (iSurfer) to 2026 WebGL2 Studio (SurfZar)#
Completing the iSurfer thesis at ITBA—pairing our dynamic AST-to-GLSL compiler with multi-touch arcball rotation, pinch-zoom, and a curated gallery of classical algebraic surfaces on iOS—proved that mobile GPUs could render advanced mathematical geometry in real time without a single triangle mesh.
Click to zoomSurfZar 3D Studio — The modern WebGL2 evolution of iSurfer
More than a decade later, in 2026, I brought the entire architecture of iSurfer to the modern web with SurfZar — Real-Time 3D Algebraic Surface Visualizer & Hybrid GPU Solver, adding:
- Closed-form Cardano (degree 3) and Ferrari (degree 4) analytical root solvers alongside Interval Arithmetic (
Interval f_interval(...)) raymarching. - Dual-sided Physically Based Rendering (PBR) with procedural normal maps, metallic/roughness materials, and HDRI environment reflections.
- A multi-track Keyframe Animation Timeline for morphing parameters and exporting WebM video turntables.
- Marching Cubes STL / OBJ Export so any algebraic surface sculpted in the GPU raymarcher can be 3D-printed as a physical mathematical sculpture.
Links & Resources#
- Original 2013 iSurfer iPhone Demo Video: Watch on YouTube
- Try SurfZar Live in Your Browser: https://surfzar.vercel.app/
- Read Part 1: Why Implicit Algebraic Surfaces Demand Per-Pixel GPU Ray Tracing Over Polygon Meshes
- Read Part 2: From Touch Input to GPU Silicon — The Equation Parser & On-the-Fly GLSL Shader Compiler