Read "Computer Graphics Programming (Using OpenGL and C++)" 12 - Lighting

The most common lighting models nowadays are called "ADS" models because they are based on 3 types of reflections labeled A, D and S.

Ambient reflection simulates low-level lighting and affects all  objects in the scene.

Diffuse reflection adjusts the  brightness of the object according to the incident angle of the light.

Specular reflection is  used to show the luster of an object. It is achieved by strategically placing appropriately sized highlights on the surface of the object where the light is most directly reflected to our eyes .

ADS models can be used to simulate different lighting effects and various materials.

The drawing of the scene is ultimately accomplished by a fragment shader that outputs a color for each pixel on the screen. Using the ADS lighting model requires specifying the components due to lighting on a pixel's RGBA output value. Factors include:

●Light source types and their environment, diffuse reflection and specular reflection characteristics;

●Environmental, diffuse and specular reflection characteristics of the object material;

●The material of the object is specified as "gloss";

●The angle at which the light hits the object;

●The angle from which the scene is viewed.

There are many types of light sources , each with different characteristics and requiring different steps to simulate their effects. Common light source types are:

Global ambient light is the simplest light source model. There is no light position - no matter where the object is in the scene, every pixel on it has the same lighting. Has only an ambient reflection component, set with RGBA values; it has no diffuse or specular components. For example, global ambient light can be defined as follows:

float globalAmbient[4] = { 0.7f, 0.7f, 0.7f, 1.0f };

The value range of RGBA is 0 to 1. The global ambient light is usually modeled as dark white light, in which each RGB value is set to the same decimal number from 0 to 1, and alpha is set to 1.

A directional or distant light also does not have a source position, but it does have a direction. It can be used to simulate situations where the light source is so far away that the rays are nearly parallel, such as sunlight. Modeling a directional light requires specifying its direction (in vector form) and its ambient, diffuse and specular characteristics (in RGBA values). A red directional light pointing in the negative Z direction can be specified as follows:

