现代OpenGL学习-07

原文地址

环境光(固有色)

The ambient component of the Phong reflection model basically specifies a minimum brightness. Even if there is no light hitting a surface directly, the ambient component will light up the surface a little bit to stop it from being pure black. The ambient brightness is constant for all surfaces.

We will calculate the ambient component using a percentage of the original intensities of the light source. We will store this ambient percentage as a float with a value between zero (0%) and one (100%), in a variable named ambientCoefficient. For example if ambientCoefficient is 0.05 (5%) and the reflected light intensities are (1,0,0)

, which is pure red light, then the ambient component will be (0.05,0,0)

, which is very dim red light.

vec3 ambient = light.ambientCoefficient * surfaceColor.rgb * light.intensities;

The specular component is what makes a surface look shiny. The word "specular" means "like a mirror," and it is used here because the shiny patches (a.k.a. specular highlights) are fake reflections of light, like a mirror would reflect light.


现代OpenGL学习-07_第1张图片

R is new, and it represents the angle of reflection (AoR).


现代OpenGL学习-07_第2张图片

Notice how when the topcoat reflects light, the rays don't hit the paint layer. Normally the paint layer would change the color of the light by absorbing some of the intensities, but that can't happen if the light doesn't hit the paint. This means that the specular component is usually a different color to the diffuse component. Most specular surfaces don't absorb anything, they just reflect all of the light, which means the specular color would be white. This is why the shiny parts of a car are white, even though the paint is red.


现代OpenGL学习-07_第3张图片

To apply the specular exponent, we take cos(angle) and raise it to the power of the specular exponent. This produces the "specular coefficient", which is the brightness of the reflection.

vec3 incidenceVector = -surfaceToLight; //a unit vector

vec3 reflectionVector = reflect(incidenceVector, normal); //also a unit vector

vec3 surfaceToCamera = normalize(cameraPosition - surfacePosition); //also a unit vector

float cosAngle = max(0.0, dot(surfaceToCamera, reflectionVector));

float specularCoefficient = pow(cosAngle, materialShininess);

vec3 specularComponent = specularCoefficient * materialSpecularColor * light.intensities;

光线距离衰减

float attenuation = 1.0 / (1.0 + light.attenuation * pow(distanceToLight, 2));

The GLSL to combine the ambient, diffuse and specular components, including attenuation, looks like this:

vec3 linearColor = ambient + attenuation*(diffuse + specular);

This is almost the final color for the fragment/pixel. The last step is to do gamma correction.

之前的颜色计算是在线性颜色空间计算的

For example, the 100% red color (1,0,0)

should be twice as bright as the 50% red color (0.5,0,0)

.

但是显示器的颜色空间不是线性的;


现代OpenGL学习-07_第4张图片

The 100% red is actually about 4.5 times brighter than the 50% red, which makes the brightness in the 3D scene look wrong.

Gamma correction is pretty simple to implement. You take each of the RGB values and raise them to the power of gamma. Some games give the user a brightness setting which allows them to change the gamma value, but we will just use the constant value 1/2.2

in this article, which is the correct value for CRT monitors.

vec3 gamma = vec3(1.0/2.2);

finalColor = pow(linearColor, gamma);

vec3 gamma = vec3(1.0/2.2, 1.0/2.2, 1.0/2.2);

finalColor = vec3(pow(linearColor.r, gamma.r),

pow(linearColor.g, gamma.g),

pow(linearColor.b, gamma.b));

#version 150

uniform mat4 model;

uniform vec3 cameraPosition;

// material settings

uniform sampler2D materialTex;

uniform float materialShininess;

uniform vec3 materialSpecularColor;

uniform struct Light {

vec3 position;

vec3 intensities; //a.k.a the color of the light

float attenuation;

float ambientCoefficient;

} light;

in vec2 fragTexCoord;

in vec3 fragNormal;

in vec3 fragVert;

out vec4 finalColor;

void main() {

vec3 normal = normalize(transpose(inverse(mat3(model))) * fragNormal);

vec3 surfacePos = vec3(model * vec4(fragVert, 1));

vec4 surfaceColor = texture(materialTex, fragTexCoord);

vec3 surfaceToLight = normalize(light.position - surfacePos);

vec3 surfaceToCamera = normalize(cameraPosition - surfacePos);

//ambient

vec3 ambient = light.ambientCoefficient * surfaceColor.rgb * light.intensities;

//diffuse

float diffuseCoefficient = max(0.0, dot(normal, surfaceToLight));

vec3 diffuse = diffuseCoefficient * surfaceColor.rgb * light.intensities;

//specular

float specularCoefficient = 0.0;

if(diffuseCoefficient > 0.0)

specularCoefficient = pow(max(0.0, dot(surfaceToCamera, reflect(-surfaceToLight, normal))), materialShininess);

vec3 specular = specularCoefficient * materialSpecularColor * light.intensities;

//attenuation

float distanceToLight = length(light.position - surfacePos);

float attenuation = 1.0 / (1.0 + light.attenuation * pow(distanceToLight, 2));

//linear color (color before gamma correction)

vec3 linearColor = ambient + attenuation*(diffuse + specular);

//final color (after gamma correction)

vec3 gamma = vec3(1.0/2.2);

finalColor = vec4(pow(linearColor, gamma), surfaceColor.a);

}

There are new material uniforms:

uniform sampler2D materialTex;

uniform float materialShininess;

uniform vec3 materialSpecularColor;


struct ModelAsset {

tdogl::Program* shaders;

tdogl::Texture* texture;

GLuint vbo;

GLuint vao;

GLenum drawType;

GLint drawStart;

GLint drawCount;

GLfloat shininess; //new this article

glm::vec3 specularColor; //new this article

};

struct Light {

glm::vec3 position;

glm::vec3 intensities;

float attenuation; //new this article

float ambientCoefficient; //new this article

};

你可能感兴趣的:(现代OpenGL学习-07)