To light or not to light? That is the question.

Different types of light

Today I’m gonna talk a bit about different tyes of lights that you can use in 3D graphics & OpenGL and how they all work. Although this might differ from normal OpenGL posts I found this quite interesting too. Enjoy reading!

Point Light

A point light is a type of light that illuminates the scene in every direction from the source and like real life, the intensity or strength of the light gradually decreases as the object is farther away from the point light. The loss of intensity due to distance can be programmed into your OpenGL project (assuming you already have a 3D light object in your project). Unlike real life, we are able to control how the light drops in intensity using a different mathematical equation. I’m not gonna go super in depth to the maths but I’ll try my best to explain the source code.

vec4 pointLight(){

    vec3 lightVec = lightPos - crntPos;
    float dist = length(lightVec);
    float a = 3.0;
    float b = 0.7;
    float intensity = 1.0f / (a * dist * dist + b * dist + 1.0f);


    //''''ambient'''' lighting
    float ambient = 0.20f;


    vec3 normal = normalize(Normal);
    vec3 lightDirection = normalize(lightVec);
    float diffuse = max(dot(normal, lightDirection), 0.0f);

    float specularLight = 0.50f;
    vec3 viewDirection = normalize(camPos - crntPos);
    vec3 reflectionDirection = reflect(-lightDirection, normal);

    float specAmount = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 16); //(last one controls how defined the specular light is)
    float specular = specAmount * specularLight;

    return (texture(tex0, texCoord) * (diffuse + ambient * intensity) + texture(tex1, texCoord).r * specular * intensity) * lightColor;

}

The a float controls how far the light will diffuse and drop, the b float controls how steep it drops down from with more of a value being a steep droop while a lesser value will delay the droop or reduce how steep the drop is. The intensity variable is filled with the mathematical calculation I was talking about, all you have to do to implement it is just to include it in to the final return value.

Directional Light

Usually used to illuminate a large scene, similar to how the sun illuminates earth. The source is usually quite far away from the object so it can cover a large space. Even though I’m not gonna use this in my project i’ll cover on how to implement it.

vec4 directLight(){
//''''ambient'''' lighting
float ambient = 0.20f;

    vec3 normal = normalize(Normal);
    vec3 lightDirection = normalize(vec3(1.0f, 1.0f, 0.0f));
    float diffuse = max(dot(normal, lightDirection), 0.0f);

    float specularLight = 0.50f;
    vec3 viewDirection = normalize(camPos - crntPos);
    vec3 reflectionDirection = reflect(-lightDirection, normal);

    float specAmount = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 16); //(last one controls how defined the specular light is)
    float specular = specAmount * specularLight;

    return (texture(tex0, texCoord) * (diffuse + ambient ) + texture(tex1, texCoord).r * specular) * lightColor;

}

This is quite simple, simply replace any variable you had that was specifying your light direction with a vec3 sppecifying where the light is going to point at. Also a note, it matters how you return the values, currently I have to write the opposite direction my light is going to go because of how I returned the values.

Spotlight

Last but not least is the spotlight, generally used to light an area with a conical projection, just like a flashlight and like a normal real life spotlight. Let me show you how you’d implement this too.

vec4 spotlight(){

    float outerCone = 0.90f;
    float innerCone = 0.95f;
    //''''ambient'''' lighting
    float ambient = 0.20f;


    vec3 normal = normalize(Normal);
    vec3 lightDirection = normalize(lightPos - crntPos);
    float diffuse = max(dot(normal, lightDirection), 0.0f);

    float specularLight = 0.50f;
    vec3 viewDirection = normalize(camPos - crntPos);
    vec3 reflectionDirection = reflect(-lightDirection, normal);

    float specAmount = pow(max(dot(viewDirection, reflectionDirection), 0.0f), 16); //(last one controls how defined the specular light is)
    float specular = specAmount * specularLight;

    float angle = dot(vec3(0.0f, -1.0f, 0.0f), -lightDirection);
    float intensity = clamp((angle - outerCone) / (innerCone - outerCone), 0.0f, 1.0f);

    return (texture(tex0, texCoord) * (diffuse * intensity + ambient ) + texture(tex1, texCoord).r * specular * intensity) * lightColor;

}

First off we need to assign some values to our innerCone and outerCone so it has a smooth diffuse gradient when we use the spotlight instead of one strong switch of colours. The outerCone value shows the angle between light projection and the outer part of the cone and vice versa. We then calculate the cosine values of both variables since this is more efficient than calculating the angles directly. We then get the intensity of light for the spot we are going to illuminate and make sure to clamp it so it doesnt go above 1.

Extras

Since I wrote this long blog post I might as well add something extra. To really make the texture more realistic use a specular map to really show the depth between the light and dark parts of your texture. Make sure you use the right color channel when implementing it!