Ipphones

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Saturday, 20 December 2008

More on OpenGL and Normals

Posted on 08:37 by Unknown
In an earlier post I talked about surface normals in OpenGL. In writing the Wavefront OBJ loading class, I've learned far more about normals than I ever wanted to know. Among those things I learned is that there are two different kinds of normals - surface normals, which are what I talked about in the surface normals posting, and vertex normals.

Finding a nice, simple explanation of what vertex normals is, is not actually that easy. Which is a shame, because it's really not a difficult concept. A vertex normal is simply the average of the surface normals of all the polygons that a particular vertex is part of. Look at the following image:



That's not a cube, by the way, For simplicity, we're looking at a flat, two-dimensional mesh of six triangles. There are a total of seven vertices used to make the shape. That vertex marked A is shared by all six triangles, so the vertex normal for that vertex is the average of the surface normals for all seven triangles. Averaging is done per element, so the x values are averaged, the y values are averaged, and the z values are averaged, and the result, put into a new Vector3D is the average vector.

Let's look at how these were calculated in the Wavefront OBJ loader:

GL_FLAT and Surface Normals


If we're using GL_FLAT shading, the array of normals we need to build for the mesh pictured above will have six vectors in it, which means eighteen GLfloats. Each vector in that array needs to be the normalized surface normal for one triangle, and the normals need to be in the array in the same order that the triangles are passed into glDrawElements().

Here's how we can calculate the surface normal again (this is a slightly different algorithm than the one in my previous posting). The second function calculates the surface normal, but uses the first function.

static inline Vector3D Vector3DMakeWithStartAndEndPoints(Vertex3D start, Vertex3D end)
{
Vector3D ret;
ret.x = end.x - start.x;
ret.y = end.y - start.y;
ret.z = end.z - start.z;
vectorNormalize(&ret);
return ret;
}

static inline Vector3D Triangle3DCalculateSurfaceNormal(Triangle3D triangle)
{
Vector3D u = Vector3DMakeWithStartAndEndPoints(triangle.v2, triangle.v1);
Vector3D v = Vector3DMakeWithStartAndEndPoints(triangle.v3, triangle.v1);

Vector3D ret;
ret.x = (u.y * v.z) - (u.z * v.y);
ret.y = (u.z * v.x) - (u.x * v.z);
ret.z = (u.x * v.y) - (u.y * v.x);
return ret;
}

