I implemented a shader builder class to create all the shaders of the engine.

Lighting implementation is one of the most important things in game engine development, and I decided to start with implementing them. There are two types of lighting movability: static and dynamic. Static lights are lights whose intensity, position, direction etc cannot be changed, whereas dynamic lights can be updated during game time.

ShaderBuilder needs to handle both types of lights in order to obtain more optimal software. Since dynamic lights require uniform variables, and static lights do not builder class goes by this case. ShaderBuilder class, lighting renders and corresponding shaders are shown here:

Shader Builder

ShaderBuilder class is builder of all shaders of the scene. It consists of three main parts: uniforms, outside main, and inside main scripts. When a game is loaded, firstly it builds scene’s fragment shader. Then if any object has a special shader class, it can be used to create them as well. By doing this, I aimed a shader management system away from complication.

/*
        ShaderBuilder.h
*/

#ifndef __SHADERBUILDER_H__
#define __SHADERBUILDER_H__

#include "Goknar/Core.h"

class DirectionalLight;
class PointLight;
class SpotLight;

inline namespace SHADER_VARIABLE_NAMES
{
	inline namespace MATERIAL
	{
		extern const char* AMBIENT;
		extern const char* DIFFUSE;
		extern const char* SPECULAR;
		extern const char* PHONG_EXPONENT;
	}
	
	inline namespace LIGHT
	{
		extern const char* DIRECTIONAL_LIGHT;
		extern const char* POINT_LIGHT;
		extern const char* SPOT_LIGHT;
	}

	inline namespace LIGHT_KEYWORDS
	{
		extern const char* POSITION;
		extern const char* INTENSITY;
		extern const char* DIRECTION;
		extern const char* COVERAGE_ANGLE;
		extern const char* FALLOFF_ANGLE;
	}
}

extern const std::string DEFAULT_SHADER_VERSION;

class GOKNAR_API ShaderBuilder
{
public:
	ShaderBuilder();
	~ShaderBuilder();

	void Init();

	const std::string& GetSceneVertexShader() const
	{
		return sceneVertexShader_;
	}

	const std::string& GetSceneFragmentShader() const
	{
		return sceneFragmentShader_;
	}

	std::string GetShaderVersion() const
	{
		return shaderVersion_;
	}

	void SetShaderVersion(const std::string& shaderVersion)
	{
		shaderVersion_ = shaderVersion;
	}

protected:

private:
	void BuildSceneVertexShader();
	void BuildSceneFragmentShader();

	void CombineShader();

	std::string shaderVersion_;

	// Vertex Shader
	std::string sceneVertexShader_;

	// Fragment Shader

	// Fragment shader builder getter functions
	std::string GetShaderVersionText();
	std::string GetMaterialVariables();

	// Point Light
	std::string GetPointLightUniformTexts(const std::string& lightVariableName);
	std::string GetPointLightColorFunctionText();
	std::string GetStaticPointLightText(const PointLight* pointLight, const std::string& lightVariableName) const;
	std::string GetPointLightColorSummationText(const std::string& lightVariableName);

	// Directional Light
	std::string GetDirectionalLightUniformTexts(const std::string& lightVariableName);
	std::string GetDirectionalLightColorFunctionText();
	std::string GetStaticDirectionalLightText(const DirectionalLight* directionalLight, const std::string& lightVariableName) const;
	std::string GetDirectionalLightColorSummationText(const std::string& lightVariableName);

	// Spot Light
	std::string GetSpotLightUniformTexts(const std::string& lightVariableName);
	std::string GetSpotLightColorFunctionText();
	std::string GetStaticSpotLightText(const SpotLight* directionalLight, const std::string& lightVariableName) const;
	std::string GetSpotLightColorSummationText(const std::string& lightVariableName);

	// Fragment shader variables
	std::string uniforms_;
	std::string sceneFragmentShaderOutsideMain_;
	std::string sceneFragmentShaderInsideMain_;
	std::string sceneFragmentShader_;
};

#endif
/*
    ShaderBuilder.cpp
*/

#include "pch.h"
#include "ShaderBuilder.h"

#include "Goknar/Engine.h"
#include "Goknar/Scene.h"

#include "Goknar/Lights/DirectionalLight.h"
#include "Goknar/Lights/PointLight.h"
#include "Goknar/Lights/SpotLight.h"

