Phong Shader produces the best results of all these shading methods, but is much more expensive to calculate, because instead of calculating only 3 color values per triangle face, it calculates the color for each pixel! Instead of interpolating the color values within the face, this method interpolates the smooth normals.
Assuming you model covers half of your 1200 x 900 window, that means the fragment shader calculates 1200 x 900 / 2 = 540.000 times the color according to the scene's light sources.
Complete Source Code:
Note: from now on we wont use the geometry shader again.
Renderer_OpenGL.cpp
#include "Renderer_OpenGL.h"
#include "Shader.h"
#include "Model.h"
#include "OBJ.h"
#include "Main.h"
#include <iostream>
#define MAX_MODEL_INSTANCES 1000
#define MAX_MATERIALS 1000
#define MAX_POINTLIGHTS 10
#define MAX_DIRECTIONALLIGHTS 10
#define MAX_SPOTLIGHTS 10
void CheckForGLError()
{
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
{
std::cout << "ERROR: (Renderer_OpenGL) \t";
if (error == GL_INVALID_ENUM)
std::cout << "GL_INVALID_ENUM";
if (error == GL_INVALID_VALUE)
std::cout << "GL_INVALID_VALUE";
if (error == GL_INVALID_OPERATION)
std::cout << "GL_INVALID_OPERATION";
if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_OUT_OF_MEMORY)
std::cout << "GL_OUT_OF_MEMORY";
if (error == GL_STACK_UNDERFLOW)
std::cout << "GL_STACK_UNDERFLOW";
if (error == GL_STACK_OVERFLOW)
std::cout << "GL_STACK_OVERFLOW";
std::cout << (char)7 << std::endl; /*play sound*/
std::cin.get();
}
}
Renderer_OpenGL::Renderer_OpenGL(unsigned int width, unsigned int height)
{
// settings
// ----------------------------------------------------------------------------------------------------------
glEnable(GL_DEPTH_TEST); // 3D
glEnable(GL_CULL_FACE); // dont render backward faces
// ----------------------------------------------------------------------------------------------------------
// setup framebuffer:
// ----------------------------------------------------------------------------------------------------------
// make sure that the framebuffer isnt too small
m_width = max2(100, width);
m_height = max2(100, height);
glViewport(0, 0, m_width, m_height);
glGenTextures(1, &m_framebuffercolortexture);
glBindTexture(GL_TEXTURE_2D, m_framebuffercolortexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferintegertexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferintegertexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// special texture to store only integer values (32 bit)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, m_width, m_height, 0, GL_RED_INTEGER, GL_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferdepthtexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferdepthtexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create framebuffer
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_framebuffercolortexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_framebufferintegertexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_framebufferdepthtexture, 0);
std::vector<GLenum> drawbuffers = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(drawbuffers.size(), drawbuffers.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ERROR CHECK:
GLenum status = glCheckNamedFramebufferStatus(m_framebuffer, GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (status == GL_FRAMEBUFFER_UNDEFINED)
std::cout << "ERROR: undefined framebuffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
std::cout << "ERROR: a necessary attachment is uninitialized" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
std::cout << "ERROR: no attachments" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)
std::cout << "ERROR: incomplete draw buffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
std::cout << "ERROR: incomplete read buffer" << std::endl;
if (status == GL_FRAMEBUFFER_UNSUPPORTED)
std::cout << "ERROR: combination of attachments is not supported" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)
std::cout << "ERROR: number if samples for all attachments does not match" << std::endl;
}
// ----------------------------------------------------------------------------------------------------------
// setup program
// ----------------------------------------------------------------------------------------------------------
m_program = glCreateProgram();
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
CompileShader(vertexshader, LoadTextFile("shader.vs"));
CompileShader(fragmentshader, LoadTextFile("shader.fs"));
LinkProgram(m_program, { vertexshader , fragmentshader });
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
// ----------------------------------------------------------------------------------------------------------
// setup vertexarray
// ----------------------------------------------------------------------------------------------------------
glGenVertexArrays(1, &m_vertexarray);
glGenBuffers(1, &m_vertexbuffer);
glGenBuffers(1, &m_instancebuffer);
glBindVertexArray(m_vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 0));
glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 3));
glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 5));
glVertexAttribIPointer(3, 1, GL_INT, sizeof(Vertex), (void*)(sizeof(float) * 8));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_instancebuffer);
glVertexAttribPointer(4, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 0));
glVertexAttribPointer(5, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 4));
glVertexAttribPointer(6, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 8));
glVertexAttribPointer(7, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 12));
glVertexAttribIPointer(8, 1, GL_INT, sizeof(ModelInstance), (void*)(sizeof(float) * 16));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
// sent these attributes only once per instance to the program:
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glVertexAttribDivisor(6, 1);
glVertexAttribDivisor(7, 1);
glVertexAttribDivisor(8, 1);
glBindVertexArray(0);
// ----------------------------------------------------------------------------------------------------------
// setup vertex buffer
// ----------------------------------------------------------------------------------------------------------
std::vector<Vertex> vertices;
std::vector<Material> materials;
LoadSphere(vertices, materials, 10, 10);
if (vertices.size() == 0)
{
vertices.push_back({ { 0, 0, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });
vertices.push_back({ { 1, 0, 0 },{ 0, 1 },{ 0, 0, 1 },{ 0 } });
vertices.push_back({ { 0, 1, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });
}
m_vertexcount = vertices.size();
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// setup instance buffer
// ----------------------------------------------------------------------------------------------------------
glBindBuffer(GL_UNIFORM_BUFFER, m_instancebuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ModelInstance) * MAX_MODEL_INSTANCES, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// setup material buffer
// ----------------------------------------------------------------------------------------------------------
materials.resize(MAX_MATERIALS);
glGenBuffers(1, &m_materialbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_materialbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(Material) * materials.size(), materials.data(), GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, 1, m_materialbuffer);
// ----------------------------------------------------------------------------------------------------------
// setup light buffer
// ----------------------------------------------------------------------------------------------------------
glGenBuffers(1, &m_directionallightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_directionallightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLight) * MAX_DIRECTIONALLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glGenBuffers(1, &m_pointlightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_pointlightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(PointLight) * MAX_POINTLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glGenBuffers(1, &m_spotlightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_spotlightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(SpotLight) * MAX_SPOTLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// bind buffers to uniform blocks:
glBindBufferBase(GL_UNIFORM_BUFFER, 2, m_directionallightbuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, 3, m_pointlightbuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, 4, m_spotlightbuffer);
// ----------------------------------------------------------------------------------------------------------
}
Renderer_OpenGL::~Renderer_OpenGL()
{
glDeleteProgram(m_program);
glDeleteFramebuffers(1, &m_framebuffer);
glDeleteTextures(1, &m_framebuffercolortexture);
glDeleteTextures(1, &m_framebufferintegertexture);
glDeleteTextures(1, &m_framebufferdepthtexture);
glDeleteVertexArrays(1, &m_vertexarray);
glDeleteBuffers(1, &m_vertexbuffer);
glDeleteBuffers(1, &m_instancebuffer);
glDeleteBuffers(1, &m_materialbuffer);
glDeleteBuffers(1, &m_directionallightbuffer);
glDeleteBuffers(1, &m_pointlightbuffer);
glDeleteBuffers(1, &m_spotlightbuffer);
}
void Renderer_OpenGL::Render(const Scene & scene, int& tracked_ID)
{
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
// explicitly clear each buffer:
float clearvalue0[] = { 0.1f, 0.2f, 0.7f, 0.0f }; // background color
int clearvalue1[] = { -1 }; // background integer values
glClearBufferfv(GL_COLOR, 0, clearvalue0);
glClearBufferiv(GL_COLOR, 1, clearvalue1);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glUseProgram(m_program);
glBindVertexArray(m_vertexarray);
// set camera
// ----------------------------------------------------------------------------------------------------------
glUniformMatrix4fv(glGetUniformLocation(m_program, "View"), 1, false, value_ptr(scene.Camera.View()));
glUniformMatrix4fv(glGetUniformLocation(m_program, "Projection"), 1, false, value_ptr(perspective(scene.Camera.FoV, 1.33f, scene.Camera.ZNear, scene.Camera.ZFar)));
// ----------------------------------------------------------------------------------------------------------
// set light sources:
// ----------------------------------------------------------------------------------------------------------
glUniform3fv(glGetUniformLocation(m_program, "CameraPosition"), 1, value_ptr(scene.Camera.Position()));
glUniform3fv(glGetUniformLocation(m_program, "Ambient"), 1, value_ptr(scene.Light.Ambient));
unsigned int directionallightcount = min2(MAX_DIRECTIONALLIGHTS, scene.Light.DirectionalLights.size());
unsigned int pointlightcount = min2(MAX_POINTLIGHTS, scene.Light.PointLights.size());
unsigned int spotlightcount = min2(MAX_SPOTLIGHTS, scene.Light.SpotLights.size());
glUniform1i(glGetUniformLocation(m_program, "DirectionalLightCount"), directionallightcount);
glUniform1i(glGetUniformLocation(m_program, "PointLightCount"), pointlightcount);
glUniform1i(glGetUniformLocation(m_program, "SpotLightCount"), spotlightcount);
if (directionallightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_directionallightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DirectionalLight) * directionallightcount, scene.Light.DirectionalLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
if (pointlightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_pointlightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(PointLight) * pointlightcount, scene.Light.PointLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
if (spotlightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_spotlightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SpotLight) * spotlightcount, scene.Light.SpotLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
// ----------------------------------------------------------------------------------------------------------
// create instance buffer data:
// ----------------------------------------------------------------------------------------------------------
std::vector<ModelInstance> instances;
for (auto& object : scene.Objects)
instances.push_back({ object.ModelMatrix(), (int)object.ID() });
// ----------------------------------------------------------------------------------------------------------
// determine instance count:
// ----------------------------------------------------------------------------------------------------------
unsigned int instancecount = instances.size();
if (instancecount >= MAX_MODEL_INSTANCES)
instancecount = MAX_MODEL_INSTANCES;
// ----------------------------------------------------------------------------------------------------------
// upload instance buffer data:
// ----------------------------------------------------------------------------------------------------------
glBindBuffer(GL_UNIFORM_BUFFER, m_instancebuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ModelInstance) * instancecount, instances.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// render objects in scene
glDrawArraysInstanced(GL_TRIANGLES, 0, m_vertexcount, instancecount);
glBindVertexArray(0);
glUseProgram(0);
// read ID at cursor position:
ivec2 cursorposition = Framework->CursorPosition();
ivec2 windowsize = Framework->WindowSize();
int x = cursorposition.x * (float)m_width / windowsize.x;
int y = cursorposition.y * (float)m_height / windowsize.y;
glReadBuffer(GL_COLOR_ATTACHMENT1);
glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &tracked_ID);
// copy to default framebuffer:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(
0, 0, m_width, m_height,
0, 0, windowsize.x, windowsize.y,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// check for errors
CheckForGLError();
}
Shader Source Code:
Vertex Shader: "shader.vs"
#version 450 core
layout (location = 0) in vec3 in_position;
layout (location = 1) in vec2 in_texcoord;
layout (location = 2) in vec3 in_normal;
layout (location = 3) in int in_materialindex;
layout (location = 4) in mat4 in_model;
//layout (location = 5) in use ...
//layout (location = 6) in use ...
//layout (location = 7) in use ...
layout (location = 8) in int in_objectID;
out VS_FS_INTERFACE {
vec3 position;
vec2 texcoord;
vec3 normal;
flat int materialindex;
flat int objectID;
} vs_out;
uniform mat4 View = mat4(1);
uniform mat4 Projection = mat4(1);
void main(void)
{
mat4 MVP = Projection * View * in_model;
gl_Position = MVP * vec4(in_position, 1);
vs_out.position = (in_model * vec4(in_position, 1)).xyz;
vs_out.texcoord = in_texcoord;
vs_out.normal = (in_model * vec4(in_normal, 0)).xyz;
vs_out.materialindex = in_materialindex;
vs_out.objectID = in_objectID;
}
Fragment Shader: "shader.fs"
#version 450 core
#define MAX_MATERIALS 1000
#define MAX_POINTLIGHTS 10
#define MAX_DIRECTIONALLIGHTS 10
#define MAX_SPOTLIGHTS 10
in VS_FS_INTERFACE {
vec3 position;
vec2 texcoord;
vec3 normal;
flat int materialindex;
flat int objectID;
} vs_out;
// here the definition has vec4s instead of vec3
// thats because some drivers get the std140 layout for vec3s wrong
struct Material {
vec4 Ka, Kd, Ks;
float Ns;
float d;
};
// has constant intensity
struct DirectionalLight {
vec4 Direction;
vec4 Intensity;
};
// has distance-dependent intensity
struct PointLight {
vec4 Position;
vec4 Intensity;
float AttenuationLinear;
};
// has angle-dependent intensity
struct SpotLight {
vec4 Position;
vec4 Direction;
vec4 Intensity;
int Exponent;
};
layout (std140, binding = 1) uniform MaterialBlock
{
Material Materials[MAX_MATERIALS];
};
layout (std140, binding = 2) uniform DirectionalLightBlock
{
DirectionalLight DirectionalLights[MAX_DIRECTIONALLIGHTS];
};
layout (std140, binding = 3) uniform PointLightBlock
{
PointLight PointLights[MAX_POINTLIGHTS];
};
layout (std140, binding = 4) uniform SpotLightBlock
{
SpotLight SpotLights[MAX_SPOTLIGHTS];
};
uniform vec3 CameraPosition = vec3(0, 0, 0);
uniform vec3 Ambient = vec3(0, 0, 0);
uniform int DirectionalLightCount = 0;
uniform int PointLightCount = 0;
uniform int SpotLightCount = 0;
layout (location = 0) out vec4 Fragment0;
layout (location = 1) out int Fragment1;
void ProcessDirectionalLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfaceNormal, vec3 SurfaceToCamera);
void ProcessPointLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfaceNormal, vec3 SurfaceToCamera);
void ProcessSpotLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfaceNormal, vec3 SurfaceToCamera);
void main(void)
{
// total intensities
vec3 TotalDiffuse = vec3(0, 0, 0);
vec3 TotalSpecular = vec3(0, 0, 0);
vec3 SurfaceNormal = normalize(vs_out.normal);
vec3 SurfaceToCamera = normalize(CameraPosition - vs_out.position);
ProcessDirectionalLights( TotalDiffuse, TotalSpecular, SurfaceNormal, SurfaceToCamera);
ProcessPointLights( TotalDiffuse, TotalSpecular, SurfaceNormal, SurfaceToCamera);
ProcessSpotLights( TotalDiffuse, TotalSpecular, SurfaceNormal, SurfaceToCamera);
vec3 Ka = Materials[vs_out.materialindex].Ka.rgb;
vec3 Kd = Materials[vs_out.materialindex].Kd.rgb;
vec3 Ks = Materials[vs_out.materialindex].Ks.rgb;
vec3 Intensity = Ambient * Ka + TotalDiffuse * Kd + TotalSpecular * Ks;
float d = Materials[vs_out.materialindex].d;
Fragment0 = vec4(Intensity, d);
Fragment1 = vs_out.objectID;
}
void ProcessDirectionalLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfaceNormal, vec3 SurfaceToCamera)
{
float Ns = Materials[vs_out.materialindex].Ns;
for (int i = 0; i < DirectionalLightCount; i++)
{
// for specular:
vec3 ReflectedLight = normalize(reflect(DirectionalLights[i].Direction.xyz, SurfaceNormal));
Diffuse += DirectionalLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, -normalize(DirectionalLights[i].Direction.xyz)));
Specular += DirectionalLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns)));
}
}
void ProcessPointLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfaceNormal, vec3 SurfaceToCamera)
{
float Ns = Materials[vs_out.materialindex].Ns;
for (int i = 0; i < PointLightCount; i++)
{
// for diffuse:
vec3 SurfaceToLight = PointLights[i].Position.xyz - vs_out.position;
float DistanceLightSurface = length(SurfaceToLight);
SurfaceToLight /= DistanceLightSurface;
float Attenuation = 1.0f + PointLights[i].AttenuationLinear * DistanceLightSurface;
// for specular:
vec3 ReflectedLight = normalize(reflect(-SurfaceToLight, SurfaceNormal));
Diffuse += PointLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, SurfaceToLight)) / Attenuation;
Specular += PointLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns))) / Attenuation;
}
}
void ProcessSpotLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfaceNormal, vec3 SurfaceToCamera)
{
float Ns = Materials[vs_out.materialindex].Ns;
for (int i = 0; i < SpotLightCount; i++)
{
// for diffuse:
vec3 SurfaceToLight = normalize(SpotLights[i].Position.xyz - vs_out.position);
float CosAngle = dot(-SurfaceToLight, normalize(SpotLights[i].Direction.xyz));
float Multiplier = pow(max(0, CosAngle), max(1, SpotLights[i].Exponent));
// for specular:
vec3 ReflectedLight = normalize(reflect(-SurfaceToLight, SurfaceNormal));
Diffuse += SpotLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, SurfaceToLight)) * Multiplier;
Specular += SpotLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns))) * Multiplier;
}
}