当前位置:网站首页>OpenGL - Lighting

OpenGL - Lighting

2022-07-05 09:18:00 Farmer er

We see in real life that the color of an object is not the color that the object really has , But what it reflects (Reflected) Color . let me put it another way , Those that cannot be absorbed by objects (Absorb) The color of is the color of the object we can perceive .

The simple understanding is RGB Multiplication of components :

glm::vec3 lightColor(0.33f, 0.42f, 0.18f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);

The effect can refer to ->

To simulate a more realistic lighting effect , according to Feng's illumination model (Phong Lighting Model) It is divided into The ambient light (Ambient Lighting) Diffuse reflection (Diffuse Lighting) and Specular reflection (Specular Lighting).
 Insert picture description here

Ambient lighting

One property of light is , It can diverge in many directions and bounce , So that we can reach points that are not very directly adjacent . therefore , Light can be reflected on other surfaces , Have an indirect effect on an object . We use a small constant ( light ) Color , Add to the final color of the object segment , In this way, even if there is no direct light source in the scene, it can appear that there is some divergent light .

// fragment shader
void main()
{
    
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
} 

Diffuse lighting

For calculating diffuse reflection , We need to know :

  • The normal vector (Normal vector): A vector perpendicular to the vertex surface .
  • Directional light (The directed light ray): The direction vector as the vector difference between the position of the light source and the position of the clip .

 Insert picture description here
For specific calculation examples, please refer to ->

The normal vector acts in the world coordinate system , In order to solve the problem that the normal vector is not perpendicular to the surface caused by the non proportional scaling of the object , Can pass Normal matrix (Normal Matrix) Correct this behavior :

Normal = mat3(transpose(inverse(model))) * aNormal;  

Specular Lighting

 Insert picture description here

Just like diffuse lighting , Specular illumination also depends on the direction vector of light and the normal vector of the object , But it also depends on the direction of observation , For example, in what direction does the player look at this clip . We calculate the reflection vector by inverting the direction of the incident light according to the normal vector . Then we calculate the angle difference between the reflection vector and the viewing direction , The smaller the angle between them , The greater the effect of mirror light . The result is , When we look at the reflection direction of the incident light on the surface , You'll see a little highlights .

To calculate the reflection vector, you can use reflect( The light source points to the vector of the clip , The normal vector ), To calculate the mirror component, you can use :

// This 32 value is the shininess value of the highlight.
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;

 Insert picture description here

Integrate three lighting effects to superimpose , The final code can refer to ->

Materials

In the real world, different objects behave differently under the same light source , The reason is that the materials are different :

#version 330 core
struct Material {
    
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
}; 

For light sources , The influence proportions of the three attributes on objects are also different :

struct Light {
    
    vec3 position;
  
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

Combine the two structures , The actual color of the object is calculated as follows :

in vec3 FragPos;  
in vec3 Normal;  
  
uniform vec3 viewPos;
uniform Material material;
uniform Light light;

void main()
{
    
    // ambient
    vec3 ambient = light.ambient * material.ambient;
  	
    // diffuse 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * (diff * material.diffuse);
    
    // 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 * material.specular);  
        
    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
} 

Lighting maps

In the last section, the color of diffuse and specular reflection in the material is the same for the whole object , If you apply texture to reflection , It can be called diffuse map and specular map . The ambient color is equal to the diffuse color in almost all cases , So we don't need ambient light mapping .

For specific examples, please refer to ->

Light casters

In the real world , We have many kinds of light , Each performance is different . Project light (Cast) The light source to the object is called Projector (Light Caster).

Directional Light

When a light source is far away , Each ray from the light source will be approximately parallel to each other , Only the direction of the light source is needed to simulate the directional light .

struct Light {
    
    // vec3 position; //  Using directional light is no longer needed 
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
...
void main()
{
    
  vec3 lightDir = normalize(-light.direction);
  ...
}

Point lights

A point light is a light at a certain position in the world , It will shine in all directions , But as the distance of light propagation increases , Will gradually reduce the intensity of light , be called attenuation (Attenuation), Rules are as follows :
 Insert picture description here

Due to the existence of quadratic terms , Light will decay in a linear manner most of the time , Until the distance becomes large enough , Let the second term exceed the first term , The intensity of light decreases at a faster rate . This is the result , Light is very bright at close range , But as the distance increases, the brightness decreases rapidly , Finally, the brightness will be reduced at a slower speed . The picture below shows the following in 100 The effect of attenuation within a distance :
 Insert picture description here
As for how to choose these constant terms , You can refer to ->

The code implementation is as follows :

#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

A spotlight is a light source located somewhere in the environment , It shines light in only one direction, not all directions . The result is that only objects within a specific radius in the direction of the spotlight will be illuminated , Other objects will remain dark . A good example of spotlight is a street lamp or flashlight .
 Insert picture description here
For each clip , We calculate whether the clip is between the tangent directions of the spotlight ( That is, in the cone ), Computation LightDir Vector and SpotDir Dot product between vectors , And connect it with Cutting angle ϕ Value comparison , If it meets the requirements , We will illuminate the fragment accordingly .

To create a spotlight that looks smooth at the edges , We need to simulate a spotlight Inner cone (Inner Cone) And a Outer cone (Outer Cone). We can set the inner cone to the one in the previous section , But we also need an outer cone , To darken the light from the inner cone , To the boundary of the outer cone .

To create an outer cone , We only need to define a cosine value to represent the focusing direction vector and the outer cone vector ( Equal to its radius ) The angle between . then , If a clip is between the inner and outer cones , I'll give it a 0.0 To 1.0 Strength value between . If the fragment is within the inner cone , Its strength is 1.0, If outside the outer cone, the strength value is 0.0.

 Insert picture description here

The code implementation is as follows :

#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);
} 

Combine the above three light sources , The effect can refer to ->

原网站

版权声明
本文为[Farmer er]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207050912153424.html