inline namespace SHADER_VARIABLE_NAMES
{
	inline namespace MATERIAL
	{
		const char* AMBIENT = "ambientReflectance";
		const char* DIFFUSE = "diffuseReflectance";
		const char* SPECULAR = "specularReflectance";
		const char* PHONG_EXPONENT = "phongExponent";
	}

	inline namespace LIGHT
	{
		const char* DIRECTIONAL_LIGHT = "DirectionalLight";
		const char* POINT_LIGHT = "PointLight";
		const char* SPOT_LIGHT = "SpotLight";
	}

	inline namespace LIGHT_KEYWORDS
	{
		const char* POSITION = "Position";
		const char* INTENSITY = "Intensity";
		const char* DIRECTION = "Direction";
		const char* COVERAGE_ANGLE = "CoverageAngle";
		const char* FALLOFF_ANGLE = "FalloffAngle";
	}
}

const std::string DEFAULT_SHADER_VERSION = "330 core";

ShaderBuilder::ShaderBuilder()
{
	shaderVersion_ = DEFAULT_SHADER_VERSION;
}

ShaderBuilder::~ShaderBuilder()
{
}

void ShaderBuilder::Init()
{
	BuildSceneVertexShader();
	BuildSceneFragmentShader();
}

void ShaderBuilder::BuildSceneFragmentShader()
{
	const Scene* scene = engine->GetApplication()->GetMainScene();

	uniforms_ += R"(
out vec3 color;
in vec3 fragmentPosition;
in vec3 vertexNormal;
uniform vec3 viewPosition;

)";

	sceneFragmentShaderOutsideMain_ += GetMaterialVariables();

	const Vector3& sceneAmbientLight = scene->GetAmbientLight();
	sceneFragmentShaderOutsideMain_ += "vec3 sceneAmbient = vec3(" + std::to_string(sceneAmbientLight.x / 255.f) + ", " + std::to_string(sceneAmbientLight.y / 255.f) + ", " + std::to_string(sceneAmbientLight.z / 255.f) + ");\n";

	sceneFragmentShaderInsideMain_ += "\tvec3 lightColor = sceneAmbient * ambientReflectance;\n";

	const std::vector<const PointLight*>& staticPointLights = scene->GetStaticPointLights();
	const std::vector<const PointLight*>& dynamicPointLights = scene->GetDynamicPointLights();
	if (staticPointLights.size() > 0 || dynamicPointLights.size() > 0)
	{
		sceneFragmentShaderOutsideMain_ += GetPointLightColorFunctionText() + "\n";

		for (const PointLight* staticPointLight : staticPointLights)
		{
			std::string lightVariableName = staticPointLight->GetName();
			sceneFragmentShaderOutsideMain_ += GetStaticPointLightText(staticPointLight, lightVariableName);
			sceneFragmentShaderInsideMain_ += GetPointLightColorSummationText(lightVariableName);
		}

		for (const PointLight* dynamicPointLight : dynamicPointLights)
		{
			std::string lightVariableName = dynamicPointLight->GetName();
			uniforms_ += GetPointLightUniformTexts(lightVariableName);
			sceneFragmentShaderInsideMain_ += GetPointLightColorSummationText(lightVariableName);
		}
	}

	const std::vector<const DirectionalLight*>& staticDirectionalLights = scene->GetStaticDirectionalLights();
	const std::vector<const DirectionalLight*>& dynamicDirectionalLights = scene->GetDynamicDirectionalLights();
	if (staticDirectionalLights.size() > 0 || dynamicDirectionalLights.size() > 0)
	{
		sceneFragmentShaderOutsideMain_ += GetDirectionalLightColorFunctionText() + "\n";
		
		for (const DirectionalLight* staticDirectionalLight : staticDirectionalLights)
		{
			std::string lightVariableName = staticDirectionalLight->GetName();
			sceneFragmentShaderOutsideMain_ += GetStaticDirectionalLightText(staticDirectionalLight, lightVariableName);
			sceneFragmentShaderInsideMain_ += GetDirectionalLightColorSummationText(lightVariableName);
		}

		for (const DirectionalLight* dynamicDirectionalLight : dynamicDirectionalLights)
		{
			std::string lightVariableName = dynamicDirectionalLight->GetName();
			uniforms_ += GetDirectionalLightUniformTexts(lightVariableName);
			sceneFragmentShaderInsideMain_ += GetDirectionalLightColorSummationText(lightVariableName);
				
		}
	}

	const std::vector<const SpotLight*>& staticSpotLights = scene->GetStaticSpotLights();
	const std::vector<const SpotLight*>& dynamicSpotLights = scene->GetDynamicSpotLights();
	if (staticSpotLights.size() > 0 || dynamicSpotLights.size() > 0)
	{
		sceneFragmentShaderOutsideMain_ += GetSpotLightColorFunctionText() + "\n";

		for (const SpotLight* staticpotLight : staticSpotLights)
		{
			std::string lightVariableName = staticpotLight->GetName();
			sceneFragmentShaderOutsideMain_ += GetStaticSpotLightText(staticpotLight, lightVariableName);
			sceneFragmentShaderInsideMain_ += GetSpotLightColorSummationText(lightVariableName);
		}

		for (const SpotLight* dynamicSpotLight : dynamicSpotLights)
		{
			std::string lightVariableName = dynamicSpotLight->GetName();
			uniforms_ += GetSpotLightUniformTexts(lightVariableName);
			sceneFragmentShaderInsideMain_ += GetSpotLightColorSummationText(lightVariableName);
		}
	}

	sceneFragmentShaderInsideMain_ += "\tcolor = lightColor;\n";

	CombineShader();

	std::cout << sceneFragmentShader_ << std::endl;
}

