People: Gavin Zimmerman
Timeline: Mar 2022 - Apr 2022
Focus: 3D Scene; Realtime
Tech: C, OpenGL, GLSL, Mv:RE(Rendering Engine)
Link: Github

Table of Contents


README / About

This was completed as part of the "Advanced Computer Graphics" course taught at University of Colorado, Boulder by Willem A. (Vlakkies) Schreüder. The final project was to create a substantial creative program with OpenGL utilizing concepts from the course including: lighting, image processing, stored textures/ noise, geometry/tesselation shaders, and procedural textures.

All of which were done from scratch; where no game engine, animation software, meshes, and painted textures were used in the making of this gravitational field. This project does utilize a custom rendering-pipeline framework developed off the OpenGL library, which has its own page here on my website.

The beauty of this project is the fully procedural creation of an asteroid field filled with unique geometry.

Asteroid Shader Pipeline

I've provided an explanation on each step in the shader pipeline for the concepts behind this project. Here I talk about the methods used, necessary optimizations, cautions to developers, and additional various notes. This commentary is on the code available for viewing here.


The idea behind creating an asteroid is to 'layer' noise on top of some existing geometry as a displacement. Essentially the noise adds some height extended along the normal of that existing geometry.

For this project, all asteroids first start off as an icosphere since vertices are equally spaced for uniform detail. The vertex shader simply passes all relevant information along in the shader pipeline.

This includes:

Tessellation Control

A bit more happens in the tesselation control shader. This is where extra detail is allocated (but not specified) on the icospheres. Both the inner and outer tessellation levels are set to the same level of detail.

That level of detail depends on how much screen space the object is taking, which works dynamically. This is accomplished by remapping the distance*scale of an object to the tesselation level, which is clamped by macros specifying the min and max tess level. A curve is also applied during the remap to better optimize the level of detail with customization on the falloff.

Additional optimizations are also performed at this time, including camera culling. If the object is not in view of the camera, then the tess level is set to 0. This is done by calculating the NDC coords of an object's center and checking if it is viewable (between -1 and 1).

Individual patches are also culled during this step. However, since the geometry will be extruded randomly and the normals will change slightly, so only patches that are extremely back-facing are culled.

Tessellation Evaluation

A lot happens during the tessellation evaluation stage.

This is where the extra detail is specified, where the position of each vertex is set and new vertex normals are identified. Obtaining the new vertex position is done by applying some noise along the normal (as a displacement).

Here I used a ridged-multifractal Musgrave texture which gives a ridged terrain look.

When I had first implemented this I had sourced a GLSL function on the internet. And it worked very well, until I upped the number of asteroids. Within the function there was a loop, and the conditional statement written at the bottom. This was so obscure that the GPU compiler could not perform its loop-unrolling optimization. So after reworking the function a bit to make this optimization happen, there was a real increase in speed (at least on my newer laptop).

For the Musgrave texture, I changed up the lacunarity parameter to be less than 1. Which is a bit odd if you think about it. Lacunarity is the scale between octaves on the texture, so when it's less than 1 the scale grows instead of shrinks. Interestingly, the range of this particular noise function is also not always bound between [0,1]. Below is the layering of multiple octaves to its full scale.







  1. 1 Octave
  2. 3 Octaves
  3. 5 Octaves
  4. 6 Octaves
  5. 7 Octaves
  6. 7.5 Octaves
A final note on this procedural texture is that it uses simplex noise under the hood. Calculating noise is often a relatively expensive process, which isn't great for rendering say 400 asteroids. A fairly cool optimization which was made, was replacing the GLSL simplex noise calculation with 3D texture sampling. 3D textures can get pretty big memory wise fast, so the pre-computed texture was only 1.278451... world space units long in each direction. The obscure unit was to prevent repetition in the sampling which had actually worked very well except at the origin. For this texture to be continuous, the OpenGL wrapping parameter is set to mirror-repeat.

For normals, there wasn't a derivative or mathematically correct way to do this. So Gouraud shading is implemented here. This means that more vertices are 'created' around the original vertex, creating polygons where the normal can be calculated with dot products. Then all those normals are averaged to produce a final one.


The geometry shader is an optional process. I've included this step here, because it was useful early in development as a way to check if tessellation was correctly implemented by providing normals for flat shading.


Finally, the fragment shader is very simple.

A lighting calculation is performed by a dot product on the light offset vector and normal. It is then multiplied by the asteroid diffuse color. The colors are then down-sampled so that there are only 4-5 shades.

Afterwards, blue is added using a fresnel effect for visual flare. Noise and a red hue are finally applied in a post-processing step.

This was to emulate cel-shading or the look of traditional animation. Traditional animation was once limited to very few colors (a couple hundred all-together), which drove this unique style.


Along the development of this project, I took a few recordings which roughly document the process. Those are included here. However, as the project got more and more developed the video naming scheme got more and more obscure.

"Reactive Tessellation"

"Mr. Meteor"

"Fluffy Aztroid"

"Aztroid Power"

"Aztroid But Fire"