I cracked normal maps

Project Page Next project post Post History

Delving further into the render engine, I decided to try my hand at adding more detail to rendered models by including normal texture mapping into Hyperion. I'm familiar with normals and what they do for 3D in principle. I just didn't know how to deal with them in code.

My engine did have normals to begin with, but they were only simple and enough to begin rendering with. You can see an example of them in the render below. This render is a special one that renders the normals of the scene. What you're seeing in this render, is a Phong shaded sphere object. The Phong shading smooths the surface of the object. Just to clarify though, you're not seeing shaded surfaces - i.e., this isn't a render that's lit with light. In fact, there's no lighting in this at all.

A render of the scene normals and a Phong shaded sphere

Normals can be used for a few things. Mostly though, for a rendering engine, they tell you in which direction something points. Another place they can be useful is in compositing. Take a magnifying glass in 3D for example, render this out and composite it over your image by using a rendered normals image (like the one above) to displace the image underneath the glass area.

By telling which direction something is, we can tell how the surface might be affected by lighting. But what normals really do for most rendering, is they allow you to manipulate the polygon surface. Instead of averaging across a polygon to make a flat surface curved or smoothed over (more on this below), we can add more detail to our model's surface without having to make more geometry. This means we can render more detailed looking surfaces without more geometry, and faster.

Here's a render of our sphere again, this time instead of making the polygon surface look smooth, it now looks more detailed:

Our low polygon sphere, with more detailed surfacing

In the example render above, I'm using a texture map to adjust the surface normals. We can make these texture maps as detailed as we like, though in this instance it's only a small texture, 512x512 pixels, which I pulled from a free texture place for testing. This is what the texture map looks like as an image:

A normals texture map, in tangent space. The purple/blue colour is deliberate.

So, we kind of wrap the texture map around the sphere, and we read from the texture map how we should be adjusting the normals on the polygon surface. It's a little more than that, but it's that kind of thing. The colours of purples and blues are deliberate too, because we use the red, green and blue channels in images to store the direction vector. I won't go into the tech side of it, suffice to say the colours do mean something. If you'd like to read up more on this, here's a place to start.

For reference and proof of concept, here's a render of the geometry of our sphere, showing it's low polygon geometry:

The next image is a side by side comparison of three spheres, all with the same geometry. Just to explain; the sphere on the left has no Phong shaded normals, the sphere in the middle has Phong shaded normals, and the sphere on the right has Phong shaded and texture mapped normals.

Hyperion rendering different normals across the same geometric surfaces

You can really see how normal maps can add detail to the surface of a low polygon model.

That said, normals aren't a complete solution. The effect of normal maps can break at certain angles and distances from the camera. But if you know their limitations, and you know how and where to use them, they can be really effective for surface detailing.

I'm relieved to have cracked this. Most people writing render engines or game engines probably find this mundane, and would do it without thinking. But for someone with no formal programming skills, I spent awhile trying to figure this out and how it was suppose to work in code. One of my biggest issues is that I've got no one else to talk to about these things. What I mean by this is; I don't have a friend or work colleague I can show these things to and problem solve with. So it can take me some time to figure things out. I understand the principles of 3D very well, have been in that for a few decades now, just don't know how to deal with them in code like a seasoned programmer might.

That said, I got some hints off the Cinema4D plugin forum. Not the answers I was after, but enough to put me in the right direction. It's worked a treat.