void ShaderBuilder::BuildSceneVertexShader()
{
	sceneVertexShader_ = GetShaderVersionText();
	sceneVertexShader_ += R"(

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;

uniform mat4 MVP;

uniform mat4 modelMatrix;

out vec3 fragmentPosition;

out vec3 vertexNormal;

void main()
{
	gl_Position = MVP * vec4(position, 1.f);
	vertexNormal = mat3(transpose(inverse(modelMatrix))) * normal;
	fragmentPosition = vec3(modelMatrix * vec4(position, 1.f));
}
)";
}

std::string ShaderBuilder::GetShaderVersionText()
{
	return "#version " + shaderVersion_;
}

std::string ShaderBuilder::GetMaterialVariables()
{
	std::string materialVariableText = std::string("// Base Material Variables\n");
	materialVariableText += "uniform vec3 ";
	materialVariableText += SHADER_VARIABLE_NAMES::MATERIAL::AMBIENT;
	materialVariableText += ";\n";

	materialVariableText += "uniform vec3 ";
	materialVariableText += SHADER_VARIABLE_NAMES::MATERIAL::DIFFUSE;
	materialVariableText += ";\n";

	materialVariableText += "uniform vec3 ";
	materialVariableText += SHADER_VARIABLE_NAMES::MATERIAL::SPECULAR;
	materialVariableText += ";\n";

	materialVariableText += "uniform float ";
	materialVariableText += SHADER_VARIABLE_NAMES::MATERIAL::PHONG_EXPONENT;
	materialVariableText += ";\n\n";
	return materialVariableText;
}

std::string ShaderBuilder::GetPointLightUniformTexts(const std::string& lightVariableName)
{
	return "uniform vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::POSITION + ";\n" + 
		   "uniform vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + ";\n";
}

std::string ShaderBuilder::GetPointLightColorFunctionText()
{
	return R"(
vec3 CalculatePointLightColor(vec3 position, vec3 intensity)
{
	// To light vector
	vec3 wi = position - fragmentPosition;
	float wiLength = length(wi);
	wi /= wiLength;

	// To viewpoint vector
	vec3 wo = viewPosition - fragmentPosition;
	float woLength = length(wo);
	wo /= woLength;

	// Half vector
	vec3 halfVector = (wi + wo) * 0.5f;

	float inverseDistanceSquare = 1 / (wiLength * wiLength);

	// Diffuse
	float cosThetaPrime = max(0.f, dot(wi, vertexNormal));
	vec3 color = diffuseReflectance * cosThetaPrime;

	// Specular
	float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
	color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

	color *= intensity * inverseDistanceSquare;

	return clamp(color, 0.f, 1.f);
}
)";
}

std::string ShaderBuilder::GetStaticPointLightText(const PointLight* pointLight, const std::string& lightVariableName) const
{
	const Vector3& lightPosition = pointLight->GetPosition();
	const Vector3& lightColor = pointLight->GetColor();
	float lightIntensity = pointLight->GetIntensity();

	return "vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::POSITION + " = vec3(" + std::to_string(lightPosition.x) + ", " + std::to_string(lightPosition.y) + ", " + std::to_string(lightPosition.z) + ");\n" +
		   "vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + " = vec3(" + std::to_string(lightColor.x * lightIntensity) + ", " + std::to_string(lightColor.y * lightIntensity) + ", " + std::to_string(lightColor.z * lightIntensity) + ");\n";
}

std::string ShaderBuilder::GetPointLightColorSummationText(const std::string& lightVariableName)
{
	return "\tlightColor += CalculatePointLightColor(" +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::POSITION + ", " +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + ");\n";
}

std::string ShaderBuilder::GetDirectionalLightUniformTexts(const std::string& lightVariableName)
{
	return "uniform vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::DIRECTION + ";\n" +
		   "uniform vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + ";\n";
}

