Rendering a 3D Sphere with Just Math
Rendering a 3D Sphere with Just Math
If you want to render procedural planets, you need to understand how to render spheres. But here's the thing: we're not going to use any 3D geometry. No vertex buffers, no triangles, no meshes. Just pure math in a fragment shader.
By the end of this post, you'll understand how to take a flat 2D pixel coordinate and make it look like a fully lit 3D sphere. This is the foundation for everything from ray marching to procedural planet rendering.
The Distance Function
Every pixel on your screen has a coordinate. The simplest question we can ask is: "How far is this pixel from some center point?" That distance tells us whether we're inside or outside a circle.
Try changing the radius (0.3) or the center position. The distance() function is just the Pythagorean theorem:
Right now we have a hard edge because we're using a binary comparison (<). That looks pretty ugly up close.
Smooth Edges with smoothstep
The smoothstep() function creates a smooth transition between two values. We can use it to anti-alias our circle edge:
Now we have a smooth edge. The range 0.29 to 0.31 is pretty tight (0.02 units), which makes it sharp but not pixelated. Widen that range and you get a softer falloff.
From 2D to 3D: The Key Insight
Here's where it gets interesting. A circle is just a 2D slice of a sphere. If we know the x and y coordinates of a point on a circle, we can calculate its z coordinate using the sphere equation:
Solving for z:
Since we already have the distance , this simplifies to:
Once we have all three coordinates (x, y, z), we have a point on a sphere surface. And the vector from the sphere center to that point? That's the surface normal.
Look at that! It already looks 3D. The normals point in different directions across the surface, which gives it depth. The red channel shows x-direction, green shows y, blue shows z (which is highest at the center where the sphere is closest to camera).
Adding Light
Normals are the secret to lighting. The dot product between a normal and a light direction tells us how much that surface faces the light:
- Dot product = 1: surface directly faces light (brightest)
- Dot product = 0: surface perpendicular to light
- Dot product = -1: surface faces away from light (darkest)
Now we're talking! This looks like a proper 3D sphere. Try changing:
lightDirto move the light aroundambientto control how dark the shadows aresphereColorto change the material color
Making It Interactive
Let's add smooth edges and some tweakable parameters so you can really play with it:
Play with those constants at the top. Change the sphere color, move the light source, adjust the ambient lighting. Every tweak helps you understand how these pieces fit together.
Adding Animation
Let's make the light rotate around the sphere:
The light now orbits around the sphere. Notice how the highlight moves across the surface—this is exactly how lighting works on a real 3D object.
Interactive Rotation (Drag to Rotate!)
Now for the really cool part: let's make the sphere rotatable with your mouse. We'll use the u_mouse uniform to track mouse position and rotate the sphere's normal vectors:
Try dragging your mouse across the canvas! The sphere rotates to follow your mouse. The rotation matrices (rotateY and rotateX) transform the surface normals, which changes how light reflects off the sphere at each angle.
This technique is essential for interactive 3D graphics. You're essentially doing view rotation without any 3D engine—just matrix math in a shader.
Click and Drag Rotation
The previous example follows your mouse position. But what if you want the sphere to only rotate when you're actively dragging? We can use the isDragging state to accumulate rotation:
Now click and drag to rotate! The sphere stays put when you're just hovering. The JavaScript tracks mouse deltas and accumulates rotation, so you can spin it around multiple times. Check the JavaScript tab to see how we track the isDragging state and calculate the delta between frames.
This is how most 3D viewers work—persistent rotation that you control by dragging.
Multiple Spheres
Once you understand the pattern, you can create multiple spheres. Here's a scene with three:
What Just Happened?
You learned how to:
- Use distance functions to define 2D shapes
- Calculate surface normals from the sphere equation
- Implement diffuse lighting with the dot product
- Create smooth edges with smoothstep
- Animate lighting to make it feel dynamic
- Add interactive rotation using rotation matrices and mouse input
And most importantly: you rendered a convincing 3D sphere without a single triangle. Just math.
Challenges
Ready to level up? Try these:
- Add specular highlights (Blinn-Phong reflection) - make shiny spots where light directly reflects
- Combine rotation with animation - make the sphere auto-rotate AND respond to mouse
- Add a second light source with a different color
- Create a day/night terminator like on a planet
- Add a shadow cast by one sphere onto another
- Make rotation persist on drag - accumulate rotation instead of following mouse directly
What's Next?
This sphere is completely smooth and uniform. Real planets have terrain, continents, oceans, and atmosphere. In the next post, we'll add procedural noise to the sphere surface to create terrain height. That's when this really starts to look like a planet.
The math you learned here—distance fields, normals, and lighting—is the foundation for ray marching, signed distance functions, and eventually full procedural planet rendering. You're on your way.
Enjoyed this? The next post covers noise functions and how to add terrain detail to your sphere.