I am in the process of refactoring my engine for deferred rendering and am trying to implement proper lighting in OpenGL 2.1 / GLSL 120. So far, I believe I have implemented an ambient component based on an ambient light's color as well as the diffuse components of directional, point, and spot lights. However, I became a little stumped when it came to the specular components of these light types. Here is my current code for the second (lighting) pass of the deferred shader:
light_pass.vs
#version 120
attribute vec2 a_position;
uniform mat4 u_projection;
uniform mat4 u_view;
void main()
{
gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0);
}
light_pass.fs
#version 120
const int AMBIENT = 0;
const int DIRECTIONAL = 1;
const int POINT = 2;
const int SPOT = 3;
struct Light
{
int type;
vec3 color;
vec3 direction;
vec3 position;
float radius;
float inner_angle;
float outer_angle;
};
uniform vec2 u_screen_size;
uniform Light u_light;
uniform sampler2D u_position;
uniform sampler2D u_normal;
uniform sampler2D u_diffuse;
vec2 calc_texcoord()
{
return gl_FragCoord.xy / u_screen_size;
}
vec4 calc_ambient_light()
{
return vec4(u_light.color, 1.0);
}
vec4 calc_directional_light(vec3 position, vec3 normal)
{
vec3 light_dir = normalize(-u_light.direction);
float contribution = max(dot(normal, light_dir), 0.0);
return vec4(contribution * u_light.color, 1.0);
}
vec4 calc_point_light(vec3 position, vec3 normal)
{
float dist = length(u_light.position - position);
float att = clamp(1.0 - (dist*dist)/(u_light.radius*u_light.radius), 0.0, 1.0); att *= att;
vec3 light_dir = normalize(u_light.position - position);
float contribution = max(0.0, dot(normal, light_dir));
return vec4(contribution * u_light.color, 1.0) * att;
}
vec4 calc_spot_light(vec3 position, vec3 normal)
{
float dist = length(u_light.position - position);
float att = clamp(1.0 - (dist*dist)/(u_light.radius*u_light.radius), 0.0, 1.0); att *= att;
vec3 light_dir = normalize(u_light.position - position);
float contribution = max(0.0, dot(normal, light_dir));
vec3 spot_dir = normalize(u_light.direction);
float angle = acos(dot(light_dir, spot_dir));
float angle_factor = 1 - smoothstep(u_light.inner_angle, u_light.outer_angle, angle);
return vec4(contribution * u_light.color, 1.0) * angle_factor * att;
}
void main()
{
vec2 texcoord = calc_texcoord();
vec3 position = texture2D(u_position, texcoord).xyz;
vec3 normal = texture2D(u_normal, texcoord).xyz;
vec3 diffuse = texture2D(u_diffuse, texcoord).xyz;
//normal = normalize(normal);
vec4 result = vec4(0, 0, 0, 0);
int type = u_light.type;
if(type == AMBIENT)
result = calc_ambient_light();
else if(type == DIRECTIONAL)
result = calc_directional_light(position, normal);
else if(type == POINT)
result = calc_point_light(position, normal);
else if(type == SPOT)
result = calc_spot_light(position, normal);
gl_FragColor = vec4(diffuse, 1.0) * result;//vec4(texcoord, 0, 1);
}
And here are the properties I have currently use in my light class (ignore the shadow properties) to define each type of light:
light.py
from enum import Enum
from pyorama.core.entity import Entity
class LightTypes(Enum):
AMBIENT = 0
DIRECTIONAL = 1
POINT = 2
SPOT = 3
class AmbientLight(Entity):
def __init__(self, color):
self.color = color
self.cast_shadow = False
super(AmbientLight, self).__init__()
class DirectionalLight(Entity):
def __init__(self, color, direction):
self.color = color
self.direction = direction
self.cast_shadow = False
super(DirectionalLight, self).__init__()
class PointLight(Entity):
def __init__(self, color, position, radius):
self.color = color
self.position = position
self.radius = radius
self.cast_shadow = False
super(PointLight, self).__init__()
class SpotLight(Entity):
def __init__(self, color, position, radius, direction, inner_angle, outer_angle):
self.color = color
self.position = position
self.radius = radius
self.direction = direction
self.inner_angle = inner_angle
self.outer_angle = outer_angle
self.cast_shadow = False
super(SpotLight, self).__init__()
Now currently, I do not have a defined material class and am treating the texture associated with my meshes as the source for a model's diffuse component. My biggest question with lighting is understanding which properties belong to the light and which ones belong to the material.
If I look at a typical .mtl
(material) file for a 3d model, I see that at the most basic level, there were Ka, Kd, Ks, and Ns properties, corresponding to an ambient rgb value, diffuse rgb value, specular rgb value, and a mysterious specular exponent float. Does the light have these same components as well? How are the light and the material components combined together? Lots of the tutorials online using OpenGL 2.1 use the fixed function terminology and the glLightand
glMaterial*` calls, which is further adding to my confusion.
Also, if I have a textured model, aren't these colors defined by the color of the texture rather than separate material properties? Currently, I do not have a material class and am simply assuming the diffuse color is the color of the texture at an interpolated point sampled in my fragment shader. What list of properties should a Material class have? Are there any missing/incorrect properties in my current Lighting class? I am not particularly interested in any code, just an explanation of what light and material properties I need to combine to get the final color output in a basic Blinn-Phong lighting model with ambient + diffuse + specular components. Any help would be greatly appreciated.