iPhone 3D Programming : Adding Depth and Realism - Surface Normals (part 1)

1/7/2011 9:10:09 AM
Before we can enable lighting, there’s yet another prerequisite we need to get out of the way. To perform the math for lighting, OpenGL must be provided with a surface normal at every vertex. A surface normal (often simply called a normal) is simply a vector perpendicular to the surface; it effectively defines the orientation of a small piece of the surface.

1. Feeding OpenGL with Normals

You might recall that normals are one of the predefined vertex attributes in OpenGL ES 1.1. They can be enabled like this:

// OpenGL ES 1.1
glNormalPointer(GL_FLOAT, stride, offset);

// OpenGL ES 2.0
glVertexAttribPointer(myNormalSlot, 3, GL_FLOAT, normalize, stride, offset);

I snuck in something new in the previous snippet: the GL_NORMALIZE state in ES 1.1 and the normalize argument in ES 2.0. Both are used to control whether OpenGL processes your normal vectors to make them unit length. If you already know that your normals are unit length, do not turn this feature on; it incurs a performance hit.


Don’t confuse normalize, which refers to making any vector into a unit vector, and normal vector, which refers to any vector that is perpendicular to a surface. It is not redundant to say “normalized normal.”

Even though OpenGL ES 1.1 can perform much of the lighting math on your behalf, it does not compute surface normals for you. At first this may seem rather ungracious on OpenGL’s part, but as you’ll see later, stipulating the normals yourself give you the power to render interesting effects. While the mathematical notion of a normal is well-defined, the OpenGL notion of a normal is simply another input with discretionary values, much like color and position. Mathematicians live in an ideal world of smooth surfaces, but graphics programmers live in a world of triangles. If you were to make the normals in every triangle point in the exact direction that the triangle is facing, your model would looked faceted and artificial; every triangle would have a uniform color. By supplying normals yourself, you can make your model seem smooth, faceted, or even bumpy, as we’ll see later.

2. The Math Behind Normals

We scoff at mathematicians for living in an artificially ideal world, but we can’t dismiss the math behind normals; we need it to come up with sensible values in the first place. Central to the mathematical notion of a normal is the concept of a tangent plane, depicted in Figure 1.

The diagram in Figure 1 is, in itself, perhaps the best definition of the tangent plane that I can give you without going into calculus. It’s the plane that “just touches” your surface at a given point P. Think like a mathematician: for them, a plane is minimally defined with three points. So, imagine three points at random positions on your surface, and then create a plane that contains them all. Slowly move the three points toward each other; just before the three points converge, the plane they define is the tangent plane.

The tangent plane can also be defined with tangent and binormal vectors (u and v in Figure 1), which are easiest to define within the context of a parametric surface. Each of these correspond to a dimension of the domain; we’ll make use of this when we add normals to our ParametricSurface class.

Finding two vectors in the tangent plane is usually fairly easy. For example, you can take any two sides of a triangle; the two vectors need not be at right angles to each other. Simply take their cross product and unitize the result. For parametric surfaces, the procedure can be summarized with the following pseudocode:

p = Evaluate(s, t)
u = Evaluate(s + ds, t) - p
v = Evaluate(s, t + dt) - p
N = Normalize(u × v)

Figure 1. Normal vector with tangent plane

Don’t be frightened by the cross product; I’ll give you a brief refresher. The cross product always generates a vector perpendicular to its two input vectors. You can visualize the cross product of A with B using your right hand. Point your index finger in the direction of A, and then point your middle finger toward B; your thumb now points in the direction of A×B (pronounced “A cross B,” not “A times B”). See Figure 2.

Figure 2. Righthand rule

Here’s the relevant snippet from our C++ library (see the appendix for a full listing):

template <typename T>
struct Vector3 {
// ...
Vector3 Cross(const Vector3& v) const
return Vector3(y * v.z - z * v.y,
z * v.x - x * v.z,
x * v.y - y * v.x);
// ...
T x, y, z;
  •  iPhone 3D Programming : Adding Depth and Realism - Filling the Wireframe with Triangles
  •  iPhone 3D Programming : Adding Depth and Realism - Creating and Using the Depth Buffer
  •  iPhone 3D Programming : Adding Depth and Realism - Examining the Depth Buffer
  •  iPhone 3D Programming : HelloCone with Fixed Function
  •  iPhone 3D Programming : Vector Beautification with C++
  •  jQuery 1.3 : An image carousel
  •  jQuery 1.3 : Headline rotator
  •  Silverlight : Print a Document
  •  Silverlight : Capture a Webcam
  •  Silverlight : Make Your Application Run out of the Browser
  •  Silverlight : Put Content into a 3D Perspective
  •  Silverlight : Response to Timer Events on the UI Thread
  •  Silverlight : Build a Download and Playback Progress Bar
  •  Silverlight : Play a Video
  •  C# 4.0 : Add a Static Constructor and Initialization
  •  C# 4.0 : Add a Constructor
  •  .NET Compact Framework : Font Selection
  •  .NET Compact Framework : Drawing Text
  •  Programming the Service Bus
  •  WCF Services : Generics
    Top 10
    Most Favorite Business Softwares – Feb 2013
    Maingear Nomad 15 – Don’t Judge A Gaming Laptop By Its Cover
    MSI GX60 Gaming Notebook - Great Looks And A Fast GPU
    OCUK Limited Edition P170EM - A Great Screen And Balanced Setup
    Samsung Series 5 550P5C-S03 Review - A Perfect Farewell To Windows 7
    Kobo Mini eReader Review
    Keep Kids Online Safely (Part 3)
    Keep Kids Online Safely (Part 2)
    Keep Kids Online Safely (Part 1)
    Nikon 24-85MM F3.5-4.5G ED-IF VR With Amazing Optical Performance
    Most View
    Troubleshooting Reference : Tablets & Smartphones
    iPhone 3D Programming : Adding Depth and Realism - Filling the Wireframe with Triangles
    Windows Vista : Migrating User State Data - Installing USMT, Understanding USMT Components
    Optimus 4X HD Battery Life Appears Unconvincing
    BlackBerry Java Application : Installing the Development Environment - downloading the Java Development Kit
    Introducing Windows Phone 7 Photo Features (part 2) - Using a Chooser to Open Photos & Saving Photos to the Phone
    iPhone 5 rumors fueled by new iPad released
    Freecom Silverstore 2-Drive NAS 2TB
    Communicate Between Two Machines on the Same Network (WCF)
    Group Test: Android Tablets – November 2012 (Part 2) - DisGo 9104, Samsung Galaxy Tab 27.0 Wi-Fi
    Microsoft Sues Comet For Pirating Windows
    Exposure 1010 CD Player And Integrated Amplifier
    Seagate Slim Portable – Thin Mobile Drive
    Beginer's Guide To Sports Photography (Part 2)
    Thunderbolt Storage (Part 1)
    Manage iOS with iCloud (Part 2)
    Best Of The Year 2012 (Part 1)
    ASP.NET AJAX : Partial Refreshes (part 1) - A Simple UpdatePanel Test
    Understanding and Using Windows Server 2008 R2 UNIX Integration Components (part 1)
    In Detail – Humax DTR-T1000