std::string ShaderBuilder::GetDirectionalLightColorFunctionText()
{
	return R"(
vec3 CalculateDirectionalLightColor(vec3 direction, vec3 intensity)
{
	vec3 wi = -direction;
	float wiLength = length(wi);
	wi /= wiLength;

	float normalDotLightDirection = dot(vertexNormal, wi);

	vec3 color = diffuseReflectance * max(0, normalDotLightDirection);

	// To viewpoint vector
	vec3 wo = viewPosition - fragmentPosition;
	float woLength = length(wo);
	wo /= woLength;

	// Half vector
	vec3 halfVector = (wi + wo) * 0.5f;
	float inverseDistanceSquare = 1 / (wiLength * wiLength);

	// Specular
	float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
	color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

	color *= intensity * inverseDistanceSquare;

	return clamp(color, 0.f, 1.f);
}
)";
}

std::string ShaderBuilder::GetStaticDirectionalLightText(const DirectionalLight* directionalLight, const std::string& lightVariableName) const
{
	const Vector3& lightDirection = directionalLight->GetDirection();
	const Vector3& lightColor = directionalLight->GetColor();
	float lightIntensity = directionalLight->GetIntensity();

	return  "vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::DIRECTION + " = vec3(" + std::to_string(lightDirection.x) + ", " + std::to_string(lightDirection.y) + ", " + std::to_string(lightDirection.z) + ");\n" +
			"vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + " = vec3(" + std::to_string(lightColor.x * lightIntensity) + ", " + std::to_string(lightColor.y * lightIntensity) + ", " + std::to_string(lightColor.z * lightIntensity) + ");\n";

}

std::string ShaderBuilder::GetDirectionalLightColorSummationText(const std::string& lightVariableName)
{
	return "\tlightColor += CalculateDirectionalLightColor(" +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::DIRECTION + ", " +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + ");\n";
}

std::string ShaderBuilder::GetSpotLightUniformTexts(const std::string& lightVariableName)
{
	return  "uniform vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::POSITION + ";\n" + 
		  + "uniform vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::DIRECTION + ";\n" +
		  + "uniform vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + ";\n" +
		  + "uniform float " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::COVERAGE_ANGLE + ";\n" +
		  + "uniform float " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::FALLOFF_ANGLE + ";\n";
}

std::string ShaderBuilder::GetSpotLightColorFunctionText()
{
	return R"(
vec3 CalculateSpotLightColor(vec3 position, vec3 direction, vec3 intensity, float coverageAngle, float falloffAngle)
{
	vec3 diffuse = vec3(0.f, 0.f, 0.f);
	vec3 specular = vec3(0.f, 0.f, 0.f);

	// To light vector
	vec3 wi = fragmentPosition - position;
	float wiLength = length(wi);
	wi /= wiLength;

	// To viewpoint vector
	vec3 wo = viewPosition - fragmentPosition;
	float woLength = length(wo);
	wo /= woLength;

	// Half vector
	vec3 halfVector = (wi + wo) * 0.5f;

	float inverseDistanceSquare = 1.f / (wiLength * wiLength);

	float cosCoverage = cos(coverageAngle);
	float cosFalloff = cos(falloffAngle);

	float cosTheta = dot(wi, direction);

	if(cosTheta > cosFalloff)
	{
		diffuse = diffuseReflectance;
	}
	else if(cosTheta > cosCoverage)
	{
		float c = pow((cosTheta - cosCoverage) / (cosFalloff - cosCoverage), 4);
		diffuse = diffuseReflectance * c;
	}

	// Specular
	float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
	specular = specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

	vec3 color = (diffuse + specular) * intensity * inverseDistanceSquare;
	return clamp(color, 0.f, 1.f);
}
)";
}

std::string ShaderBuilder::GetStaticSpotLightText(const SpotLight* spotLight, const std::string& lightVariableName) const
{
	const Vector3& lightPosition = spotLight->GetPosition();
	const Vector3& lightDirection = spotLight->GetDirection();
	const Vector3& lightColor = spotLight->GetColor();
	float lightIntensity = spotLight->GetIntensity();
	float coverageAngle = spotLight->GetCoverageAngle();
	float falloffAngle = spotLight->GetFalloffAngle();

	return  "vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::POSITION + " = vec3(" + std::to_string(lightPosition.x) + ", " + std::to_string(lightPosition.y) + ", " + std::to_string(lightPosition.z) + ");\n"
		  + "vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::DIRECTION + " = vec3(" + std::to_string(lightDirection.x) + ", " + std::to_string(lightDirection.y) + ", " + std::to_string(lightDirection.z) + ");\n"
		  + "vec3 " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + " = vec3(" + std::to_string(lightColor.x * lightIntensity) + ", " + std::to_string(lightColor.y * lightIntensity) + ", " + std::to_string(lightColor.z * lightIntensity) + ");\n"
		  + "float " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::COVERAGE_ANGLE + " = " + std::to_string(coverageAngle) + ";\n"
		  + "float " + lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::FALLOFF_ANGLE + " = " + std::to_string(falloffAngle) + ";\n";
}