So, to create the array of normals for GL_FLAT, we simply need to loop through the triangles (in the same order we'll later loop through them to draw them, and call Triangle3DCalculateSurfaceNormal(), storing the results in an array of GLfloats or Vector3D (remember, one Vector3D is the same as an array of three GLfloats.

GL_SMOOTH and Vertex Normals


Now, if we're using a shading model of GL_SMOOTH, however, we need to pass in a different normal array - an array of vertex normals. And this time, instead of passing an array that contains one normal for each triangle, we need to pass one normal for each vertex, in the same order that the vertices appear in the vertex array that we pass to OpenGL using GLVertexPointer(). For speed and simplicity, I calculate both surface and vertex normals in the same method in the Wavefront OBJ loader:
- (void)calculateNormals
{
if (surfaceNormals)
free(surfaceNormals);

// Calculate surface normals and keep running sum of vertex normals
surfaceNormals = calloc(numberOfFaces, sizeof(Vector3D));
vertexNormals = calloc(numberOfVertices, sizeof(Vector3D));

NSUInteger index = 0;
NSUInteger *facesUsedIn = calloc(numberOfVertices, sizeof(NSUInteger)); // Keeps track of how many triangles any given vertex is used in
for (int i = 0; i < [groups count]; i++)
{
OpenGLWaveFrontGroup *oneGroup = [groups objectAtIndex:i];
for (int j = 0; j < oneGroup.numberOfFaces; j++)
{
Triangle3D triangle = Triangle3DMake(vertices[oneGroup.faces[j].v1], vertices[oneGroup.faces[j].v2], vertices[oneGroup.faces[j].v3]);

surfaceNormals[index] = Triangle3DCalculateSurfaceNormal(triangle);
vectorNormalize(&surfaceNormals[index]);
vertexNormals[oneGroup.faces[j].v1] = Vector3DAdd(surfaceNormals[index], vertexNormals[oneGroup.faces[j].v1]);
vertexNormals[oneGroup.faces[j].v2] = Vector3DAdd(surfaceNormals[index], vertexNormals[oneGroup.faces[j].v2]);
vertexNormals[oneGroup.faces[j].v3] = Vector3DAdd(surfaceNormals[index], vertexNormals[oneGroup.faces[j].v3]);

facesUsedIn[oneGroup.faces[j].v1]++;
facesUsedIn[oneGroup.faces[j].v2]++;
facesUsedIn[oneGroup.faces[j].v3]++;


index++;
}
}

// Loop through vertices again, dividing those that are used in multiple faces by the number of faces they are used in
for (int i = 0; i < numberOfVertices; i++)
{
if (facesUsedIn[i] > 1)
{
vertexNormals[i].x /= facesUsedIn[i];
vertexNormals[i].y /= facesUsedIn[i];
vertexNormals[i].z /= facesUsedIn[i];
}
vectorNormalize(&vertexNormals[i]);

}
free(facesUsedIn);
}
The basic idea is that as we loop through the triangles, we keep track of how many times each vertex is used, and every time we use one, we add the surface normal of the triangle to the vertex array element corresponding to the vertex being used. When we're all done, we loop through the vertices, dividing vertex array, which has the sum of all the surface normals for all the triangles in which that vertex is used, and divide it by the number of triangles it's used in.

One thing that you might have noticed is that I used calloc() rather than malloc(). These two calls do pretty much the same thing, except that calloc initializes all elements to 0, which is important when you're doing running counts and adding values rather than setting values.

Now, on to texture mapping...
Email ThisBlogThis!Share to XShare to Facebook
Posted in OpenGL ES | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • Making OpenGL ES Screenshot
    The Bit-101 Blog has an entry that shows how to take a screenshot when using OpenGL ES . I tested this in my much-delayed particle-generato...
  • Adding CLANG to Your Build Process
    Frasier Spiers has a nifty piece this morning on using Git pre-commit hooks to automatically run the CLANG Static Analyzer. I'm not a G...
  • CLANG Static Analyzer
    If you aren't using the LLVM/Clang Static Analyzer , you really should be. The Clang Project is an attempt to write a front end for the...
  • A Little Help
    I'm having a problem with OpenGL ES, and it's keeping me from finishing my particle engine post. I was hoping someone here could see...
  • WWDC Accommodations
    Staying downtown in San Francisco is very expensive in the summertime. Bu, if you're going to WWDC, you really want to stay downtown. Yo...
  • Xcode File Templates and a Mystery
    One of the things that confuses many newcomers to Xcode is how to set it up so that your company name gets automatically filled in when you ...
  • Brain Surgery?
    Craig Hockenberry has an interesting post on his blog today about the iPhone background processing issue. Craig speaks from personal experi...
  • Book's Almost Done
    I just finished Chapter 16. I'll give it another read-over in the morning then it will go off to my writing partner for his review, then...
  • iPhone Alley
    Looks like Dave and I are going to make an appearance on the iPhone Alley Podcast next week. We're recording on Sunday night, so I woul...
  • Shuffling Arrays
    Ever want to randomize an array of items? It's a task that, for some reason, I've had to do a lot in recent programs. So, I wrote a ...

Categories

  • 3D Models
  • Ad Hoc Distribution
  • ADC
  • Address Book
  • Amazon
  • Anaglyphs
  • App Store
  • Apple
  • Apple DTS
  • Apple Store
  • Application Store
  • articles
  • Award
  • Background Processing
  • Barcodes
  • Beta
  • Blog
  • Blogger
  • Blogging
  • Blogs
  • Blogspot
  • Book project
  • Bug Reporting
  • Captain Obvious
  • Categories
  • Censorship
  • CFFoundation
  • CGAffineTransform
  • Clang Static Analyzer
  • Cocoa
  • Cocoa Touch
  • Code Reuse
  • Code Signing
  • Computer
  • conferences
  • Controller Classes
  • Core Animation
  • Daring Fireball
  • Database
  • Debugging
  • Defect
  • Delegates
  • Design Awards
  • Developer Certifications
  • Discussion Forums
  • Edit Mode
  • employment opportunities
  • Encryption
  • Enterprise
  • Errata
  • free code
  • Free software
  • Full Screen
  • Game Programming
  • Gestures
  • Getting Started
  • goof
  • Google Code
  • Google Maps
  • Gotcha
  • Help
  • HIG
  • HTTP PUT
  • Idiots
  • Idle Timer
  • Images
  • Instruments
  • Interface Builder
  • iPHone
  • iPhone Applications
  • iPhone Dev Center
  • iPhone Developers
  • iPhone OS 3.0
  • iPhone SDK
  • iPhone SDK PNG
  • iPhone Simulator
  • iPhoneSDK
  • iPod
  • Job Opportunities.
  • k
  • Key Value Observing
  • Keynote
  • KVO
  • Landscape Mode
  • Learn Cocoa
  • Learn Cocoa on the Mac
  • libxml
  • Licensing
  • Mac Developers
  • Mac OS X
  • Macworld Expo
  • Microsoft
  • NDA
  • NeHe
  • New Category
  • New Release
  • NSFileHandle
  • NSMutableArray
  • NSMutableURLRequest
  • NSXML
  • Object-Oriented Design
  • Objective-C
  • Open Source
  • OpenGL ES
  • Optimizations
  • Other blogs
  • Paired Arrays
  • Parsing
  • Particle Engine
  • Party
  • PeopleSoft
  • Performance
  • Persistence
  • Pink Screen of Death
  • Piracy
  • Pixar
  • Podcasts
  • Press Release WTF
  • Press Releases WTF
  • private APIs Google
  • Project Template
  • Properties
  • Random Numbers
  • Rant
  • Rejected
  • Resources
  • Responder Chain
  • REST
  • Reverse Engineering
  • Rumors
  • Runtime
  • Sample Code
  • Screencast
  • screenshot
  • Scroll Views
  • snippet
  • Snow Leopard.
  • SOAP
  • Sockets
  • Source
  • Splash Screen
  • SQLite
  • SQLitePersistentObjects
  • Steve Jobs
  • Steve-Note
  • Strings
  • Stupidity
  • Subversion
  • Table Views
  • Taps
  • Template
  • Tip
  • Tips
  • Tririga
  • tutorials
  • Twitter
  • UIAlertView
  • UIColor
  • UIImage
  • UIPickerView
  • UIScrollView
  • UITextField
  • UIView
  • UIWebView
  • Update
  • Utilities
  • UUID
  • Vacation
  • Version Control
  • Web Services
  • Writing
  • WTF
  • WWDC
  • Xcode
  • XML

Blog Archive

  • ►  2009 (141)
    • ►  May (14)
    • ►  April (30)
    • ►  March (48)
    • ►  February (26)
    • ►  January (23)
  • ▼  2008 (163)
    • ▼  December (46)
      • Coming Soon - My OpenGL Particle Engine
      • Updated OpenGL ES Xcode Project Template
      • Another Wavefront OBJ loader
      • PVRTC Textures & OpenGL resources from Apple
      • A Reusable Date Drill-Down Controller
      • Learn Objective-C on the Mac is Shipping
      • We broke Amazon's Top 100 Books
      • Reusable Controllers
      • Wavefront OBJ Loader - Another Update
      • Almost There...
      • Texture Coordinate Arrays or Things Nobody Tells You
      • Happy Holidays
      • OpenGL ES Extensions on the iPhone
      • Texture Mapping is Coming
      • And we have a Winner
      • More on UIAlertView and Landscape Problem
      • UIAlertView & Landscape Mode
      • More on OpenGL and Normals
      • Wavefront OBJ Loader - Normals Done...
      • Progress on the Wavefront OBJ Loader
      • Andy Ihnatko on Apple & the Macworld Expo
      • gluLookAt()
      • MacWorld 2009
      • 3D Models as C Header Files
      • OpenGL Postings
      • The Start of a WaveFront OBJ File Loader Class
      • More on Outlets - Instance Variables vs. Properties
      • Just for Fun: OpenGL Icosahedron
      • Surface Normals in OpenGL
      • NeHe Tutorials, Lesson 6 Ported to iPhone (sort of)
      • Lesson 5 Code Fixed
      • OpenGL Warning!
      • Preparations for Porting NeHe Lesson 06
      • NeHe Tutorials, Lesson 5 Ported to iPhone
      • Captain Obvious Discusses OpenGL Function Document...
      • NeHe Tutorials, Lesson 4 Ported to iPhone
      • NeHe Tutorials, Lesson 3 Ported to iPhone
      • OpenGL Project Template Redux
      • NeHe Tutorials, Lesson 2 Ported to iPhone
      • gluPerspective
      • An OpenGL Project Template for Xcode
      • Back in town
      • Next Few Days
      • Outlets - Property vs. Instance Variable
      • Speaking of Errata...
      • Happy Belated Thanksgiving
    • ►  November (25)
    • ►  October (44)
    • ►  September (2)
    • ►  August (5)
    • ►  July (2)
    • ►  June (9)
    • ►  May (2)
    • ►  April (11)
    • ►  March (17)
Powered by Blogger.

About Me

Unknown
View my complete profile