City Rendering, Lighting

Calculating normals + lighting

Lighting Basics

The next step I took to improve the rendering was to add in lighting. Until now I had just gave everything a normal of “up”. This meant that every surface of the building had the same brightness as the normal represents the direction the surface is facing. Basic is lighting is done by taking the angle between the light direction and the surface normal – the smaller the angle the brighter the surface must be as the surface is directly facing the light source. This is illustrated in the image below:


Diffuse lighting

This is documented on many other websites for example here.

By adding the lighting the 3d look of the structures is instantly more effective as shown in the images below.


Calculating Normals

It is relatively easy to calculate a surface normal for a triangle as shown in the code below. The basic concept is to use the vertices to create 2 vectors and then use the cross product to find the perpendicular vector, which will be the surface normal.

GLKVector3 CalculateNormal (GLKVector3 v1,GLKVector3 v2,GLKVector3 v3)


GLKVector3 a, b;

a = GLKVector3Subtract(v1,v2);

b = GLKVector3Subtract(v1,v3);

return GLKVector3Normalize(GLKVector3CrossProduct(a, b));


Caveats and fixes

The code above relies on the winding of the triangles being consistent or some of the normals will point towards the centre of the model instead of outside. This manifests itself in random black triangles or surfaces that are pointing in the same direction not having the same shading. In our case, calculating the normals for the buildings I came up with the same issue even though the windings of my triangles should have been consistent. It turns out this was in-fact due to the polygons from open street map not having a consistent winding. Some of the polygons were clockwise, while others were anticlockwise. Luckily this is a simple problem to rectify. Using the standard formulae for calculating the area of a polygon, we can reverse the coordinate array for polygons that all have an area <0. This results in all polygons having a consistent winding and thus the normals were then calculated consistently. This also will have fixed the issue which was causing me to have to disable back face culling (see here).


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s