Procedural Terrain rendering
and Water simulation
Language: c++, Middleware: OpenGL
Overview of the project
Motivation
- How to triangulate terrain optimally and simulate realistic water.
Problem which I had
- Terrain contains huge data, so it need to be optimized. Using the highest level of detail wastes resources of the system.
- Wave function is crucial to animate and render realistic water surface.
Which features are included in this project
- Dynamic LOD(Level of Detail): Based on camera position, multiple LODs are combined to optimize the terrain.
- Wave simulation: The combination of wave function can simulate realistic water.
- Water rendering: The method to render reflections on the water surface.
- Multipass rendering: Post-processing of rendering. It allows rendered scenes to have various effects such as HDR or bloom.
How to solve the problem
- Used Quadtree to store all terrain LOD and implemented dynamic LOD to triangulate the terrain.
- Combined multiple wave function to simulate realistic waves and used environment mapping, normal mapping and multipass rendering to render realistic reflections on the water.
Limitation
- Wave functions, which are used in this project, can simulate gentle waves like that in the lake. In order to animate ocean wave, more complex wave function has to be used.
Detail of the project
1. LOD (Level of Detail) of Terrain
LOD is the most common way to improve the performance of system in 3D. Human is less likely to notice the detail of object which are far away. When it comes to the terrain, near terrain will be trianglated in pixel level, which vertices of triangles are each pixel in heightmap, and the size of triangles in far terrain will be much larger than near one.
Figure 1. The process of Quadtree generation
In order to store the terrain data into Quadtree, the terrain will be divided into 4 parts and those are stored into each child node. This process will be continued until the size of divided area is equivalent to pixel size or can be stopped when it reaches proper area size. In other words, the root of Quadtree stores the lowest level of detail and leaf node contains the highest terrain LOD (see Figure 2).
Each node of tree contains following data (see Figure 3).
(1) 4 pointers to child node
(2) 1 centre point of current node (sub area)
(3) 4 vertices (corners) of current node (sub area)
(4) 4 midpoints between vertices
(1) 4 pointers to child node
(2) 1 centre point of current node (sub area)
(3) 4 vertices (corners) of current node (sub area)
(4) 4 midpoints between vertices
There are two way to decide LOD which are adaptive LOD and dynamic LOD based on camera position. First, adaptive LOD is to decide level of detail based on terrain data. For example, if the terrain is planar, the area will be triangulated using large triangles. In case of rugged mountains, smaller triangles will be used. Second, dynamic LOD based on camera position. LOD will be determined by the distance from the camera to the terrain (see Figure 5).
Figure 5. Dynamic
LOD based on camera position
In
order to enhance system performance, the system only render the terrain which
is inside view frustum of camera.
- Result
2. Water rendering
Sea level is where y position of terrain is zero. In order to generate realistic wave rendering, I applied wave simulation and wave is rendered using displacement shader. Displacement shader is vertex shader which changes geometry of the object.
Basis of water is a 2D plane and its geometry will be deformed by the shader.
Following equation is the function of wave.
In
order to generate realistice wave, I used the concept of Fourier transform.
Every function can be described by the cmobination of sine and cosine function.
So I combined various wave functions having different directions, amplitudes
frequencies.
In order to render terrain reflection on the water, I used stencil test.
Stencil buffer is normally used along with depth buffer. Depth buffer only stores only the closest depth whereas Stencil buffer is used to per-pixel masking so that we can remove certain objects in the scene using it.
(1) Set Stencil buffer as 1 where water surface exists
(2) Flip terrain using scale(1, -1, 1) – It will be fake reflection of the terrain
(3) Render flipped terrain and conduct stencil test.
Then, only flipped terrain through water will be renderred like window.
Stencil buffer is normally used along with depth buffer. Depth buffer only stores only the closest depth whereas Stencil buffer is used to per-pixel masking so that we can remove certain objects in the scene using it.
(1) Set Stencil buffer as 1 where water surface exists
(2) Flip terrain using scale(1, -1, 1) – It will be fake reflection of the terrain
(3) Render flipped terrain and conduct stencil test.
Then, only flipped terrain through water will be renderred like window.
3. Multipass Rendering
When we play modern games, they use many dynamic lighting effects. One of the most common effect is HDR (High Dynamic Range) effect.
In order to apply this effect, we used multipass rendering which is post processing of rendering process. First, we need store rendering result into the texture. In order to achieve this, we applied frame buffer in OpenGL. Using frame buffer, we render the scene into a texutre rather than the screen. Following this, we can apply various filter onto it such as blur or sharpen filters. Finally, we render this texture onto the plane which fills the screen. So what we see is projected 3D image onto 2D plane.
In this project, we applies 3 passes to get HDR effect. These multipass is written in the fragment shader. Following explains each pass.
(1) 1st pass: Capture the area having high luminance
In this project, we applies 3 passes to get HDR effect. These multipass is written in the fragment shader. Following explains each pass.
(1) 1st pass: Capture the area having high luminance
(2) 2nd pass: Blur the
area to get glow effect
(3) 3rd pass: Add blurred result onto original texture