Programming

Hypnotic Figure Eight Klein Bottle Emblem

Small Figure 8 Animation

Introduction

I made this cool little emblem in C++/GLSL[1]. It is shaded in realtime to look smooth and smarmy at arbitrary zoom. You can find the math at MathWorld's Klein Bottle [entry] (http://mathworld.wolfram.com/KleinBottle.html) (scroll down a little).

Challenges

Normals in closed form

I had to punch the parametric equations into Mathematica to get a closed-form solution for the normals (a huge pile of sine and cosine terms [2]), so that it maintains its infinitely awesome smoothness when Blinn-Phong shaded on a per pixel basis in hardware.

Normal mapping a non-orientable surface

The main challenge was figuring out how to normal map and cull a non-orientable surface! Of course the trick was to do it piecewise, as we do in any good-old-nasty-sticky-applied-math-type situation.

Code

The code is available for playing with, learning from, and improving (hence GPLv2).


  1. Here is a zip of the Xcode project. It compiles on my MacBook Pro (Intel) under Leopard. ↩︎

  2. As here:

    p(u,v) = (x(u,v), y(u,v), z(u,v))
    x(u,v) = cos(u) [ a + cos(u/2) sin(v) - sin(u/2) sin(2 v) ]
    y(u,v) = sin(u) [ a + cos(u/2) sin(v) - sin(u/2) sin(2 v) ]
    z(u,v) = sin(u/2) sin(v) + cos(u/2) sin(2 v)
    dp/du =
    dx/du =   cos(u) (-1/2 sin(u/2) sin(v) - 1/2 cos(u/2) sin(2 v))
    - sin(u) (a + cos(u/2) sin (v) - sin(u/2) sin(2 v))
    dy/du =   sin(u) (-1/2 sin(u/2) sin(v) - 1/2 cos(u/2) sin(2 v))
    + cos(u) (a + cos(u/2) sin(v) - sin(u/2) sin(2 v))
    dz/du = 1/2 cos(u/2) sin(v) - 1/2 sin (u/2) sin(2 v)
    dp/dv =
    dx/dv = cos(u) (cos(u/2) cos(v) - 2 cos(2 v) sin(u/2))
    dy/dv = sin(u) (cos(u/2) cos(v) - 2 cos(2 v) sin(u/2))
    dz/dv = 2 cos(u/2) cos(2 v) + cos(v) sin(u/2)
    normal = cross(normalize(dp/du), normalize(dp/dv)) ↩︎