EnvSim - An Enironment Simulator

EnvSim

An Environment Simulator

By Christopher Gilson

EnvSim is a semi photo-realistic simulation of the boundary between a land and water mass, with the ultimate goal of rendering entire island mass, that is easily populated with models. EnvSim uses a variety of techniques to achieve various effects that one might encounter in such an environment.

Controls

Movement


Mouse: Click-and-drag to rotate camera

  W
ASD    Move Forward, Backwards, Left, and Right (as though they are laid out like arrow keys)

F - Toggle Camera Constraint (Turning constraint off allows you to fly through the landmass and water)

Display Controls


M - Toggle drawing landmass
N - Toggle drawing water
T - Raise Water Level
G - Lower Water Level
B - Toggle detail texture on landmass
C - Toggle water animation
V - Toggle drawing gyroscopes

Technologies Used

Terrain Generation

Terrain is procedurally generated using the diamond-square algorithm. A quick summary of the algorithm is:

  1. Given a square piece of terrain, with values set at the four corners
  2. Set the midpoints of each side to the average of the side's corner values
  3. Set the middle point of the square to the average of each of the midpoint values
  4. Subdivide the square into 4 smaller squares, and apply the algorithm to each smaller square
  5. Stop when a specific threshold has been reached

The algorithm I used is modified with a random aspect, so that each value is within a range of the average of it's component values. This produces a more realistic terrain effect. Additionally, the four initial edge values for the terrain, as well as the original terrain's middle point were seeded with random values. The particular algorithm implemented in EnvSim requires the terrain map to be a square map of (n^2 + 1) length in order to work properly.

Terrain normals are generated procedurally, per vertex. The normal of each face in the terrain mesh is calculated, and then added to the normal vector of each component vertex. Once all face normals have been calculated, all vertex normals are normalized. This results in each vertex having a normal vector that is the average of the normal vectors of each face that it is a part of.

Shader Based Terrain Multitexturing

In order to create the effect of a smooth conversion between the landmass terrain and the seabed terrain, while only drawing one continuous triangle mesh, the terrain is Multi-textured using shaders. The vertex shader for this effect simply passed the vertex positions and texture coordinates along to the fragment shader, along with the current level of the water in the scene, and the current height of the vertex being processed

The fragment shader determined which textures to apply based on the difference between the height of the fragment being processed and the water level. Any fragments above the water level were textured as a blue sky map with an overlay of a crack texture in order to create the fragmented effect. Fragments within .1 world units of the border were textured with a mix of the land texture, the cracks texture, and the sand texture, slowly fading from the land and cracks, down to just the sand. All other fragments had the sand texture applied only. A golden material was then multiplied into the texture values in order to make the terrain green (instead of the native texture's blue) and the sand a more golden, sandy color.

Note on processor load: This texture mapping is a very intensive process, requiring a fair bit of texture memory. Because shaders are run by the GPU, this shader will run especially slowly on virtualized systems that are not connected directly to a real graphics card, because the CPU is being forced to do a massive amount of extra processing that the GPU would normally be responsible for.

Shader Based Water Rendering

Because the computation of the dynamics of an animated water surface is generally an iterative and processor intensive task, I implemented shaders to animate the water surface, as well as generate the surface normals.

The water surface's deformation is a simple sine wave. The vertex shader deforms the y coordinate


vertex.y = vertex.y + sin(time + t) * .07;
Where
t = sqrt(square(i) * square(j))

Time is an animation constant (discussed later in this page), and i and j are the x and z positions of the vertex in the terrain mesh. The multiplication by .07 is in order to scale down the sin wave to be more reasonable in size. Note that the equation for t allows for circular waves. It is possible to create straight waves with

t = (i + j) / 2

instead of the given t equation.

The surface normals are also calculated in the vertex shader. The surface normal calculation is currently not mathematically accurate and is instead a mathematical approximation in order to create shadows that look similar to what one might expect from a rippling water surface. Normal calculation is also calculated in the Vertex shader using the following parameterization:


normal.x = (j/i) * (-sin(nA));
normal.y = cos(nA);
normal.x = (i/j) * (-sin(nA));

where

nA = arctan(cos(time + t) * .07);

Time, t, i, and j are the same as above. This equation was derived using a little math and a massive amount of trial and error. I hope to implement a mathematically correct model in the future.

False Reflections

False reflections were implemented for EnvSim using the stencil buffer.

The procedure for creating false reflections using the stencil buffer for EnvSim was as follows:

  1. Render the entire scene, excluding the objects to be reflected
  2. Enable the stencil buffer, and disable OpenGL's color map (because we are drawing the stencil)
  3. Set the stencil to accept any objects that are painted
  4. First, paint the surface to be reflected over (A flat plane at the current water level was used)
  5. Set the stencil to black out any objects that are painted from the stencil
  6. Re-paint whatever geometry in the scene should NOT be reflective (in this case, a non-textured version of the land-mass was drawn.) Note that depth testing is still enabled in order to account for the fact that reflections SHOULD be drawn if the water is above the terrain.
  7. Disable any changes to the stencil.
  8. Disable any depth testing, draw the reflected images (reflections are accomplished by mirroring the CTM over the y-axis).
  9. Disable the stencil buffer.
  10. Draw the regular objects.
Note that this procedure only works well for objects that are entirely above the reflection surface. Strange artifacts are produced otherwise.

Others (Minor Examples)

Hierarchical Modeling

The Gyroscope objects are hierarchically modeled. Each ring has an additional rotation applied to it, creating a gyroscopic effect.

Real-Time animation

The animation of the gyroscopes and the water are based on an animation constant "time". At the beginnning of each display loop, "time" is updated by a call to glutGet(GLUT_ELAPSED_TIME);. This allows the animations to be advanced in real time, and process at the same speeds regardless of processor speed or refresh rate.

Camera Restriction (Collision Detection)

In camera constraint mode, the camera is locked above the terrain and the water, and within a 15x15x15 box at all times. This is accomplished simply by checking the camera's coordinates against the 15x15x15 box, and against the height of the map vertex that the camera is currently directly above (or against the water surface, whichever is closer to the camera).

Bit-mapped text

The coordinates on the map (equivalent to which vertex in the terrain that you are hovering over), as well as the absolute world coords, are displayed using bit-mapped text, displayed under an orthographic projection, using glutBitmapCharacter();.

Issues

AMD64 OpenGL Bug

There is a specific bug in OpenGL's extension capabilities when running on AMD64 processors, where a call to glGetString(GL_EXTENSIONS) causes an unexpected segfault. The report of this bug can be found here. This is relevant to any OpenGL project attempting to access and use OpenGL extensions.

GLee compatibility issues

EnvSim uses GLee as it's library to access OpenGL extensions and versions beyond 2.0. However, it sometimes runs into unexpected problems regarding the location of certain extension function pointers. The source of this bug is currently unknown to me.

References

LightHouse3D GLSL - A good introduction to shaders and how to make them
LightHouse3D Multitexturing - A simple tutorial on multitexturing in shaders
OpenGl Reflection tutoral - A verbose but complete explanation of reflecting using the stencil buffer
Swiftless Basic Reflection - A simple but effective rendring tutorial using the stencil buffer
GameDev Forum - A topic thread on how to calculate the normal to a sin wave
GameProgrammer.com tutorials - A great explanation of the diamond/square algorithm for fractal terrain generation (as well as others)
OpenGL: A primer - A fantastic book for all things openGL, used as reference for shaders