std::string ShaderBuilder::GetSpotLightColorSummationText(const std::string& lightVariableName)
{
	return "\tlightColor += CalculateSpotLightColor(" +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::POSITION + ", " +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::DIRECTION + ", " +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::INTENSITY + ", " +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::COVERAGE_ANGLE + ", " +
			lightVariableName + SHADER_VARIABLE_NAMES::LIGHT_KEYWORDS::FALLOFF_ANGLE + ");\n";
}

void ShaderBuilder::CombineShader()
{
	sceneFragmentShader_ = GetShaderVersionText() + "\n\n";
	sceneFragmentShader_ += uniforms_;

	sceneFragmentShader_ += sceneFragmentShaderOutsideMain_;
	sceneFragmentShader_ += R"(
void main()
{
)";
	sceneFragmentShader_ += sceneFragmentShaderInsideMain_ + "\n";
	sceneFragmentShader_ += "}";
}

Static Lighting

Point Light

Point lights are the first light types implemented when making a game engine since they are the types of lights we see in our everyday life; for example, in our office, home, street lights and much more. A point light is consisting of a position, light color, and intensity. In the shader, color variable of it is sent to the fragment shader as a multiplication by its intensity in order to get rid of one more variable.

Corresponding render:

Corresponding fragment shader built by ShaderBuilder:

#version 330 core


out vec3 color;
in vec3 fragmentPosition;
in vec3 vertexNormal;
uniform vec3 viewPosition;

// Base Material Variables
uniform vec3 ambientReflectance;
uniform vec3 diffuseReflectance;
uniform vec3 specularReflectance;
uniform float phongExponent;

vec3 sceneAmbient = vec3(0.098039, 0.098039, 0.098039);

vec3 CalculatePointLightColor(vec3 position, vec3 intensity)
{
        // To light vector
        vec3 wi = position - fragmentPosition;
        float wiLength = length(wi);
        wi /= wiLength;

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;

        float inverseDistanceSquare = 1 / (wiLength * wiLength);

        // Diffuse
        float cosThetaPrime = max(0.f, dot(wi, vertexNormal));
        vec3 color = diffuseReflectance * cosThetaPrime;

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        color *= intensity * inverseDistanceSquare;

        return clamp(color, 0.f, 1.f);
}

vec3 PointLight0Position = vec3(-7.500000, 7.500000, 7.500000);
vec3 PointLight0Intensity = vec3(50.000000, 50.000000, 50.000000);
vec3 PointLight1Position = vec3(7.500000, -7.500000, 10.000000);
vec3 PointLight1Intensity = vec3(50.000000, 50.000000, 50.000000);

void main()
{
        vec3 lightColor = sceneAmbient * ambientReflectance;
        lightColor += CalculatePointLightColor(PointLight0Position, PointLight0Intensity);
        lightColor += CalculatePointLightColor(PointLight1Position, PointLight1Intensity);
        color = lightColor;

}

Directional Light

A directional light is a light lighting everywhere with the same direction and intensity. Huge light systems emit these types of lights such as the sun.

Corresponding render:

Corresponding fragment shader built by ShaderBuilder:

#version 330 core


out vec3 color;
in vec3 fragmentPosition;
in vec3 vertexNormal;
uniform vec3 viewPosition;

// Base Material Variables
uniform vec3 ambientReflectance;
uniform vec3 diffuseReflectance;
uniform vec3 specularReflectance;
uniform float phongExponent;

vec3 sceneAmbient = vec3(0.098039, 0.098039, 0.098039);

vec3 CalculateDirectionalLightColor(vec3 direction, vec3 intensity)
{
        vec3 wi = -direction;
        float wiLength = length(wi);
        wi /= wiLength;

        float normalDotLightDirection = dot(vertexNormal, wi);

        vec3 color = diffuseReflectance * max(0, normalDotLightDirection);

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;
        float inverseDistanceSquare = 1 / (wiLength * wiLength);

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        color *= intensity * inverseDistanceSquare;

        return clamp(color, 0.f, 1.f);
}

vec3 DirectionalLight0Direction = vec3(0.615457, 0.492366, -0.615457);
vec3 DirectionalLight0Intensity = vec3(0.500000, 0.500000, 0.500000);

void main()
{
        vec3 lightColor = sceneAmbient * ambientReflectance;
        lightColor += CalculateDirectionalLightColor(DirectionalLight0Direction, DirectionalLight0Intensity);
        color = lightColor;

}

Spot Light

Let’s talk about the fanciest light ever! A spot light is the light of the stages. It is used in theaters, museums, stadiums and so on in order to point something out. Spot light is nothing more than a point light except for having a direction and fallout and coverage angles.

