The terrain is rendered using OpenGL Tessellation shaders. A single channel 32bit height-map texture is used by the tessellation shader to determine the newly created vert’s height position. Depending on the distance from the camera, the tessellation amount is decreased, a simple but effective implementation for LOD. All maps: diffuse, normal, and height, are created and exported using World Machine, a tool used to create terrain for various game engines including the Crytek Engine.
The Grass is rendered in patches of 64×64 units. Grass position are randomly selected within each patch and are only actually used if if it passes the probability map. The grass is drawn as GL_POINTS and is made into a triangle strip by a geometry shader. This save a lot of data as the amount of grass can easily reach into the millions.
To save on unnecessary draw calls, each grass patch is given an AABB that is used to cull patches that are not inside the camera’s frustum. Along with this culling, we need to also cull patches that are far away, as the are too small to see and can cause pixel flickering due to their small size on screen. A simple technique is to slowly drop the draw count of far away grass patches as well as drop the height of individual blades, making a nice and unnoticeable drop off of grass. This greatly increase performance, allowing patches to contain thousands of grass blades each.
The Probability map is a single channel 8bit texture map holding the probability of a piece of grass being in a certain location. This allows us to allow grass to only grow above or below a certain point and not on steep slopes made of rock.
To display the whole scene reflected in the water we need another draw pass of the world. This is simply done by flipping the world over the surface normal of the reflective surface (glScale(1.0, -1.0, 1.0) in this example) and clipping any geometry that extends pass the 3d plane created by the surface. When this is rendered to a texture, you can pass the texture to the water shader, allowing it to sample the texture using screen coordinates and modify it to simulate water.
The biggest problem with this is the extra draw call. For this project, tessellation for the terrain set to the lowest value. Surprisingly though, using clustered deferred rendering doing to draw passes of the scene with full lighting on both passes still keeps the program running at 60+ FPS.