float lightAmbient[4] = { 0.1f, 0.0f, 0.0f, 1.0f };
float lightDiffuse[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float lightSpecular[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float lightSpecular[3] = { 0.0f, 0.0f, -1.0f};

Positional lights have specific positions in a 3D scene. A light source close to the scene, such as a table lamp, candle, etc. Positional lights can also contain attenuation factors to simulate how their intensity decreases with distance. Like other types of lights we've seen, positional lights have ambient, diffuse, and specular properties specified as RGBA values. The red position light at position (5,2,−3) can be specified as follows:

float lightAmbient[4] = { 0.1f, 0.0f, 0.0f, 1.0f };
float lightDiffuse[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float lightSpecular[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float lightSpecular[3] = { 5.0f, 2.0f, -3.0f};

There are several ways to model the attenuation factor. One way is to use constant, linear and quadratic (referred to as kc, kl and kq respectively) decay and introduce non-negative adjustable parameters. These parameters are calculated in conjunction with the distance from the light source (d):

Multiplying this factor with the intensity of the light causes the intensity of the light to decay more the farther away you are from the light. Note that kc should always be set to a value greater than or equal to 1, so that the attenuation factor falls into the [0...1] interval and approaches 0 as d increases.

A spotlight has both a position and a direction. The "tapering" effect can be simulated using a cutoff angle θ of 0° to 90°, specifying the half-width of the beam, and using an attenuation index to simulate the intensity change with the beam angle. As shown in the figure, we determine the angle φ between the spotlight direction and the vector from the spotlight to the pixel. When φ is smaller than θ, we calculate the intensity factor by raising the cosine of φ to the decay exponent (when φ is larger than θ, the intensity factor is set to 0). The result is that the intensity factor ranges from 0 to 1. The decay exponent affects the rate at which the intensity factor approaches 0 as the angle φ increases. The intensity factor is then multiplied by the intensity of the light to simulate a cone effect.

Materials can be simulated in the ADS lighting model by specifying 4 values ​​(3 of which we are already familiar with - ambient, diffuse and specular RGB colors) . The fourth is called gloss , which is used to create an appropriate specular highlight for the selected material. 

Transparency is achieved by the opacity of the fourth (alpha) channel in the RGBA standard. A value of 1.0 means completely opaque, and a value of 0 means completely transparent.

Predefining some optional materials will be very convenient when using them. Therefore we need to add the following code to the Utils.cpp file:

 1 // 黄金材质 — 环境光、漫反射、镜面反射和光泽
 2 float * Utils::goldAmbient()
 3 {
 4     static float a[4] = { 0.2473f, 0.1995f, 0.0745f, 1 };
 5     return (float*)a;
 6 }
 7 
 8 float * Utils::goldDiffuse()
 9 {
10     static float a[4] = { 0.7516f, 0.6065f, 0.2265f, 1 };
11     return (float*)a;
12 }
13 
14 float * Utils::goldSpecular()
15 {
16     static float a[4] = { 0.6283f, 0.5559f, 0.3661f, 1 };
17     return (float*)a;
18 }
19 
20 float Utils::goldShininess()
21 {
22     return 51.2f;
23 }
24 // 白银材质 — 环境光、漫反射、镜面反射和光泽
25 float * Utils::silverAmbient()
26 {
27     static float a[4] = { 0.1923f, 0.1923f, 0.1923f, 1 };
28     return (float*)a;
29 }
30 
31 float * Utils::silverDiffuse()
32 {
33     static float a[4] = { 0.5075f, 0.5075f, 0.5075f, 1 };
34     return (float*)a;
35 }
36 
37 float * Utils::silverSpecular()
38 {
39     static float a[4] = { 0.5083f, 0.5083f, 0.5083f, 1 };
40     return (float*)a;
41 }
42 
43 float Utils::silverShininess()
44 {
45     return 51.2f;
46 }
47 // 青铜材质 — 环境光、漫反射、镜面反射和光泽
48 float * Utils::bronzeAmbient()
49 {
50     static float a[4] = { 0.2125f, 0.1275f, 0.0540f, 1 };
51     return (float*)a;
52 }
53 
54 float * Utils::bronzeDiffuse()
55 {
56     static float a[4] = { 0.7140f, 0.4284f, 0.1814f, 1 };
57     return (float*)a;
58 }
59 
60 float * Utils::bronzeSpecular()
61 {
62     static float a[4] = { 0.3936f, 0.2719f, 0.1667f, 1 };
63     return (float*)a;
64 }
65 
66 float Utils::bronzeShininess()
67 {
68     return 25.6f;
69 }

The basic ADS lighting calculation  we need to do is to determine the reflection intensity (Reflection Intensity, I) of each pixel. We need to calculate the ambient reflection, diffuse reflection and specular reflection components of each light source for each pixel and sum them. The calculation process is as follows:

Iobserved = Iambient + Idiffuse + Ispecular

These calculations are based on the type of light source in the scene and the type of material of the model being rendered.

The ambient light component is the simplest. Its value is the product of the scene ambient light and the material ambient light components:

Iambient = Lightambient * Materialambient

Light and material brightness are both RGB values, and the calculation can be more accurately described as:

  Iredambient = Lightredambient * Materialredambient

  Igreenambient = Lightgreenambient * Materialgreenambient

  Iblueambient = Lightblueambient * Materialblueambient

The same applies below.

The diffuse reflection component is based on the angle of incidence of light with respect to the plane. Lambert's law of cosines (published in 1760) determines that the amount of light reflected from a surface is proportional to the cosine of the angle of incidence of the light. It can be modeled as the following formula:

Idiffuse = Lightdiffuse * Materialdiffuse * cos(θ)

 Determining the angle of incidence θ requires

(a) Solve for the vector from the drawn vector to the light source (or the vector opposite to the direction of the light)

(b) Solve for the normal (vertical) vector of the surface of the rendered object. Let's call them L and N respectively

 

 In the 5 math section, cos(θ) can be calculated by dot product. The diffuse component only comes into play when the surface is exposed to light, i.e. when −90 < θ < 90, cos(θ) > 0.

Idiffuse = Lightdiffuse * Materialdiffuse * 

 The specular component determines whether the rendered pixel needs to be brightened as part of a "specular highlight". It is not only related to the incident angle of the light source, but also to the reflection angle of the light on the surface and the angle between the observation point and the reflective surface.

R represents the direction of light reflection, and V (called the view vector) is the vector from the pixel to the eye. Note that V is the inverse of the vector from the eye to the pixel (in camera space, the eye is at the origin). The smaller the small angle φ between R and V, the closer the eye is to the optical axis, or looking toward the reflected light, so the specular highlight component of the pixel is larger (the pixel should appear brighter).

 The way φ is used to calculate the specular component depends on the "glossiness" of the object being rendered. Extremely shiny objects, such as mirrors, have very small specular highlights - they reflect incoming light directly back to the eye. A less shiny object will have its specular highlight spread out, so the highlight will contain more pixels.

Reflectivity is usually modeled by an attenuation function that expresses how quickly the specular component decreases to 0 as the angle φ increases. We can use cos(φ) to model attenuation and increase or decrease reflectivity by raising the power of the cosine function, such as cos(φ), cos2(φ), cos3(φ), cos10(φ), cos50(φ )wait,

Note that the higher the order in the exponent, the faster the attenuation, and therefore the smaller the specular component of reflective pixels outside the viewing optical axis. We call the exponent n in the attenuation function cosn(φ) the reflectivity factor of the material. Now we can give the complete specular reflection calculation: 

 As before to calculate diffuse reflection, we use the max() function. In this case, we need to make sure that the specular component does not use negative values ​​for cos(φ), which would cause strange artifacts such as "dark" specular highlights.

Gouraud shading (bilinear light intensity interpolation method)

The Gouraud shading process is as follows.

(1) Determine the color of each vertex and lighting related calculations.

(2) Allows the normal rasterization process to also interpolate color (and also interpolate lighting) when inserting pixels.

1 ...
  2 #include "Torus.h"
  3 #include "Utils.h"
  4 ...
  5 // 给display()用
  6 GLuint mvLoc, projLoc, nLoc;
  7 int width, height;
  8 float aspect;
  9 glm::mat4 pMat, vMat, mMat, mvMat, invTrMat;
 10 glm::mat4 tMat, rMat;
 11 GLuint vLoc, tfLoc, mLoc;
 12 stack<glm::mat4> mvStack;
 13 // 着色器统一变量中的位置
 14 GLuint globalAmbLoc, ambLoc, diffLoc, specLoc, posLoc, mAmbLoc, mDiffLoc, mSpecLoc, mShiLoc;
 15 glm::vec3 currentLightPos, lightPosV; //在模型和视觉空间中的光照位置, Vector3f类型
 16 float lightPos[3]; // 光照位置的浮点数组
 17 // 初始化光照位置
 18 glm::vec3 initialLightLoc = glm::vec3(5.0f, 2.0f, 2.0f);
 19 // 白光特性
 20 float globalAmbient[4] = { 0.7f, 0.7f, 0.7f, 1.0f };
 21 float lightAmbient[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
 22 float lightDiffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
 23 float lightSpecular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
 24 // 黄金材质特性
 25 float* matAmb = Utils::goldAmbient();
 26 float* matDif = Utils::goldDiffuse();
 27 float* matSpe = Utils::goldSpecular();
 28 float matShi = Utils::goldShininess();
 29 Torus myTorus(0.5f, 0.2f, 48);
 30 ...
 31 void installLights(glm::mat4 vMatrix)
 32 {
 33     //将光源位置转换为视图空间坐标,并存入浮点数组
 34     lightPosV = glm::vec3(vMatrix * glm::vec4(currentLightPos, 1.0));
 35     lightPos[0] = lightPosV.x;
 36     lightPos[1] = lightPosV.y;
 37     lightPos[2] = lightPosV.z;
 38     //在着色器中获取光源位置和材质属性
 39     globalAmbLoc = glGetUniformLocation(renderingProgram, "globalAmbient");
 40     ambLoc = glGetUniformLocation(renderingProgram, "light.ambient");
 41     diffLoc = glGetUniformLocation(renderingProgram, "light.diffuse");
 42     specLoc = glGetUniformLocation(renderingProgram, "light.specular");
 43     posLoc = glGetUniformLocation(renderingProgram, "light.position");
 44     mAmbLoc = glGetUniformLocation(renderingProgram, "material.ambient");
 45     mDiffLoc = glGetUniformLocation(renderingProgram, "material.diffuse");
 46     mSpecLoc = glGetUniformLocation(renderingProgram, "material.specular");
 47     mShiLoc = glGetUniformLocation(renderingProgram, "material.shininess");
 48     //在着色器中为光源与材质统一变量赋值
 49     glProgramUniform4fv(renderingProgram, globalAmbLoc, 1, globalAmbient);
 50     glProgramUniform4fv(renderingProgram, ambLoc, 1, lightAmbient);
 51     glProgramUniform4fv(renderingProgram, diffLoc, 1, lightDiffuse);
 52     glProgramUniform4fv(renderingProgram, specLoc, 1, lightSpecular);
 53     glProgramUniform3fv(renderingProgram, posLoc, 1, lightPos);
 54     glProgramUniform4fv(renderingProgram, mAmbLoc, 1, matAmb);
 55     glProgramUniform4fv(renderingProgram, mDiffLoc, 1, matDif);
 56     glProgramUniform4fv(renderingProgram, mSpecLoc, 1, matSpe);
 57     glProgramUniform1f(renderingProgram, mShiLoc, matShi);
 58 }
 59 
 60 void setupVertices(void)
 61 {    
 62     //顶点同前面环形
 63 }
 64 ...
 65 void display(GLFWwindow* window, double currentTime)
 66 {
 67     ...
 68     // 用于模型-视图变换、投影以及逆转置(法向量)矩阵的统一变量
 69     mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
 70     projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
 71     nLoc = glGetUniformLocation(renderingProgram, "norm_matrix");    
 72     // 模型-视图变换同前面旋转的太阳
 73     ...
 74     // 基于当前光源位置,初始化光照
 75     currentLightPos = glm::vec3(initialLightLoc.x, initialLightLoc.y, initialLightLoc.z);
 76     installLights(vMat);
 77 
 78     // 构建MV矩阵的逆转置矩阵,以变换法向量
 79     invTrMat = glm::transpose(glm::inverse(mvStack.top()));//mvMat
 80 
 81     // 太阳旋转
 82     glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvStack.top())); //mvMat
 83     glUniformMatrix4fv(nLoc, 1, GL_FALSE, glm::value_ptr(invTrMat));
 84 
 85     glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
 86     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
 87     glEnableVertexAttribArray(0);
 88 
 89     glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
 90     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
 91     glEnableVertexAttribArray(1);
 92 
 93     glEnable(GL_CULL_FACE); // 要求识别并“剔除”背向的三角形
 94     glFrontFace(GL_CCW);
 95     glEnable(GL_DEPTH_TEST);
 96     glDepthFunc(GL_LEQUAL);
 97 
 98     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
 99     glDrawElements(GL_TRIANGLES, myTorus.getNumIndices(), GL_UNSIGNED_INT, 0);
100 }

vertex shader

 1 #version 460
 2 layout (location = 0) in vec3 vertPos;
 3 layout (location = 1) in vec3 vertNormal;
 4 out vec4 varyingColor;
 5 
 6 struct PositionalLight
 7 {
 8 vec4 ambient;
 9 vec4 diffuse;
10 vec4 specular;
11 vec3 position;
12 };
13 
14 struct Material
15 {
16 vec4 ambient;
17 vec4 diffuse;
18 vec4 specular;
19 float shininess;
20 };
21 
22 uniform mat4 norm_matrix;  // 用来变换法向量
23 uniform mat4 mv_matrix;
24 uniform mat4 proj_matrix;
25 uniform Material material; 
26 uniform vec4 globalAmbient;
27 uniform PositionalLight light;
28 
29 void main(void)
30 { 
31     vec4 color;
32     // 将顶点位置转换到视觉空间
33     // 将法向量转换到视觉空间
34     // 计算视觉空间光照向量(从顶点到光源)
35     vec4 P = mv_matrix * vec4(vertPos, 1.0);
36     vec3 N = normalize((norm_matrix * vec4(vertNormal, 1.0)).xyz);
37     vec3 L = normalize(light.position - P.xyz);
38     // 视觉向量等于视觉空间中的负顶点位置
39     vec3 V = normalize(-P.xyz);
40     // R是-L的相对于表面向量N的镜像
41     vec3 R = reflect(-L, N);
42     // 环境光、漫反射和镜面反射分量
43     vec3 ambient = ((globalAmbient * material.ambient) + (light.ambient * material.ambient)).xyz;
44     vec3 diffuse = light.diffuse.xyz * material.diffuse.xyz * max(dot(N, L), 0.0);
45     vec3 specular = material.specular.xyz * light.specular.xyz * pow(max(dot(R, V), 0.0f), material.shininess);
46     // 将颜色输出发送到片段着色器
47     varyingColor = vec4((ambient + diffuse + specular), 1.0);
48     // 将位置发送到片段着色器,如前
49     gl_Position = proj_matrix * mv_matrix * vec4(vertPos, 1.0);
50 }

fragment shader

 1 #version 460
 2 out vec4 fragColor;
 3 in vec4 varyingColor;
 4 
 5 // 与顶点着色器相同的统一变量
 6 // 但并不直接在当前片段着色器使用
 7 
 8 struct PositionalLight
 9 {
10 vec4 ambient;
11 vec4 diffuse;
12 vec4 specular;
13 vec3 position;
14 };
15 
16 struct Material
17 {
18 vec4 ambient;
19 vec4 diffuse;
20 vec4 specular;
21 float shininess;
22 };
23 
24 uniform mat4 norm_matrix;  // 用来变换法向量
25 uniform mat4 mv_matrix;
26 uniform mat4 proj_matrix;
27 uniform Material material; 
28 uniform vec4 globalAmbient;
29 uniform PositionalLight light;
30 
31 void main(void)
32 { 
33 //color = vec4(1.0, 0.0, 0.0, 1.0);
34 fragColor = varyingColor;
35 }

The effect is as follows:

 installLights() reads the position of the light source in the visual space and the ADS characteristics of the material into the corresponding unified variables for use by the shader.

One of the important details is the transformation matrix MV, which is used to move the vertex position to the visual space. It is the inverse transposition matrix of MV. This new matrix is ​​called "invTrMat" and is passed to the shader through a unified variable.

The vertex shader code contains our first example of using struct syntax. A GLSL "structure" is like a data type, it has a name and a set of fields. Pay attention to the field selector notation ".xyz", this is a shortcut to convert a vec4 to an equivalent vec3 containing only its first 3 elements.

The vast majority of lighting calculations occur in the vertex shader. For each vertex, apply the appropriate matrix transformation to the vertex position and associated normal vector, and compute the vectors for light direction (L) and reflection (R). Then perform an ADS calculation to get the color of each vertex (named varyingColor in the code). Colors are interpolated as part of the normal rasterization process. The fragment shader is then simply passed through. The lengthy list of uniform variable declarations is also in the fragment shader, but they are not actually used there.

GLSL function normalize(), which is used to convert vectors to unit length. This function must be used to perform dot product operations correctly.

The reflect() function calculates the reflection of one vector based on another vector.

There are obvious artifacts in the output torus. Its specular highlights have a blocky, patchy feel. This artifact is more noticeable when the object is moving.

Gouraud shading is also susceptible to other artifacts. If the entire range of the specular highlight is within a triangle in the model - that is, there is not a single model vertex within the highlight range - then it may not be rendered. Since the specular reflection component is calculated based on vertices, when all vertices of the model do not have specular reflection components, the rasterized pixels will not have specular reflection light.

Phong coloring

Bui Tuong Phong developed a smooth shading algorithm while a graduate student at the University of Utah. The structure of this algorithm is similar to that of Gouraud shading, except that lighting calculations are done on a pixel-by-pixel basis rather than a vertex-by-vertex basis.

The process that was previously done partially in the vertex shader is now done back in the fragment shader. The effect of normal vector interpolation is shown in the figure:

Since the C++/OpenGL code has not changed at all, here we only show the modified vertex shader and fragment shader. 

vertex shader

#version 460
layout (location = 0) in vec3 vertPos;
layout (location = 1) in vec3 vertNormal;
out vec3 varyingNormal; // 视觉空间顶点法向量
out vec3 varyingLightDir; // 指向光源的向量
out vec3 varyingVertPos; // 视觉空间中的顶点位置

struct PositionalLight
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec3 position;
};

struct Material
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
};

uniform mat4 norm_matrix;  // 用来变换法向量
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform Material material; 
uniform vec4 globalAmbient;
uniform PositionalLight light;

void main(void)
{ 
    //输出顶点位置、光照方向和法向量到光栅器以进行插值
    varyingVertPos = (mv_matrix * vec4(vertPos, 1.0)).xyz;
    varyingLightDir = light.position - varyingVertPos; 
    varyingNormal = (norm_matrix * vec4(vertNormal, 1.0)).xyz; 
    
    gl_Position = proj_matrix * mv_matrix * vec4(vertPos, 1.0);
}

fragment shader

#version 460
out vec4 fragColor;
in vec3 varyingNormal; 
in vec3 varyingLightDir;
in vec3 varyingVertPos;

struct PositionalLight
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec3 position;
};

struct Material
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
};

uniform mat4 norm_matrix;  // 用来变换法向量
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform Material material; 
uniform vec4 globalAmbient;
uniform PositionalLight light;

void main(void)
{ 
    // 正规化光照向量、法向量、视觉向量
    vec3 L = normalize(varyingLightDir);
    vec3 N = normalize(varyingNormal);
    vec3 V = normalize(-varyingVertPos);
    // 计算光照向量基于N的反射向量
    vec3 R = normalize(reflect(-L, N));
    // 计算光照与平面法向量间的角度
    float cosTheta = dot(L, N);
    // 计算视觉向量与反射光向量的角度
    float cosPhi = dot(V, R);
    // 计算ADS分量(按像素),并合并以构建输出颜色
    vec3 ambient = ((globalAmbient * material.ambient) + (light.ambient * material.ambient)).xyz;
    vec3 diffuse = light.diffuse.xyz * material.diffuse.xyz * max(cosTheta, 0.0);
    vec3 specular = light.specular.xyz * material.specular.xyz * pow(max(cosPhi, 0.0), material.shininess);
    fragColor = vec4((ambient + diffuse + specular), 1.0);
}

The effect is as follows

Although Phong shading has a more realistic effect than Gouraud shading, it is based on increased performance consumption. James Blinn proposed an optimization method for Phong shading in 1977, called the Blinn-Phong reflection model. This optimization is based on the observation that one of the most expensive calculations in Phong shading is solving for the reflection vector R.

Blinn discovered that the vector R is not necessary in the calculation - R is just a means of calculating the angle φ. The angle φ can be calculated without using the vector R, but through the angular bisector vector H of L and V. As shown in the figure, the angle α between H and N is exactly equal to 1⁄2(φ).

 The angle bisector vector can be obtained simply using L+V, and then cos(α) can be obtained by

Dot product calculation.

 These calculations can be done in the fragment shader or even in the vertex shader for performance reasons (with some tweaking).

 Likewise the C++/OpenGL code has not changed at all.

vertex shader

...
// 角平分线向量H作为新增的输出
out vec3 varyingHalfVector;
 ...
void main(void)
{ 
    // 与之前的计算相同,增加了 L+V 的计算
    varyingHalfVector = (varyingLightDir + (-varyingVertPos)).xyz;
    // 其余代码没改动
}

fragment shader

1 ...
 2 in vec3 varyingHalfVector;
 3 ...
 4 void main(void)
 5 { 
 6     // 现在不需要计算R
 7     vec3 L = normalize(varyingLightDir);
 8     vec3 N = normalize(varyingNormal);
 9     vec3 V = normalize(-varyingVertPos);
10     vec3 H = normalize(varyingHalfVector);    
11     // 计算光照与平面法向量间的角度
12     float cosTheta = dot(L, N);
13     // 计算法向量N与角平分线向量H之间的角度
14     float cosPhi = dot(H, N);
15     // 角平分线向量H已经在顶点着色器中计算过,并在光栅器中进行过插值
16     vec3 ambient = ((globalAmbient * material.ambient) + (light.ambient * material.ambient)).xyz;
17     vec3 diffuse = light.diffuse.xyz * material.diffuse.xyz * max(cosTheta, 0.0);
18     vec3 specular = light.specular.xyz * material.specular.xyz * pow(max(cosPhi, 0.0), material.shininess*3.0);
19     // 最后乘以3.0作为改善镜面高光的微调
20     fragColor = vec4((ambient + diffuse + specular), 1.0);
21 }

The effect is as shown in the figure:

If you’re new to lighting, you can’t really tell the difference. Let me put the three effects together and look at them:

 Eyes dazzled

Guess you like

Origin blog.csdn.net/ttod/article/details/135346606