参考
介绍
现实世界中的光照是极其复杂,难以计算的,因此OpenGL的光照使用的是简化的模型,其中一个模型被称为冯氏光照模型(Phong Lighting Model)。
冯氏光照模型的主要结构由三个分量组成:
环境(Ambient)光照
漫反射(Diffuse)光照
镜面(Specular)光照
环境光照
光的一大特点是,它可以向很多方向发散并反弹,所以现实环境周围通常总会有些光亮,对应的物体通常都会反射些微弱的光。
计算:
用一个很小的分量乘以光的颜色,最后乘以物体的颜色
漫反射光照
漫反射光照使物体与光线方向越接近的片段能充光源处获得更多的亮度。
如果光线垂直于物体表面,这束光对物体的影响会最大化(译注:更亮)。
可以大致理解为
片段的漫反射光照 = (光源坐标到片段坐标的向量与片段法向量的夹角) * 光源颜色
注意两个向量都要先归一化
为了计算漫反射光照我们需要知道这几个值:
片段所在面的法向量
光源的世界坐标
片段的世界坐标
最后目前片段的颜色为: (环境光照值+漫反射光照值) * 片段自身颜色
镜面光照
镜面光照决定于表面的反射特性。如果我们把物体表面设想为一面镜子,那么镜面光照最强的地方就是我们看到表面上反射光的地方。如下图:
为此我们还需要一个观察者坐标(即摄像机)。光的反射向量与观察方向之间夹角越小,镜面光照效果越强。
最终效果:
主要代码:
std::vector<float> sphereVertices;std::vector<int> sphereIndices;// 生成球的顶点for (int y = 0; y <= Y_SEGMENTS; y++){for (int x = 0; x <= X_SEGMENTS; x++){float xSegment = (float)x / (float)X_SEGMENTS;float ySegment = (float)y / (float)Y_SEGMENTS;float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI);float yPos = std::cos(ySegment * PI);float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI);sphereVertices.push_back(xPos);sphereVertices.push_back(yPos);sphereVertices.push_back(zPos);}}// 生成球的Indicesfor (int i = 0; i < Y_SEGMENTS; i++){for (int j = 0; j < X_SEGMENTS; j++){sphereIndices.push_back(i * (X_SEGMENTS + 1) + j);sphereIndices.push_back((i + 1) * (X_SEGMENTS + 1) + j);sphereIndices.push_back((i + 1) * (X_SEGMENTS + 1) + j + 1);sphereIndices.push_back(i * (X_SEGMENTS + 1) + j);sphereIndices.push_back((i + 1) * (X_SEGMENTS + 1) + j + 1);sphereIndices.push_back(i * (X_SEGMENTS + 1) + j + 1);}}float cube[] = {-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f};//sphere---lightglBindVertexArray(VAO[2]);glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);glBufferData(GL_ARRAY_BUFFER, sphereVertices.size() * sizeof(float), &sphereVertices[0], GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[2]);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sphereIndices.size() * sizeof(int), &sphereIndices[0], GL_STATIC_DRAW);//cubeglBindVertexArray(VAO[3]);glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);glBufferData(GL_ARRAY_BUFFER, sizeof(cube), cube, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);
每次渲染时都绑定信息:
//让光源绕原点旋转float distance = 2.0f;float lightY = sin(glfwGetTime()) * distance;float lightZ = -(cos(glfwGetTime()) * distance);float lightX = sin(glfwGetTime()) * distance;//lightPos.x = lightX; lightPos.y = 1; lightPos.z = 1.2; //直线移动lightPos.x = lightX; lightPos.y = lightY; lightPos.z = lightZ; //环绕移动shader.use();model = glm::mat4(1.0f);shader.setMat4("model", model);shader.setMat4("projection", projection);shader.setMat4("view", view);shader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);shader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);shader.setVec3("lightPos", lightPos);shader.setVec3("viewPos", camera.position);glBindVertexArray(VAO[3]);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);light_shader.use();model = glm::mat4(1.0f);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.1f));light_shader.setMat4("model", model);light_shader.setMat4("projection", projection);light_shader.setMat4("view", view);glBindVertexArray(VAO[2]);glDrawElements(GL_TRIANGLES, X_SEGMENTS * Y_SEGMENTS * 6, GL_UNSIGNED_INT, 0);//glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);
顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;out vec3 FragPos;
out vec3 Normal;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{FragPos = vec3(model * vec4(aPos, 1.0));mat3 normalMatrix = mat3(transpose(inverse(model)));Normal = normalMatrix * aNormal; // 计算法向量经过模型变换后值gl_Position = projection * view * vec4(FragPos, 1.0);
}
片段着色器:
#version 330 core
out vec4 FragColor;in vec3 Normal;
in vec3 FragPos; uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform vec3 viewPos; void main()
{// ambientfloat ambientStrength = 0.1;vec3 ambient = ambientStrength * lightColor;// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = diff * lightColor;// specularfloat specularStrength = 0.5;vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);vec3 specular = specularStrength * spec * lightColor; //距离影响光照强度,暂时是我自己想的float enableDistance = 4.0f; //有效光照距离vec3 light2fragVec = lightPos - vec3(FragPos);float distan = min(sqrt(light2fragVec.x * light2fragVec.x + light2fragVec.y * light2fragVec.y + light2fragVec.z * light2fragVec.z), enableDistance);float strength = 1 - distan / enableDistance;//光照远近先不影响环境光照了vec3 result = ambient*objectColor+(diffuse + specular) * objectColor*strength;FragColor = vec4(result, 1.0);
}
光源的顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);
}
光源的片段着色器:
#version 330 core
out vec4 FragColor;void main()
{FragColor = vec4(1.0); // set all 4 vector values to 1.0
}