Calculating spot light is fairly easy. If dot product of light position to fragment position and spot light’s direction is less then cos of the falloff angle then calculate this area’s light like a point light. If it is more then cos of falloff angle but less then cos of coverage angle then the light is ((cosTheta – cosCoverage) / (cosFalloff – cosCoverage))^4 (Basic proportion). If none of these two options, then no light from spotlight at that position.

Corresponding render:

Corresponding fragment shader built by ShaderBuilder:

#version 330 core


out vec3 color;
in vec3 fragmentPosition;
in vec3 vertexNormal;
uniform vec3 viewPosition;

// Base Material Variables
uniform vec3 ambientReflectance;
uniform vec3 diffuseReflectance;
uniform vec3 specularReflectance;
uniform float phongExponent;

vec3 sceneAmbient = vec3(0.098039, 0.098039, 0.098039);

vec3 CalculateSpotLightColor(vec3 position, vec3 direction, vec3 intensity, float coverageAngle, float falloffAngle)
{
        vec3 diffuse = vec3(0.f, 0.f, 0.f);
        vec3 specular = vec3(0.f, 0.f, 0.f);

        // To light vector
        vec3 wi = fragmentPosition - position;
        float wiLength = length(wi);
        wi /= wiLength;

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;

        float inverseDistanceSquare = 1.f / (wiLength * wiLength);

        float cosCoverage = cos(coverageAngle);
        float cosFalloff = cos(falloffAngle);

        float cosTheta = dot(wi, direction);

        if(cosTheta > cosFalloff)
        {
                diffuse = diffuseReflectance;
        }
        else if(cosTheta > cosCoverage)
        {
                float c = pow((cosTheta - cosCoverage) / (cosFalloff - cosCoverage), 4);
                diffuse = diffuseReflectance * c;
        }

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        specular = specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        vec3 color = (diffuse + specular) * intensity * inverseDistanceSquare;
        return clamp(color, 0.f, 1.f);
}

vec3 SpotLight0Position = vec3(15.000000, 15.000000, 15.000000);
vec3 SpotLight0Direction = vec3(-0.577350, -0.577350, -0.577350);
vec3 SpotLight0Intensity = vec3(200.000000, 200.000000, 200.000000);
float SpotLight0CoverageAngle = 0.174533;
float SpotLight0FalloffAngle = 0.139626;

void main()
{
        vec3 lightColor = sceneAmbient * ambientReflectance;
        lightColor += CalculateSpotLightColor(SpotLight0Position, SpotLight0Direction, SpotLight0Intensity, SpotLight0CoverageAngle, SpotLight0FalloffAngle);
        color = lightColor;

}

All three light types:

Corresponding render:

Corresponding fragment shader built by ShaderBuilder:

#version 330 core


out vec3 color;
in vec3 fragmentPosition;
in vec3 vertexNormal;
uniform vec3 viewPosition;

// Base Material Variables
uniform vec3 ambientReflectance;
uniform vec3 diffuseReflectance;
uniform vec3 specularReflectance;
uniform float phongExponent;

vec3 sceneAmbient = vec3(0.098039, 0.098039, 0.098039);

vec3 CalculatePointLightColor(vec3 position, vec3 intensity)
{
        // To light vector
        vec3 wi = position - fragmentPosition;
        float wiLength = length(wi);
        wi /= wiLength;

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;

        float inverseDistanceSquare = 1 / (wiLength * wiLength);

        // Diffuse
        float cosThetaPrime = max(0.f, dot(wi, vertexNormal));
        vec3 color = diffuseReflectance * cosThetaPrime;

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        color *= intensity * inverseDistanceSquare;

        return clamp(color, 0.f, 1.f);
}

vec3 PointLight0Position = vec3(-7.500000, 7.500000, 7.500000);
vec3 PointLight0Intensity = vec3(50.000000, 50.000000, 50.000000);
vec3 PointLight1Position = vec3(7.500000, -7.500000, 10.000000);
vec3 PointLight1Intensity = vec3(50.000000, 50.000000, 50.000000);

vec3 CalculateDirectionalLightColor(vec3 direction, vec3 intensity)
{
        vec3 wi = -direction;
        float wiLength = length(wi);
        wi /= wiLength;

        float normalDotLightDirection = dot(vertexNormal, wi);

        vec3 color = diffuseReflectance * max(0, normalDotLightDirection);

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;
        float inverseDistanceSquare = 1 / (wiLength * wiLength);

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        color *= intensity * inverseDistanceSquare;

        return clamp(color, 0.f, 1.f);
}

vec3 DirectionalLight0Direction = vec3(0.615457, 0.492366, -0.615457);
vec3 DirectionalLight0Intensity = vec3(0.500000, 0.500000, 0.500000);

