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

  • Strong Encryption for Cocoa / Cocoa Touch
    Please do not use this code! Instead, check out Jim Dovey's Common Crypto code from AQToolkit . AES is a strong encryption standard tha...
  • Texture Coordinate Arrays or Things Nobody Tells You
    For some reason, a lot of OpenGL stuff just isn't well documented. Yes, I read the Red Book chapter on texturing. It didn't have a s...
  • Dynamically adding class objects
    Okay, folks, the first draft of the book is done, and I have some breathing room until the chapters start coming back from the tech reviewer...
  • Web Services Fun
    Before we work more with libxml, let's talk about web services. Web services are a good practical application of XML and, it would seem ...
  • An OpenGL Project Template for Xcode
    I'm not a fan of the OpenGL ES Application project template that ships with the iPhone SDK. It's a pain in the ass to go in and dele...
  • Handling Double Taps
    Alright, since the NDA is down, let's celebrate by posting some information, shall we? One question that I see posted a lot by new iPhon...
  • Twittering iPhone Developer List
    Tim Haines, the developer of the excellent BurnBall game, has started a list of iPhone developers who use Twitter. The last time I checked,...
  • More on OpenGL and Normals
    In an earlier post I talked about surface normals in OpenGL. In writing the Wavefront OBJ loading class, I've learned far more about no...
  • OpenGL ES From the Ground Up, Part 1 Addendum: Alphabet Soup
    One problem with blog postings is that they are self-edited. For my postings here, I have no external technical review and no copy editing d...
  • Writing
    I'm pretty swamped with writing work at the moment. The book project is going pretty well, and I'm fortunate to have a very experien...

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