Environment Mapping

Overview

Environment mapping is conceptually the process of constructing an artificial environment around an object in order to provide, for example, effects such as reflective surfaces or refractive objects. In the r2 package, the artificial environment is represented by cube maps, and the only provided effect is a simulation of reflection. Effects such as refraction are instead provided via generic refraction, which doesn't use environment mapping.

Cube Maps

A cube map is a texture with six faces. When used for environment mapping, each face represents a 90° image of the environment visible in the direction (in world space) of that face. Cube maps are normally constructed by placing an observer in a scene and then orienting the observer in the direction of each cube face in turn and rendering an image. As an example:

Given the above scene, with the observer placed exactly in the center of the indicated magenta circle and assuming a 90° field of view, the six images visible from that location corresponding to the -x, +x, -y, -z, -z, +z cube faces are:

While sampling from ordinary two-dimensional textures involves looking up texels by their two-dimensional coordinates, sampling from cube maps requires three-dimensional coordinates. The three-dimensional coordinates are interpreted as a direction vector or ray emanating from the center of the cube, and the point of intersection between the ray and the corresponding cube face is used to select a texel from that face. Note that in OpenGL there are issues with coordinate system handedness that the r2 package corrects.

Reflections

So-called environment-mapped reflections are trivially provided by cube maps. For a given surface with a normal vector n, and given the view direction v (from the observer to the surface), a reflection vector is given by r = Reflection.reflection v n:

module Reflection where import qualified Vector3f as V3 reflection :: V3.T -> V3.T -> V3.T reflection v0 v1 = V3.sub3 v0 (V3.scale v1 (2.0 * (V3.dot3 v1 v0)))

The reflection vector r is then used to look up a texel in the current cube map directly. This gives a convincing illusion of reflection that will change as the observer moves relative to the surface. Combining normal mapping and environment mapped reflections gives a striking effect:

Note that in the actual r2 implementation, the vectors n and v will be in eye-space and therefore so will r. The vector r is transformed back to world space by the inverse of the current view matrix for use with the cube map.

Handedness

For reasons lost to time, cube maps in OpenGL use a left-handed coordinate system in contrast to the usual right-handed coordinate system. Because of this, calculated reflection vectors actually have to be inverted to prevent sampling from the wrong cube face. The r2 package enforces a consistent right-handed coordinate system everywhere. The direction of each cube face corresponds to the same direction in world space, without exception.