Rendering solid walls

Progressing Forwards

At the end of the previous post I had processed the Openstreet map data – created polygon data for each building and then drawn these buildings using the GL_LINE primitives to give us a wireframe outline for the building as shown below:

Simulator Screen Shot 8 Nov 2015 19.20.35

To take this to the next stage – the next task was to draw solid walls. We already have the corners of each of the walls so we just have to convert this to a list of triangles and submit this list to OpenGL. The naive way to draw all of the buildings would be to create the triangles per building and then submit these as a single draw call. However, this approach would be very inefficient due to the overhead associated with making so many draw calls.

Primitive types

The better way to submit the vertices for drawing would be to batch the vertices into a single buffer as this allows you to then draw multiple objects(buildings) in a single draw call. I chose to draw the walls using GL_TRIANGLE_STRIP. One has the advantage over GL_TRIANGLE is that you can save a lot of memory as you only have to store one vertex per triangle, instead of having to use three. This is also the recommended primitive type to use on iOS platforms so it seemed like a good place to start. However, using GL_TRIANGLE still probably would have been OK as long as we batched the vertex data to limit draw calls.

The importance of batching

As for how to batch calls when each vertex you add to your vertex array creates another triangle – the solution is to insert “degenerate triangles”. Degenerate triangles are triangles with an area of 0. In our case we should insert a degenerate triangle between each of our separate buildings to connect them together. You can imagine between the last vertex between building n and the first vertex of building n+1 there is an invisible triangle connecting them. The graphics hardware is clever enough to recognise the degenerate triangle so rendering these does not lead to a loss in performance. The simplest way to insert a degenerate triangle is to simply duplicate the first and last vertex of every object you need to connect.

Results and Issues

The images below shows the final result of drawing the building walls. The first image shows the scene with culling disabled. The second image shows the scene with backface culling enabled. backface culling is useful as when the roof’s are implemented only the walls closest to the camera will be visible – the back faces/walls will be hidden by the opaque roof’s. By not drawing these hidden faces we prevent the hardware from having to process these pixels thus saving filtrate. However as the screen shot shows – when the backface culling is enabled some of the front faces disappear. The hardware determines which face is the “back”(or front) face by the winding of the triangles – this will require some further investigation to solve. The issue is likely that the OSM buildings are not all defined in the same order (clockwise/anticlockwise) or my code that generates the triangle strips code simply has some bugs!

Simulator Screen Shot 8 Nov 2015 19.44.42

Building walls drawn with face “culling” disabled

A point worth noting: OpenGLES 3.0 allows you to use the “primitive restart” functionality to batch draw calls – however I have not yet had a change to experiment with OpengGLES 3.0 API so I have no idea how this compares to using degenerate triangles. It would save a bit of memory as you would no longer have to submit duplicate vertices (or as many duplicate indices).

Advertisements

One comment

  1. […] 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). […]

    Like

Leave a Reply

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

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: