Environment Mapping
Reflections, refractions, and skyboxes
A chrome sphere reflects the world around it. A glass ball bends light passing through. These effects require knowing what the environment looks like from all directions. Environment mapping captures this surrounding light in a texture, then samples it based on ray directions.
Combined with the raymarching techniques from earlier chapters, environment mapping brings raymarched scenes to life with reflections, refractions, and realistic materials.
The Environment Map
An environment map stores the color of light coming from every direction. The simplest form is an equirectangular map: a single 2D texture where horizontal position encodes longitude and vertical position encodes latitude, like unfolding a globe.
To sample an environment map, we convert a 3D direction vector to 2D coordinates:
vec2 directionToUV(vec3 dir) {
float phi = atan(dir.z, dir.x);
float theta = asin(dir.y);
return vec2(phi / (2.0 * 3.14159) + 0.5, theta / 3.14159 + 0.5);
}The inverse trigonometry extracts angles from the direction, which we then normalize to the 0-1 UV range.
Environment Map Sampling
Drag to look around. The environment is sampled by ray direction.
As you rotate the view direction, notice how the environment map coordinates change. The entire surrounding world is encoded in this single image.
Reflections
A reflection occurs when light bounces off a surface. The reflected direction depends on the incident ray and the surface normal:
Where is the incoming direction, is the surface normal, and is the reflected direction. GLSL provides this as the built-in reflect function.
vec3 reflectDir = reflect(rayDir, normal);
vec3 reflectionColor = sampleEnvironment(reflectDir);Reflective Sphere
The sphere reflects the environment based on surface normal
The sphere acts as a mirror, showing the environment distorted by its curved surface. Adjust the reflectivity to blend between the base material color and pure reflection.
Refractions
When light passes from one medium to another (like air to glass), it bends according to Snell's law:
Where values are the refractive indices of each medium. Air is approximately 1.0, water is 1.33, glass is around 1.5.
GLSL provides the refract function:
float ior = 1.5; // Index of refraction
vec3 refractDir = refract(rayDir, normal, 1.0 / ior);
vec3 refractionColor = sampleEnvironment(refractDir);The ratio 1.0 / ior assumes we are going from air into the material. When exiting the material, we would use ior / 1.0 and flip the normal.
Refraction Through Glass
Light bends as it enters and exits the sphere
Notice how the environment appears warped and shifted through the refractive material. Higher IOR values bend light more dramatically.
The Fresnel Effect
Real materials are not purely reflective or purely transmissive. The balance between reflection and refraction depends on the viewing angle. At glancing angles, surfaces become more reflective; at perpendicular angles, they are more transmissive.
This is the Fresnel effect, and it is why a lake looks like a mirror when viewed at a low angle but transparent when looking straight down.
The Schlick approximation provides a computationally cheap estimate:
float fresnel(vec3 viewDir, vec3 normal, float ior) {
float r0 = pow((1.0 - ior) / (1.0 + ior), 2.0);
float cosTheta = max(dot(-viewDir, normal), 0.0);
return r0 + (1.0 - r0) * pow(1.0 - cosTheta, 5.0);
}The result ranges from 0 (full refraction) to 1 (full reflection) based on the angle.
Fresnel Effect
At glancing angles (edge), more reflection. At steep angles (center), more refraction.
Watch how the balance shifts as you change the view angle. The edge of the sphere becomes more reflective while the center remains more transmissive.
Combining Effects
Realistic glass and water combine all these elements:
- Calculate the reflection direction and sample the environment
- Calculate the refraction direction and sample the environment
- Use Fresnel to blend between them
- Add a tint for colored glass
- Apply absorption for thick glass (light attenuates as it travels through)
vec3 reflectDir = reflect(rayDir, normal);
vec3 refractDir = refract(rayDir, normal, 1.0 / ior);
vec3 reflectColor = sampleEnvironment(reflectDir);
vec3 refractColor = sampleEnvironment(refractDir) * tint;
float f = fresnel(-rayDir, normal, ior);
vec3 finalColor = mix(refractColor, reflectColor, f);Combined Material
Fresnel blends reflection and tinted refraction for realistic glass
Adjust the parameters to create different materials: pure chrome (100% reflective), clear glass (high IOR, low absorption), colored glass (tinted refraction), or water (IOR 1.33, subtle tint).
Skyboxes
For scenes without a raymarched environment, we can procedurally generate a sky. A simple gradient from horizon to zenith:
vec3 getSkyColor(vec3 dir) {
float t = 0.5 * (dir.y + 1.0);
vec3 horizon = vec3(0.8, 0.9, 1.0);
vec3 zenith = vec3(0.3, 0.5, 0.9);
return mix(horizon, zenith, t);
}Add a sun for more interest:
vec3 sunDir = normalize(vec3(1.0, 0.5, 0.5));
float sunDot = max(dot(dir, sunDir), 0.0);
vec3 sunColor = vec3(1.0, 0.9, 0.7) * pow(sunDot, 128.0);
return skyColor + sunColor;Procedural skies integrate naturally with raymarched scenes because both are computed from ray directions.
Integration with Raymarching
To add environment reflections to a raymarched scene:
- March the primary ray until it hits a surface
- Calculate the surface normal
- Compute the reflected ray direction
- Sample the environment map (or procedural sky) with that direction
- Blend based on material properties and Fresnel
For refractive objects, the implementation becomes more complex. You may need to:
- March through the interior of the object
- Exit through the back surface
- Handle total internal reflection when the angle is too steep
- Account for multiple bounces in thick glass
The principles remain the same, but the bookkeeping grows.
Key Takeaways
- Environment maps encode surrounding light in a 2D texture sampled by direction
- Equirectangular maps use spherical coordinates mapped to UV space
- Reflection uses
reflect(rayDir, normal)to bounce rays off surfaces - Refraction uses
refract(rayDir, normal, iorRatio)to bend rays through materials - Fresnel effect: surfaces are more reflective at glancing angles
- Schlick approximation provides cheap, accurate Fresnel calculations
- Combine reflection, refraction, and Fresnel for realistic glass and water
- Procedural skyboxes generate environment colors from ray directions
- Environment mapping completes raymarching with realistic material interactions