vec3 CalculateSpotLightColor(vec3 position, vec3 direction, vec3 intensity, float coverageAngle, float falloffAngle)
{
        vec3 diffuse = vec3(0.f, 0.f, 0.f);
        vec3 specular = vec3(0.f, 0.f, 0.f);

        // To light vector
        vec3 wi = fragmentPosition - position;
        float wiLength = length(wi);
        wi /= wiLength;

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;

        float inverseDistanceSquare = 1.f / (wiLength * wiLength);

        float cosCoverage = cos(coverageAngle);
        float cosFalloff = cos(falloffAngle);

        float cosTheta = dot(wi, direction);

        if(cosTheta > cosFalloff)
        {
                diffuse = diffuseReflectance;
        }
        else if(cosTheta > cosCoverage)
        {
                float c = pow((cosTheta - cosCoverage) / (cosFalloff - cosCoverage), 4);
                diffuse = diffuseReflectance * c;
        }

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        specular = specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        vec3 color = (diffuse + specular) * intensity * inverseDistanceSquare;
        return clamp(color, 0.f, 1.f);
}

vec3 SpotLight0Position = vec3(15.000000, 15.000000, 15.000000);
vec3 SpotLight0Direction = vec3(-0.577350, -0.577350, -0.577350);
vec3 SpotLight0Intensity = vec3(200.000000, 200.000000, 200.000000);
float SpotLight0CoverageAngle = 0.174533;
float SpotLight0FalloffAngle = 0.139626;

void main()
{
        vec3 lightColor = sceneAmbient * ambientReflectance;
        lightColor += CalculatePointLightColor(PointLight0Position, PointLight0Intensity);
        lightColor += CalculatePointLightColor(PointLight1Position, PointLight1Intensity);
        lightColor += CalculateDirectionalLightColor(DirectionalLight0Direction, DirectionalLight0Intensity);
        lightColor += CalculateSpotLightColor(SpotLight0Position, SpotLight0Direction, SpotLight0Intensity, SpotLight0CoverageAngle, SpotLight0FalloffAngle);
        color = lightColor;

}

Dynamic Lighting

I added 1 point lights and 2 spot lights, and I updated point light and spot lights at runtime. Since this is a temporary change I put and updated them in Game class.

Game::Game() : Application()
{
	mainScene_->ReadSceneData("../GameProject/Content/Scenes/ThreeDifferentShapes.xml");

	newPointLight1 = new PointLight();
	newPointLight1->SetPosition(Vector3(-7.5f, 7.5f, 7.5f));
	newPointLight1->SetColor(Vector3(1.f, 1.f, 1.f));
	newPointLight1->SetIntensity(50.f);
	newPointLight1->SetLightMobility(LightMobility::Movable);
	mainScene_->AddPointLight(newPointLight1);

	newSpotLight1 = new SpotLight();
	newSpotLight1->SetPosition(Vector3(15.f, 15.f, 15.f));
	newSpotLight1->SetDirection(Vector3(-1.f, -1.f, -1.f));
	newSpotLight1->SetColor(Vector3(1.f, 0.f, 0.f));
	newSpotLight1->SetIntensity(200.f);
	newSpotLight1->SetCoverageAngle(10);
	newSpotLight1->SetFalloffAngle(8);
	newSpotLight1->SetLightMobility(LightMobility::Movable);
	mainScene_->AddSpotLight(newSpotLight1);

	newSpotLight2 = new SpotLight();
	newSpotLight2->SetPosition(Vector3(-15.f, -15.f, 15.f));
	newSpotLight2->SetDirection(Vector3(1.f, 1.f, -1.f));
	newSpotLight2->SetColor(Vector3(1.f, 1.f, 0.6f));
	newSpotLight2->SetIntensity(200.f);
	newSpotLight2->SetCoverageAngle(8);
	newSpotLight2->SetFalloffAngle(4);
	newSpotLight2->SetLightMobility(LightMobility::Movable);
	mainScene_->AddSpotLight(newSpotLight2);
}

void Game::Run()
{
	static float delay = 0.f;

	static Vector3 initialDirection1 = newSpotLight1->GetDirection();
	static Vector3 othonormalBasis1 = newSpotLight1->GetDirection().GetOrthonormalBasis();

	static Vector3 initialDirection2 = newSpotLight2->GetDirection();
	static Vector3 othonormalBasis2 = newSpotLight2->GetDirection().GetOrthonormalBasis();

	Vector3 newDirection1 = initialDirection1 + othonormalBasis1 * sin(delay) * 0.25f;
	newSpotLight1->SetDirection(newDirection1);

	Vector3 newDirection2 = initialDirection2 + othonormalBasis2 * sin(delay) * 0.25f;
	newSpotLight2->SetDirection(newDirection2);


	delay += 0.0025f;

	static float theta = 0.f;
	static float radius = 10.f;
	theta += 0.0025f;
	Vector3 pointLightPosition = Vector3(radius * cos(theta), radius * sin(theta), 10.f);
	newPointLight1->SetPosition(pointLightPosition);
}

