Cubemaps

You may need to read the articles about lighting and textures before this one.


A cubemap is a type of texture that consists of six faces, each referenced by their direction from the center of a cube. Cubemaps are sampled using normals representing the direction from the origin to the fragment instead of texture coordinates. A cubemap can be sampled using a samplerCube variable. For example:

#version 300 es

precision highp float;

in vec3 v_normal;

uniform samplerCube u_texture;

out vec4 outColor;

void main() {
	vec3 normal = normalize(v_normal);
	outColor = texture(u_texture, normal);
}

Environment Maps

The most common thing that cubemaps are used for are environment maps, which represent the environment of the scene. Environment maps are a way to approximate reflections. If a fragment shader knows the position and normal of its fragment relative to the origin, it can use the reflect function to calculate a reflection. For example:

#version 300 es

precision highp float;

in vec3 v_surfacePosition;
in vec3 v_normal;

uniform samplerCube u_texture;
uniform vec3 u_cameraPosition;

out vec4 outColor;

void main() {
	vec3 normal = normalize(v_normal);
	vec3 cameraToSurfaceDirection =
		normalize(v_surfacePosition - u_cameraPosition);
	vec3 reflectionDirection =
		reflect(cameraToSurfaceDirection, normal);
	outColor = texture(u_texture, reflectionDirection);
}

Check out the source of the example texture here.

Skyboxes

A skybox is a cubemap that represents the background of a scene (often a sky). Skyboxes can be implemented by using the inverse of the view projection matrix to rasterize the skybox to a plane that covers the entire rendering context.

The vertex shader should ensure that the skybox is always at the back of the depth buffer. For example:

#version 300 es

in vec4 a_position;
out vec4 v_position;

void main() {
	gl_Position = a_position;
	gl_Position.z = 1.0;

	v_position = a_position;
}

The skybox is treated as being infinitely far away from the camera, so calculations involving it should use a view direction matrix, which is a view matrix with the translation component removed.

A fragment shader can multiply a fragment's position by the inverse of a view direction projection matrix (a product of a projection matrix a view direction matrix) to undo it. Then, it needs to manually divide the xyz vector by w to apply perspective. The result can be sampled from the environment map to make a skybox. For example:

#version 300 es

precision highp float;

in vec4 v_position;

uniform samplerCube u_texture;
uniform mat4 u_inverseViewDirectionProjection;

out vec4 outColor;

void main() {
	vec4 t = u_inverseViewDirectionProjection * v_position;
	vec3 skyboxNormal = normalize(t.xyz / t.w);
	outColor = texture(u_texture, skyboxNormal);
}

The next article is about shadows.