Procedural Terrain

 PROCEDURAL TERRAIN

 

In this blog post I will be talking about my third homework of Ceng469 Computer Graphics II. In this homework we were asked to write a program in C++ using OpenGL graphics API that generates a random terrain on the fly using Perlin noise and geometry shaders which we can wander around like we are in a car.


BEFORE STARTING

Geometry Shaders

For those who don't know  geometry shader is an optional but very powerful shader that comes after tesselation shaders (if no tesselation shaders are used then it comes after the vertex shader) and before the rasterization stages. It is a again about processing vertex data and primitives. 


Unlike vertex shader geometry shaders have acces to all data of a primitive and the data passes by the  vertex shader. This means that instead of dealing with only one vertex at a time we can inspect the attributes of the whole triangle or line which allows us to compute triangle normals or other important data in real-time.

One other important aspect of geometry is it's output does not have match it's input at all unlike vertex shaders. This means that we can we can get points as input and generate triangles(yes plural) as output. or take triangles and create lines.

These features allows programmers to render things like a hedgehog, by taking triangles as it's skin and generating arrows on top of it's skin, or rendering a detailed tree for instance. In the modelled version of the tree we just store the branches as a mesh and send the points to that is going to have leaves on them and geometry shaders generates decides to render leaves on depending on where the point is located on that tree. For more information I think the links and books below are usefull.

Websites:


Books:


Perlin Noise

Motivation

Up to now I was rendering objects that has a single color embedded or a precomputed texture mapped to them but this is far from enough. In real life objects and entities show wear and tear they have roughness to them or some randomness in them. For instance a a cloud or a fire does not have a well defined geometry. They do have some sort of randomness to them therefore our proffessor decided that it was time for us to learn about procedural noise AKA Perlin Noise ( because it was invented by Ken Perlin)

Randomness

The idea of randomness is great we can generate a random texture and later we use it for color, bump, normal mapping or any kind of other stuff. The problem with our traditional uniform random generator functions are they generate totaly unrelated data like below 


and most of the time we do not use such textures on our objects because it wouldn't be realistic, it wouldn't be cool (computer graphics is sometimes more about generating cool images more than realistic images) and it could be even unsettling. To address this problem Ken Perlin invented an algorithm, which Perlin won an Oscar award for this algorithm. This algorithm is used in many animations, computer games and much more. Perlin's random function can generate a grayscale images like below.


 As you can see this noise has a much smoother transition compared to white noise. We can use this texture to generate the images below ( taken from https://mrl.cs.nyu.edu/~perlin/noise/ )


For these reasons I will generate a 2D grid on XZ plane then use Perlin noise to compute each vertex of this grids height, resulting in a terrain with hills and pits which we can wander around with keyboard and mouse controls.


Implementation

 Generating a Terrain on X-Z Plane

 To render the terrain, first I needed to create a grid on XZ plane, in order to achieve that I used a very simple vertex shader which does not care about it's input but considers it's gl_InstanceID while outputting vertices. If you don't know about instanced rendering it means that an instanced draw call will execute "instance times per primitive". This means that If a have terrain consisting of 1000x1000 vertices I will just send a single dummy data to GPU from my CPU and say it's going to be instanced 1000000 times and I will have 1000000 invocations of my vertex shader and each of these vertex shader will generate a different point this grid base on their gl_InstanceID and two uniform variables which determine the vertex count of the grid and the area spanned by the terrain on the XZ plane based on the following idea.



So based on some division and modulo operations we will find which point our vertex corresponds to and we will create a point at that location.

If you want to see the details of this computation you see the simple vertex shader below.

 


After generating these points in the vertex shaders as specified in my homework I took those points and generated triangles out of them based on their coordiantes in the geometry shader like illustrated below 

 
 After this point I was able to get the following output in my window

 


Adding Perlin Noise to the Terrain

Next up I am going to adjust my terrains height based on a value determined by perlin noise but I still haven't talked about how Perlin noise is generated.

In white noise we would just use a uniform random number generating function but as I mentioned before this method is not feasible in most computer graphics applications. So Ken Perlin thought instead of using random vectors we will generate a 2D grid (actually all of the things I am about to tell about perlin noise can also be used in higher dimensions) and assign not random numbers but gradient random vectors to each point in this 2D grid like in the slide below.
 

Sidenote: Perlin also suggested that we don't actually need to store these gradients vectors at all but instead we can just store 16 vectors and based on a simple hash function we can pseudorandomly choose our vectors.
 
 Then when we are sampling from noise texture we find the grid cell our point is contained in and find the distance vector from the corners of the cell to our point.
 


After finding all the distance and gradient vector we find the dot product of each distance vector with it's associated gradient vector and assign it to the corners as noise value and the interpolate results of these values according to our points place on the cell. Ken Perlin suggested that instead of using direct linear interpolation we should use a higher degree polynomial for interpolation which results in a smoother transition.
 
According to Perlin our interpolation function should be like below.
 



 After appliying this function to our distance values we finally get our linear interpolation values and apply lerp to obtained corner gradient values.

And we can finally obtain our textures like above but instead I will directly show my rendered terrain. 
 

Which I think is pretty cool. Of course in the image above I also added a simple color look up table to display heights of my terrain in a better way. But it's very simple so I will skip explaining that part.

Making Car Like Camera Movements

After rendering the terrain all I needed to do was to make a camera computation such that camera seemed like it's in a car an moving and wandering around the terrain with key-binds. In order to do that I have looked where the camera is in the current frame and by slightly incrementing it in the +x and +z directions separately I have obtained two vectors. Let's call them dx, dz. By getting the cross product of dz and dx (in this order) I have obtained the surface's normal which also should be our camera's normal and by using the cameras previous gaze direction and this up vector I have obtained the right vector of the camera and then using right vector and the up vector I have computed the new camera and I was set to go. 


Below you can see a gif of me wandering around my terrain. 




Extras

Some bugs

While implementing perlin noise at first I have made some computation errors and it created ruptures and weird looking images. They can help you understand why we don't like completely random noise  but use Perlin noise instead.
 





Perlin Bunnies

This was not part of my homework but since we have learned Perlin noise I thought it would look cool on the classical mesh model Stanford bunny and I have rendered the following bunnies just for fun.










 

Comments