Lights

Lights present a problem for the rasterization-based graphics because they involve non-local, all-pairs interactions.
In this lecture, we'll talk about three strategies for dealing with lights: forward multi-pass rendering; forward light-loop rendering; and deferred rendering.

The Challenge

We tend to think of scenes like this:

one light and one sphere

But interesting scenes are more like this:

many lights and many spheres

So how do we render multiple lights that influence multiple objects?

Strategy: Forward, Multi-pass

many lights and spheres
=
ambient light
+
ambient light
+
...
+
ambient light
//Fragment Shader:
#version 330
uniform sampler2D TEX;
uniform vec3 LIGHT_LOCATION;
uniform vec3 LIGHT_DIRECTION;
uniform vec3 LIGHT_ENERGY;
uniform float LIGHT_CUTOFF;
in vec3 position;
in vec3 normal;
in vec4 color;
in vec2 texCoord;
out vec4 fragColor;
void main() {\n"
	//compute light contribution:
	vec3 n = normalize(normal);
	vec3 l = normalize(LIGHT_LOCATION - position);
	float nl = max(0.0, dot(n, l));
	float c = dot(l,-LIGHT_DIRECTION);
	nl *= smoothstep(LIGHT_CUTOFF,mix(LIGHT_CUTOFF,1.0,0.1), c);
	vec3 e = nl * LIGHT_ENERGY;

	//compute material albedo:
	vec4 albedo = texture(TEX, texCoord) * color;

	//compute final color:
	fragColor = vec4(e*albedo.rgb, albedo.a);
}

Handle lights one by one, adding up each contribution. (Uses the linearity of light!)

Complexity: for N object pixels and L lights, must perform N*L material computations, read N*(L-1) fragments, and write N*L fragments.

Strategy: Forward, Light Loop

//Fragment Shader:
#version 330
uniform sampler2D TEX;
uniform int LIGHTS;
uniform vec3 LIGHT_LOCATION[40];
uniform vec3 LIGHT_DIRECTION[40];
uniform vec3 LIGHT_ENERGY[40];
uniform float LIGHT_CUTOFF[40];
in vec3 position;
in vec3 normal;
in vec4 color;
in vec2 texCoord;
out vec4 fragColor;
void main() {\n"
	vec3 e = vec3(0.0);
	vec3 n = normalize(normal);

	//compute light contributions:
	for (int light = 0; light < LIGHTS; ++light) {
		vec3 l = normalize(LIGHT_LOCATION[light] - position);
		float nl = max(0.0, dot(n, l));
		float c = dot(l,-LIGHT_DIRECTION[light]);
		nl *= smoothstep(LIGHT_CUTOFF[light],mix(LIGHT_CUTOFF[light],1.0,0.1), c);
		e += nl * LIGHT_ENERGY[light];
	}

	//compute material albedo:
	vec4 albedo = texture(TEX, texCoord) * color;

	//compute final color:
	fragColor = vec4(e*albedo.rgb, albedo.a);
}

Improvement: Move the light loop from the CPU to the GPU.

Complexity: for N object pixels and L lights, must perform N*L material computations and write N fragments.

Strategy: Deferred

many lights and spheres
->
positions
normals + roughness
albedos
//Fragment Shader:
#version 330
uniform sampler2D POSITION_TEX;
uniform sampler2D NORMAL_TEX;
uniform sampler2D ALBEDO_TEX;
uniform vec3 LIGHT_LOCATION;
uniform vec3 LIGHT_DIRECTION;
uniform vec3 LIGHT_ENERGY;
uniform float LIGHT_CUTOFF;
in vec2 texCoord;
out vec4 fragColor;
void main() {\n"
	vec3 position = texelFetch(POSITION_TEX, ivec2(gl_FragCoord.xy), 0);
	vec3 normal = texelFetch(NORMAL_TEX, ivec2(gl_FragCoord.xy), 0);
	vec3 albedo = texelFetch(ALBEDO_TEX, ivec2(gl_FragCoord.xy), 0);

	//compute light contribution:
	vec3 n = normalize(normal);
	vec3 l = normalize(LIGHT_LOCATION - position);
	float nl = max(0.0, dot(n, l));
	float c = dot(l,-LIGHT_DIRECTION);
	nl *= smoothstep(LIGHT_CUTOFF,mix(LIGHT_CUTOFF,1.0,0.1), c);
	vec3 e = nl * LIGHT_ENERGY;

	//compute final color:
	fragColor = vec4(e*albedo.rgb, albedo.a);
}

Render geometry like objects, then render lights like objects.

Complexity: for N object pixels and P light pixels, must perform P material computations, read 3*P fragments, and write 3*N + P fragments.