OpenGL learning note 9 - lighting 3 realizes three types of lighting: parallel light, point light, spotlight
Preface
stay Last introduction to lighting Three kinds of light were introduced
Picture selected from LearnOpenGL
Parallel light
In the simplest sense, the directional light can be understood as the sun light. The light has a fixed color and a unified direction, and the attenuation value can be ignored, that is, the light intensity has no relationship with the distance. As shown in the picture:
Code representation
#version 330 core out vec4 FragColor; struct Material { sampler2D diffuse; sampler2D specular; float shininess; }; struct Light { //vec3 position; vec3 direction; vec3 ambient; vec3 diffuse; vec3 specular; }; in vec3 FragPos; in vec3 Normal; in vec2 TexCoords; uniform vec3 viewPos; uniform Material material; uniform Light light; void main() { // ambient vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb; // diffuse vec3 norm = normalize(Normal); // vec3 lightDir = normalize(light.position - FragPos); vec3 lightDir = normalize(-light.direction); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb; // specular vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb; vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); }
Spot light
Point light source can be simply understood as lighting. The light is emitted from a point, and the light intensity is getting weaker as the distance is getting farther and farther
attenuation
Decreasing the intensity of light as the distance it travels increases is often called attenuation. Linear attenuation is easy to achieve, but the effect is not real. The attenuation effect in reality is shown in the figure, not linear attenuation:
The following formula calculates the attenuation value based on the distance between the segment and the light source, and then we multiply it by the intensity vector of the light:
Here d represents the distance of the segment from the light source. Next, in order to calculate the attenuation value, we define three (configurable) terms: constant term Kc, primary term Kl and secondary term Kq.
- The constant term is usually kept at 1.0. Its main function is to ensure that the denominator will never be smaller than 1. Otherwise, it will increase the intensity at some distance, which is certainly not the effect we want.
- The primary term multiplies the distance value, reducing the intensity in a linear way.
- The quadratic term multiplies the square of the distance, allowing the light source to reduce its intensity in a quadratic decreasing way. The influence of quadratic term is much smaller than that of primary term when the distance is small, but it will be larger than primary term when the distance value is large.
code implementation
#version 330 core out vec4 FragColor; struct Material { sampler2D diffuse; sampler2D specular; float shininess; }; struct Light { vec3 position; vec3 ambient; vec3 diffuse; vec3 specular; float constant; float linear; float quadratic; }; in vec3 FragPos; in vec3 Normal; in vec2 TexCoords; uniform vec3 viewPos; uniform Material material; uniform Light light; void main() { // ambient vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb; // diffuse vec3 norm = normalize(Normal); vec3 lightDir = normalize(light.position - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb; // specular vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb; // attenuation float distance = length(light.position - FragPos); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); ambient *= attenuation; diffuse *= attenuation; specular *= attenuation; vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); }
spotlight
The spotlight can be understood as a flashlight. The light source is circular in shape and attenuates around
- LightDir: vector from clip to light.
- SpotDir: the direction the spotlight points in.
- Phi ϕ: Specifies the tangent angle of the hotspot radius. Objects falling outside this angle will not be illuminated by this spotlight.
- Theta theta: the angle between the LightDir vector and the SpotDir vector. In the spotlight, theta should be smaller than ϕ.
Implementation code
#version 330 core out vec4 FragColor; struct Material { sampler2D diffuse; sampler2D specular; float shininess; }; struct Light { vec3 position; vec3 direction; float cutOff; float outerCutOff; vec3 ambient; vec3 diffuse; vec3 specular; float constant; float linear; float quadratic; }; in vec3 FragPos; in vec3 Normal; in vec2 TexCoords; uniform vec3 viewPos; uniform Material material; uniform Light light; void main() { vec3 lightDir = normalize(light.position - FragPos); // check if lighting is inside the spotlight cone float theta = dot(lightDir, normalize(-light.direction)); if(theta > light.cutOff) // remember that we're working with angles as cosines instead of degrees so a '>' is used. { // ambient vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb; // diffuse vec3 norm = normalize(Normal); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb; // specular vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb; // attenuation float distance = length(light.position - FragPos); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); // ambient *= attenuation; // remove attenuation from ambient, as otherwise at large distances the light would be darker inside than outside the spotlight due the ambient term in the else branche diffuse *= attenuation; specular *= attenuation; vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); } else { // else, use ambient light so scene isn't completely dark outside the spotlight. FragColor = vec4(light.ambient * texture(material.diffuse, TexCoords).rgb, 1.0); } }
With gradient edge
#version 330 core out vec4 FragColor; struct Material { sampler2D diffuse; sampler2D specular; float shininess; }; struct Light { vec3 position; vec3 direction; float cutOff; float outerCutOff; vec3 ambient; vec3 diffuse; vec3 specular; float constant; float linear; float quadratic; }; in vec3 FragPos; in vec3 Normal; in vec2 TexCoords; uniform vec3 viewPos; uniform Material material; uniform Light light; void main() { // ambient vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb; // diffuse vec3 norm = normalize(Normal); vec3 lightDir = normalize(light.position - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb; // specular vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb; // spotlight (soft edges) float theta = dot(lightDir, normalize(-light.direction)); float epsilon = (light.cutOff - light.outerCutOff); float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); diffuse *= intensity; specular *= intensity; // attenuation float distance = length(light.position - FragPos); float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); ambient *= attenuation; diffuse *= attenuation; specular *= attenuation; vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); }