Corresponding render:

Corresponding fragment shader built by ShaderBuilder:

#version 330 core


out vec3 color;
in vec3 fragmentPosition;
in vec3 vertexNormal;
uniform vec3 viewPosition;

uniform vec3 PointLight0Position;
uniform vec3 PointLight0Intensity;
uniform vec3 DirectionalLight0Direction;
uniform vec3 DirectionalLight0Intensity;
uniform vec3 SpotLight0Position;
uniform vec3 SpotLight0Direction;
uniform vec3 SpotLight0Intensity;
uniform float SpotLight0CoverageAngle;
uniform float SpotLight0FalloffAngle;
uniform vec3 SpotLight1Position;
uniform vec3 SpotLight1Direction;
uniform vec3 SpotLight1Intensity;
uniform float SpotLight1CoverageAngle;
uniform float SpotLight1FalloffAngle;
// Base Material Variables
uniform vec3 ambientReflectance;
uniform vec3 diffuseReflectance;
uniform vec3 specularReflectance;
uniform float phongExponent;

vec3 sceneAmbient = vec3(0.098039, 0.098039, 0.098039);

vec3 CalculatePointLightColor(vec3 position, vec3 intensity)
{
        // To light vector
        vec3 wi = position - fragmentPosition;
        float wiLength = length(wi);
        wi /= wiLength;

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;

        float inverseDistanceSquare = 1 / (wiLength * wiLength);

        // Diffuse
        float cosThetaPrime = max(0.f, dot(wi, vertexNormal));
        vec3 color = diffuseReflectance * cosThetaPrime;

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        color *= intensity * inverseDistanceSquare;

        return clamp(color, 0.f, 1.f);
}


vec3 CalculateDirectionalLightColor(vec3 direction, vec3 intensity)
{
        vec3 wi = -direction;
        float wiLength = length(wi);
        wi /= wiLength;

        float normalDotLightDirection = dot(vertexNormal, wi);

        vec3 color = diffuseReflectance * max(0, normalDotLightDirection);

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;
        float inverseDistanceSquare = 1 / (wiLength * wiLength);

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        color += specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        color *= intensity * inverseDistanceSquare;

        return clamp(color, 0.f, 1.f);
}


vec3 CalculateSpotLightColor(vec3 position, vec3 direction, vec3 intensity, float coverageAngle, float falloffAngle)
{
        vec3 diffuse = vec3(0.f, 0.f, 0.f);
        vec3 specular = vec3(0.f, 0.f, 0.f);

        // To light vector
        vec3 wi = fragmentPosition - position;
        float wiLength = length(wi);
        wi /= wiLength;

        // To viewpoint vector
        vec3 wo = viewPosition - fragmentPosition;
        float woLength = length(wo);
        wo /= woLength;

        // Half vector
        vec3 halfVector = (wi + wo) * 0.5f;

        float inverseDistanceSquare = 1.f / (wiLength * wiLength);

        float cosCoverage = cos(coverageAngle);
        float cosFalloff = cos(falloffAngle);

        float cosTheta = dot(wi, direction);

        if(cosTheta > cosFalloff)
        {
                diffuse = diffuseReflectance;
        }
        else if(cosTheta > cosCoverage)
        {
                float c = pow((cosTheta - cosCoverage) / (cosFalloff - cosCoverage), 4);
                diffuse = diffuseReflectance * c;
        }

        // Specular
        float cosAlphaPrimeToThePowerOfPhongExponent = pow(max(0.f, dot(vertexNormal, halfVector)), phongExponent);
        specular = specularReflectance * cosAlphaPrimeToThePowerOfPhongExponent;

        vec3 color = (diffuse + specular) * intensity * inverseDistanceSquare;
        return clamp(color, 0.f, 1.f);
}


void main()
{
        vec3 lightColor = sceneAmbient * ambientReflectance;
        lightColor += CalculatePointLightColor(PointLight0Position, PointLight0Intensity);
        lightColor += CalculateDirectionalLightColor(DirectionalLight0Direction, DirectionalLight0Intensity);
        lightColor += CalculateSpotLightColor(SpotLight0Position, SpotLight0Direction, SpotLight0Intensity, SpotLight0CoverageAngle, SpotLight0FalloffAngle);
        lightColor += CalculateSpotLightColor(SpotLight1Position, SpotLight1Direction, SpotLight1Intensity, SpotLight1CoverageAngle, SpotLight1FalloffAngle);
        color = lightColor;

}