how to add light

Hi,

is there a good tutorial for adding light??

there are many videos from universities / colleges out there, e.g. some of them can be found on youtube
this one is “unfortunately” in german language, but that shouldnt bother you much because you understand german :wink:
http://www.unistreams.de/vorlesung/computergrafik-ss-2014-kapitel-18-beleuchtung/

short intro:
– there is no “color” anymore, you’ll have to deal with “materials”
– to use materials, you have to provide “vertex normals” which are not necessarily equal to the geometric face (triangle) normal

what are “materials” ?


struct Material {
  vec3 DiffuseColor;
  vec3 SpecularColor;
  float Shininess;
  float Transparency;
};

the “diffuse color” is a RGB color, it is used for “ambient” and “diffuse” lighting
the “specular color” is also a RGB color (most of the times white (1, 1, 1)), it is used for “specular” lighting together with the “shininess” factor. sometimes there are additional parameters like “transparency”, which is just the (inverse) alpha value

the final color of a lit triangle is calculated like this:

vec3 Intensity = Intensity(ambient) * DiffuseColor + Intensity(diffuse) * DiffuseColor + Intensity(specular) * SpecularColor

or:

vec3 I = Ia * Kd + Id * Kd + Is * Ks

or

vec3 I = (Ia + Id) * Kd + Is * Ks

=> I is what the fragmentshader will write into the RGB framebuffer, or more likely: vec4(I, alpha) in a RGBA framebuffer (alpha is the materials transparency parameter)

ambient light:
is just a general brightness that has no source, it is everywhere to make sure that no location in your scene is completely black (dark)

diffuse light:
if a certain light ray hits the surface of your triangle, it will be reflected
depending on the angle between reflected lightray and the surface normal, the outcoming light intensity varies:

vec3 Intensity(diffuse) = Intensity(incoming_lightray) * DiffuseSurfaceColor * cosine(phi)

– where phi is the angle between reflected lightray and surface normal
the total diffuse intensity needs to be accumulated over ALL light sources in your scene

specular light:
if a certain light ray hits the surface of your triangle, it will be reflected
depending on the angle between reflected lightray and the direction in which you (scene camera) are looking (view direction), the outcoming light intensity varies:

vec3 Intensity(specular) = Intensity(incoming_lightray) * SpecularSurfaceColor * cosine(phi) ^ Shininess

– where phi is the angle between reflected lightray and the (negative) view direction
in other words: if the reflected lightray goes right into your eye (phi = 0), the intensity will be maximal
the bigger the angle, the less the intensity
the total specular intensity needs to be accumulated over ALL light sources in your scene
and because the material just “reflects” the specular lightray and doesnt absorb anything / any part of the light, is is completely white, Ks = (1, 1, 1)

then comes the next topic: light sources
there are 3 common types of sources:

directional light:
– has a fixed direction and intensity
– comes from “nowhere”
– its (incoming) intensity is constant, NOT dependent on distance to surface (because it comes from “nowhere”)

point light:
– has a position and intensity
– the intensity on the surface of the triangle is dependent on the distance
– therefore there are additional “attenuation” parameters

spot light:
– has a position and intensity
– has a method to calculate the angle which will be affected
– in the video they ues the cosine function as basis and a float variable as exponent to “deform” the cosine function

for all sources, there MUST be a positive intensity !!! (or 0)
negative intensity makes no sense, thats why you have to accumulate:

Id = max(0, Id1) + max(0, Id2) + ... + max(0, Idn) // for n light sources
Is = max(0, Is1) + max(0, Is2) + ... + max(0, Isn) // for n light sources

because materials usually dont change over time, it makes sense to put all of them into 1 big (uniform of shader storage) buffer, bind it and forget it ! :wink: but to be able to do that you have to make your material structs “in accordance with the memory layout of the uniform block”, in other words: you need to add some “padding” variables between all the parameters

like this:


struct Material
{
public:
	vec3 Kd; // diffuse color

private:
	float PADDING0;

public:
	vec3 Ks; // specular color

private:
	float PADDING1;

public:
	float Ns, d; // Shininess, Transparency
	
private:
	float PADDING2[2];

public:

	Material(const vec3& kd, const vec3& ks, float ns, float alpha = 1.0f)
		: Kd(kd), Ks(ks), Ns(ns), d(alpha)
	{}

}; 

those padding variables make it possible to put all materials in a buffer-backed uniform block with “standard memory layout”
https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout

… which requires (arrays of) structs to have its members 16byte-aligned (sizeof vec4)

This seams to be very complex, so i think i’m gonna start slowly.
I already added the normal values and pass them to my shader: https://git.rwth-aachen.de/carstenf/OpenGL/commit/948578f5066c97de9dafbecc274f91190db08836

Next steps:

  1. set only ambient light (white)
  2. add diffuse light
  3. add static spot light
  4. move the spot light
  5. change the light Color

it would be great if someone of you could guide me through those steps in my project