开篇的话:接着赶笔记,这篇写完正好回学校开始学习新的内容了。


投光物

将光投射(Cast)到物体的光源叫做投光物(Light Caster)。

平行光(Directional Light)

(我们通常用平行光来模拟太阳光线。)

定义一个光线方向向量而不是位置向量来模拟一个定向光。着色器的计算基本保持不变,但这次我们将直接使用光的direction向量而不是通过direction来计算lightDir向量。

struct Light {// vec3 position; // 使用定向光就不再需要了vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;
};
...
void main()
{vec3 lightDir = normalize(-light.direction);...
}

注意我们首先对light.direction向量取反。我们目前使用的光照计算需求一个从片段光源的光线方向,但人们更习惯定义定向光为一个光源出发的全局方向。所以我们需要对全局光照方向向量取反来改变它的方向,它现在是一个指向光源的方向向量了。

使用之前那十个箱子的场景:

for(unsigned int i = 0; i < 10; i++)
{glm::mat4 model;model = glm::translate(model, cubePositions[i]);float angle = 20.0f * i;model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));lightingShader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);
}

定义光源方向:(将方向定义为光源出发的方向)

lightingShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f);

如图:

点光源

点光源是处于世界中某一个位置的光源,它会朝着所有方向发光,但光线会随着距离逐渐衰减。

我们一直都在使用一个(简化的)点光源。我们在给定位置有一个光源,它会从它的光源位置开始朝着所有方向散射光线。然而,我们定义的光源模拟的是永远不会衰减的光线,这看起来像是光源亮度非常的强。在大部分的3D模拟中,我们都希望模拟的光源仅照亮光源附近的区域而不是整个场景。

衰减

记公式!

下面这个公式根据片段距光源的距离计算了衰减值,之后我们会将它乘以光的强度向量:

在这里dd代表了片段距光源的距离。接下来为了计算衰减值,我们定义3个(可配置的)项:常数项KcKc、一次项KlKl和二次项KqKq。

  • 常数项通常保持为1.0,它的主要作用是保证分母永远不会比1小,否则的话在某些距离上它反而会增加强度,这肯定不是我们想要的效果。
  • 一次项会与距离值相乘,以线性的方式减少强度。
  • 二次项会与距离的平方相乘,让光源以二次递减的方式减少强度。二次项在距离比较小的时候影响会比一次项小很多,但当距离值比较大的时候它就会比一次项更大了。

常数项KcKc在所有的情况下都是1.0。一次项KlKl为了覆盖更远的距离通常都很小,二次项KqKq甚至更小。

实现衰减

为了实现衰减,在片段着色器中我们还需要三个额外的值:也就是公式中的常数项、一次项和二次项。它们最好储存在之前定义的Light结构体中。

struct Light {vec3 position;  vec3 ambient;vec3 diffuse;vec3 specular;float constant;float linear;float quadratic;
};

然后我们将在OpenGL中设置这些项:我们希望光源能够覆盖50的距离,所以我们会使用表格(Ogre3D的Wiki)中对应的常数项、一次项和二次项:

lightingShader.setFloat("light.constant",  1.0f);
lightingShader.setFloat("light.linear",    0.09f);
lightingShader.setFloat("light.quadratic", 0.032f);

片段着色器中实现衰减还是比较直接的:我们根据公式计算衰减值,之后再分别乘以环境光、漫反射和镜面光分量。

我们仍需要公式中距光源的距离,还记得我们是怎么计算一个向量的长度的吗?我们可以通过获取片段和光源之间的向量差,并获取结果向量的长度作为距离项。我们可以使用GLSL内建的length函数来完成这一点:

float distance    = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));

(这么长一段其实没什么好解释的,就是先计算出公式里的d,然后再套公式就可以了。)

将它分别乘以环境光、漫反射和镜面光颜色。

我们可以将环境光分量保持不变,让环境光照不会随着距离减少,但是如果我们使用多于一个的光源,所有的环境光分量将会开始叠加,所以在这种情况下我们也希望衰减环境光照。简单实验一下,看看什么才能在你的环境中效果最好。

ambient  *= attenuation;
diffuse  *= attenuation;
specular *= attenuation;

运行:

只有前排的箱子被照亮的,距离最近的箱子是最亮的。后排的箱子一点都没有照亮,因为它们离光源实在是太远了

聚光灯

聚光是位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线。这样的结果就是只有在聚光方向的特定半径内的物体才会被照亮,其它的物体都会保持黑暗。聚光很好的例子就是路灯或手电筒。

OpenGL中聚光是用一个世界空间位置、一个方向和一个切光角(Cutoff Angle)来表示的,切光角指定了聚光的半径(译注:是圆锥的半径不是距光源距离那个半径)。对于每个片段,我们会计算片段是否位于聚光的切光方向之间(也就是在锥形内),如果是的话,我们就会相应地照亮片段。下面这张图会让你明白聚光是如何工作的:

  • LightDir:从片段指向光源的向量。
  • SpotDir:聚光所指向的方向。
  • Phiϕ:指定了聚光半径的切光角。落在这个角度之外的物体都不会被这个聚光所照亮。
  • Thetaθ:LightDir向量和SpotDir向量之间的夹角。在聚光内部的话θ值应该比ϕ值小。

我们要做的就是计算LightDir向量和SpotDir向量之间的点积(它会返回两个单位向量夹角的余弦值),并将它与切光角ϕ值对比。

手电筒

手电筒(Flashlight)是一个位于观察者位置的聚光,通常它都会瞄准玩家视角的正前方。手电筒就是普通的聚光,但它的位置和方向会随着玩家的位置和朝向不断更新。

在片段着色器中我们需要的值有聚光的位置向量(来计算光的方向向量)、聚光的方向向量和一个切光角。我们可以将它们储存在Light结构体中:

struct Light {vec3  position;vec3  direction;float cutOff;...
};

我们将合适的值传到着色器中:

lightingShader.setVec3("light.position",  camera.Position);
lightingShader.setVec3("light.direction", camera.Front);
lightingShader.setFloat("light.cutOff",   glm::cos(glm::radians(12.5f)));

我们并没有给切光角设置一个角度值,反而是用角度值计算了一个余弦值,将余弦结果传递到片段着色器中。这样做的原因是在片段着色器中,我们会计算LightDirSpotDir向量的点积,这个点积返回的将是一个余弦值而不是角度值,所以我们不能直接使用角度值和余弦值进行比较。

接下来就是计算θθ值,并将它和切光角ϕ对比,来决定是否在聚光的内部:

float theta = dot(lightDir, normalize(-light.direction));if(theta > light.cutOff)
{       // 执行光照计算
}
else  // 否则,使用环境光,让场景在聚光之外时不至于完全黑暗color = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0);

我们首先计算了lightDir和取反的direction向量(取反的是因为我们想让向量指向光源而不是从光源出发)之间的点积。记住要对所有的相关向量标准化。

运行程序,你将会看到一个聚光,它仅会照亮聚光圆锥内的片段。看起来像是这样的:

平滑/软化边缘

为了创建一种看起来边缘平滑的聚光,我们需要模拟聚光有一个内圆锥(Inner Cone)和一个外圆锥(Outer Cone)。我们可以将内圆锥设置为上一部分中的那个圆锥,但我们也需要一个外圆锥,来让光从内圆锥逐渐减暗,直到外圆锥的边界。

为了创建一个外圆锥,我们只需要再定义一个余弦值来代表聚光方向向量和外圆锥向量(等于它的半径)的夹角。然后,如果一个片段处于内外圆锥之间,将会给它计算出一个0.0到1.0之间的强度值。如果片段在内圆锥之内,它的强度就是1.0,如果在外圆锥之外强度值就是0.0。

可以用下面这个公式来计算这个值:

这里ϵ(Epsilon)是内(ϕ)和外圆锥(γ)之间的余弦值差。最终的I值就是在当前片段聚光的强度。

我们现在有了一个在聚光外是负的,在内圆锥内大于1.0的,在边缘处于两者之间的强度值了。如果我们正确地约束(Clamp)这个值,在片段着色器中就不再需要if-else了,我们能够使用计算出来的强度值直接乘以光照分量:

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;
...

我们使用了clamp函数,它把第一个参数约束(Clamp)在了0.0到1.0之间。这保证强度值不会在[0, 1]区间之外。

确定你将outerCutOff值添加到了Light结构体之中,并在程序中设置它的uniform值。下面的图片中,我们使用的内切光角是12.5,外切光角是17.5:

struct Light {vec3 position;  vec3 direction;float cutOff;float outerCutOff;vec3 ambient;vec3 diffuse;vec3 specular;float constant;float linear;float quadratic;
};
lightingShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));
lightingShader.setFloat("light.outerCutOff", glm::cos(glm::radians(17.5f)));

运行:

源码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#include "camera.h"#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow*window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
unsigned int loadTexture(const char *path);const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;float deltaTime = 0.0f;
float lastFrame = 0.0f;float vertices[] = {// positions          // normals           // texture coords-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,-0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
};glm::vec3 cubePositions[] = {glm::vec3(0.0f,  0.0f,  0.0f),glm::vec3(2.0f,  5.0f, -15.0f),glm::vec3(-1.5f, -2.2f, -2.5f),glm::vec3(-3.8f, -2.0f, -12.3f),glm::vec3(2.4f, -0.4f, -3.5f),glm::vec3(-1.7f,  3.0f, -7.5f),glm::vec3(1.3f, -2.0f, -2.5f),glm::vec3(1.5f,  2.0f, -2.5f),glm::vec3(1.5f,  0.2f, -1.5f),glm::vec3(-1.3f,  1.0f, -1.5f)
};glm::vec3 lightPos(1.2f, 1.0f, 2.0f);int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LightingMaps", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);//Shader lightingShader("shader/2.5_vertexShader.vs", "shader/2.5_fragmentShader_dirlight.vs");//Shader lightingShader("shader/2.5_vertexShader.vs", "shader/2.5_fragmentShader_poilight.vs");Shader lightingShader("shader/2.5_vertexShader.vs", "shader/2.5_fragmentShader_spotlight.vs");Shader lampShader("shader/2.5_lightvertexShader.vs", "shader/2.5_lightfragmentShader.vs");glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//在调用这个函数之后,无论我们怎么去移动鼠标,光标都不会显示了,它也不会离开窗口。glEnable(GL_DEPTH_TEST);unsigned int VBO, cubeVAO;glGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindVertexArray(cubeVAO);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(2);unsigned int lightVAO;glGenVertexArrays(1, &lightVAO);glBindVertexArray(lightVAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);unsigned int diffuseMap = loadTexture("container2.png");unsigned int specularMap = loadTexture("container2_specular.png");lightingShader.use();lightingShader.setInt("material.diffuse", 0);lightingShader.setInt("material.specular", 1);while (!glfwWindowShouldClose(window)){float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;processInput(window);glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);lightingShader.use();lightingShader.setVec3("light.position", camera.Position);lightingShader.setVec3("light.direction", camera.Front);lightingShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));lightingShader.setFloat("light.outerCutOff", glm::cos(glm::radians(17.5f)));lightingShader.setVec3("viewPos", camera.Position);// light propertieslightingShader.setVec3("light.ambient", 0.1f, 0.1f, 0.1f);lightingShader.setVec3("light.diffuse", 0.8f, 0.8f, 0.8f);lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);lightingShader.setFloat("light.constant", 1.0f);lightingShader.setFloat("light.linear", 0.09f);lightingShader.setFloat("light.quadratic", 0.032f);// material propertieslightingShader.setFloat("material.shininess", 32.0f);// view/projection transformationsglm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();lightingShader.setMat4("projection", projection);lightingShader.setMat4("view", view);// world transformationglm::mat4 model;lightingShader.setMat4("model", model);// bind diffuse mapglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, diffuseMap);//bind specular mapglActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, specularMap);// render the cubeglBindVertexArray(cubeVAO);//glDrawArrays(GL_TRIANGLES, 0, 36);for (unsigned int i = 0; i < 10; i++){glm::mat4 model;model = glm::translate(model, cubePositions[i]);float angle = 20.0f * i;model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));lightingShader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);}glfwPollEvents();glfwSwapBuffers(window);}glDeleteVertexArrays(1, &cubeVAO);glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &VBO);glfwTerminate();return 0;
}//回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{//glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)glViewport(0, 0, width, height);
}void processInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)camera.ProcessKeyboard(FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(RIGHT, deltaTime);}void mouse_callback(GLFWwindow*window, double xpos, double ypos)
{if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to toplastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}unsigned int loadTexture(char const * path)
{unsigned int textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);if (data){GLenum format;if (nrComponents == 1)format = GL_RED;else if (nrComponents == 3)format = GL_RGB;else if (nrComponents == 4)format = GL_RGBA;glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);stbi_image_free(data);}else{std::cout << "Texture failed to load at path: " << path << std::endl;stbi_image_free(data);}return textureID;
}

fragment_dirction:

#version 330 core
out vec4 FragColor;struct Material {sampler2D diffuse;sampler2D specular;//sampler2D emission;//vec3 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;vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(-light.direction);float diff = max(dot(norm, lightDir), 0.0);//vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));   // specularvec3 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 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));vec3 result = ambient + diffuse + specular;FragColor = vec4(result, 1.0);} 

fragment_point:

#version 330 core
out vec4 FragColor;struct Material {sampler2D diffuse;sampler2D specular;//sampler2D emission;//vec3 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;vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));// 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;vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));   // specularvec3 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 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));float distance = length(light.position - FragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * distance * distance);vec3 result = ambient * attenuation + diffuse * attenuation + specular * attenuation;FragColor = vec4(result, 1.0);} 

fragment_spot:

#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()
{// ambientvec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(light.position - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));   // specularvec3 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 * vec3(texture(material.specular, TexCoords));// 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;// attenuationfloat 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);}

多光源

模拟一个类似太阳的定向光(Directional Light)光源,四个分散在场景中的点光源(Point Light),以及一个手电筒(Flashlight)。

为了在场景中使用多个光源,我们希望将光照计算封装到GLSL函数中。这样做的原因是,每一种光源都需要一种不同的计算方法,而一旦我们想对多个光源进行光照计算时,代码很快就会变得非常复杂。如果我们只在main函数中进行所有的这些计算,代码很快就会变得难以理解。

GLSL中的函数和C函数很相似,它有一个函数名、一个返回值类型,如果函数不是在main函数之前声明的,我们还必须在代码文件顶部声明一个原型。我们对每个光照类型都创建一个不同的函数:定向光、点光源和聚光。

当我们在场景中使用多个光源时,通常使用以下方法:我们需要有一个单独的颜色向量代表片段的输出颜色。对于每一个光源,它对片段的贡献颜色将会加到片段的输出颜色向量上。所以场景中的每个光源都会计算它们各自对片段的影响,并结合为一个最终的输出颜色。大体的结构会像是这样:

out vec4 FragColor;void main()
{// 定义一个输出颜色值vec3 output;// 将定向光的贡献加到输出中output += someFunctionToCalculateDirectionalLight();// 对所有的点光源也做相同的事情for(int i = 0; i < nr_of_point_lights; i++)output += someFunctionToCalculatePointLight();// 也加上其它的光源(比如聚光)output += someFunctionToCalculateSpotLight();FragColor = vec4(output, 1.0);
}

定向光

需要在片段着色器中定义一个函数来计算定向光对相应片段的贡献:它接受一些参数并计算一个定向光照颜色。

首先,我们需要定义一个定向光源最少所需要的变量。我们可以将这些变量储存在一个叫做DirLight的结构体中,并将它定义为一个uniform。

struct DirLight {vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;
};
uniform DirLight dirLight;

我们可以将dirLight传入一个有着一下原型的函数。

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{vec3 lightDir = normalize(-light.direction);// 漫反射着色float diff = max(dot(normal, lightDir), 0.0);// 镜面光着色vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// 合并结果vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));return (ambient + diffuse + specular);
}

(既然写到了就复习一下,1.灯光向量求反,得出片元指向灯光的向量,2.求光方向和发现发现的cos值,得出diff及对漫反射的影响,3.求反射后的光向量,再求spec及对高光的影响,4.最后三个相加返回平行光的颜色。)

点光源

定义一个包含了点光源所需所有变量的结构体:

struct PointLight {vec3 position;float constant;float linear;float quadratic;vec3 ambient;vec3 diffuse;vec3 specular;
};
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];

我们在GLSL中使用了预处理指令来定义了我们场景中点光源的数量。接着我们使用了这个NR_POINT_LIGHTS常量来创建了一个PointLight结构体的数组。GLSL中的数组和C数组一样,可以使用一对方括号来创建。现在我们有四个待填充数据的PointLight结构体。

函数从参数中获取所需的所有数据,并返回一个代表该点光源对片段的颜色贡献的vec3

vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);// 漫反射着色float diff = max(dot(normal, lightDir), 0.0);// 镜面光着色vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// 衰减float distance    = length(light.position - fragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    // 合并结果vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));ambient  *= attenuation;diffuse  *= attenuation;specular *= attenuation;return (ambient + diffuse + specular);
}

(1.光源位置减去片元位置得到片元指向光源的向量,2.求diff对漫反射的影响,3.求spec对高光的影响,4.通过length函数得出片元到光源的距离,根据公式求出瞬间影响,5.求出环境光,漫反射,高光,再乘上衰减分量,最后相加)

聚光灯

(因为作者留给我们写,我就直接上函数了)

结构体:

struct SpotLight {vec3 position;vec3 direction;float cutOff;float outerCutOff;float constant;float linear;float quadratic;vec3 ambient;vec3 diffuse;vec3 specular;
};
uniform SpotLight spotLight;

函数:

vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);//漫反射float diff = max(dot(normal, lightDir), 0.0);//高光 vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);//边缘柔滑float theta = dot(lightDir, normalize(-light.direction)); float epsilon = (light.cutOff - light.outerCutOff);float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);//衰减float distance = length(light.position - fragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));ambient *= attenuation * intensity;diffuse *= attenuation * intensity;specular *= attenuation * intensity;return (ambient + diffuse + specular);
}

(1.计算片元指向光源的向量,2.漫反射影响,3.高光影响,4.根据公式计算衰减,这里的衰减既有边缘柔滑,同时也有根据距离的一个衰减,5.最后的结果要乘上边缘柔滑的分量和衰减的分量,再相加)

(注意一个问题,三种光的函数,都要在main函数之前声明!!!)

合并结果

将它们合并放到main函数中:

void main()
{// 属性vec3 norm = normalize(Normal);vec3 viewDir = normalize(viewPos - FragPos);// 第一阶段:定向光照vec3 result = CalcDirLight(dirLight, norm, viewDir);// 第二阶段:点光源for(int i = 0; i < NR_POINT_LIGHTS; i++)result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);    // 第三阶段:聚光result += CalcSpotLight(spotLight, norm, FragPos, viewDir);    FragColor = vec4(result, 1.0);
}

设置定向光结构体的uniform应该非常熟悉了,但是你可能会在想我们该如何设置点光源的uniform值,因为点光源的uniform现在是一个PointLight的数组了。设置一个结构体数组的uniform和设置一个结构体的uniform是很相似的,但是这一次在访问uniform位置的时候,我们需要定义对应的数组下标值:

lightingShader.setFloat("pointLights[0].constant", 1.0f);

我们还需要为每个点光源定义一个位置向量,所以我们让它们在场景中分散一点。我们会定义另一个glm::vec3数组来包含点光源的位置:

glm::vec3 pointLightPositions[] = {glm::vec3( 0.7f,  0.2f,  2.0f),glm::vec3( 2.3f, -3.3f, -4.0f),glm::vec3(-4.0f,  2.0f, -12.0f),glm::vec3( 0.0f,  0.0f, -3.0f)
};

设置所有灯光参数的代码,以及立方体的渲染,直接在源码中展示了,先上图:

源码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#include "camera.h"#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow*window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
unsigned int loadTexture(const char *path);const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;float deltaTime = 0.0f;
float lastFrame = 0.0f;float vertices[] = {// positions          // normals           // texture coords-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,-0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,-0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,-0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,-0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,-0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,-0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,-0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,-0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
};glm::vec3 cubePositions[] = {glm::vec3(0.0f,  0.0f,  0.0f),glm::vec3(2.0f,  5.0f, -15.0f),glm::vec3(-1.5f, -2.2f, -2.5f),glm::vec3(-3.8f, -2.0f, -12.3f),glm::vec3(2.4f, -0.4f, -3.5f),glm::vec3(-1.7f,  3.0f, -7.5f),glm::vec3(1.3f, -2.0f, -2.5f),glm::vec3(1.5f,  2.0f, -2.5f),glm::vec3(1.5f,  0.2f, -1.5f),glm::vec3(-1.3f,  1.0f, -1.5f)
};glm::vec3 lightPos(1.2f, 1.0f, 2.0f);glm::vec3 pointLightPositions[] = {glm::vec3(0.7f,  0.2f,  2.0f),glm::vec3(2.3f, -3.3f, -4.0f),glm::vec3(-4.0f,  2.0f, -12.0f),glm::vec3(0.0f,  0.0f, -3.0f)
};int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LightingMaps", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);Shader lightingShader("shader/2.6_vertexShader.vs", "shader/2.6_fragmentShader.vs");Shader lampShader("shader/2.6_lightvertexShader.vs", "shader/2.6_lightfragmentShader.vs");glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//在调用这个函数之后,无论我们怎么去移动鼠标,光标都不会显示了,它也不会离开窗口。glEnable(GL_DEPTH_TEST);unsigned int VBO, cubeVAO;glGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindVertexArray(cubeVAO);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(2);unsigned int lightVAO;glGenVertexArrays(1, &lightVAO);glBindVertexArray(lightVAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);unsigned int diffuseMap = loadTexture("container2.png");unsigned int specularMap = loadTexture("container2_specular.png");lightingShader.use();lightingShader.setInt("material.diffuse", 0);lightingShader.setInt("material.specular", 1);while (!glfwWindowShouldClose(window)){float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;processInput(window);glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);lightingShader.use();lightingShader.setVec3("viewPos", camera.Position);lightingShader.setFloat("material.shininess", 32.0f);// directional lightlightingShader.setVec3("dirLight.direction", -0.2f, -1.0f, -0.3f);lightingShader.setVec3("dirLight.ambient", 0.05f, 0.05f, 0.05f);lightingShader.setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f);lightingShader.setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f);// point light 1lightingShader.setVec3("pointLights[0].position", pointLightPositions[0]);lightingShader.setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f);lightingShader.setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f);lightingShader.setVec3("pointLights[0].specular", 1.0f, 1.0f, 1.0f);lightingShader.setFloat("pointLights[0].constant", 1.0f);lightingShader.setFloat("pointLights[0].linear", 0.09);lightingShader.setFloat("pointLights[0].quadratic", 0.032);// point light 2lightingShader.setVec3("pointLights[1].position", pointLightPositions[1]);lightingShader.setVec3("pointLights[1].ambient", 0.05f, 0.05f, 0.05f);lightingShader.setVec3("pointLights[1].diffuse", 0.8f, 0.8f, 0.8f);lightingShader.setVec3("pointLights[1].specular", 1.0f, 1.0f, 1.0f);lightingShader.setFloat("pointLights[1].constant", 1.0f);lightingShader.setFloat("pointLights[1].linear", 0.09);lightingShader.setFloat("pointLights[1].quadratic", 0.032);// point light 3lightingShader.setVec3("pointLights[2].position", pointLightPositions[2]);lightingShader.setVec3("pointLights[2].ambient", 0.05f, 0.05f, 0.05f);lightingShader.setVec3("pointLights[2].diffuse", 0.8f, 0.8f, 0.8f);lightingShader.setVec3("pointLights[2].specular", 1.0f, 1.0f, 1.0f);lightingShader.setFloat("pointLights[2].constant", 1.0f);lightingShader.setFloat("pointLights[2].linear", 0.09);lightingShader.setFloat("pointLights[2].quadratic", 0.032);// point light 4lightingShader.setVec3("pointLights[3].position", pointLightPositions[3]);lightingShader.setVec3("pointLights[3].ambient", 0.05f, 0.05f, 0.05f);lightingShader.setVec3("pointLights[3].diffuse", 0.8f, 0.8f, 0.8f);lightingShader.setVec3("pointLights[3].specular", 1.0f, 1.0f, 1.0f);lightingShader.setFloat("pointLights[3].constant", 1.0f);lightingShader.setFloat("pointLights[3].linear", 0.09);lightingShader.setFloat("pointLights[3].quadratic", 0.032);// spotLightlightingShader.setVec3("spotLight.position", camera.Position);lightingShader.setVec3("spotLight.direction", camera.Front);lightingShader.setVec3("spotLight.ambient", 0.0f, 0.0f, 0.0f);lightingShader.setVec3("spotLight.diffuse", 1.0f, 1.0f, 1.0f);lightingShader.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f);lightingShader.setFloat("spotLight.constant", 1.0f);lightingShader.setFloat("spotLight.linear", 0.09);lightingShader.setFloat("spotLight.quadratic", 0.032);lightingShader.setFloat("spotLight.cutOff", glm::cos(glm::radians(12.5f)));lightingShader.setFloat("spotLight.outerCutOff", glm::cos(glm::radians(15.0f)));// view/projection transformationsglm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();lightingShader.setMat4("projection", projection);lightingShader.setMat4("view", view);// world transformationglm::mat4 model;lightingShader.setMat4("model", model);// bind diffuse mapglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, diffuseMap);//bind specular mapglActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, specularMap);// render the cubeglBindVertexArray(cubeVAO);for (unsigned int i = 0; i < 10; i++){glm::mat4 model;model = glm::translate(model, cubePositions[i]);float angle = 20.0f * i;model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));lightingShader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);}// also draw the lamp object(s)lampShader.use();lampShader.setMat4("projection", projection);lampShader.setMat4("view", view);glBindVertexArray(lightVAO);for (unsigned int i = 0; i < 4; i++){model = glm::mat4();model = glm::translate(model, pointLightPositions[i]);model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cubelampShader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 36);}glfwPollEvents();glfwSwapBuffers(window);}glDeleteVertexArrays(1, &cubeVAO);glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &VBO);glfwTerminate();return 0;
}//回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{//glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)glViewport(0, 0, width, height);
}void processInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)camera.ProcessKeyboard(FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(RIGHT, deltaTime);}void mouse_callback(GLFWwindow*window, double xpos, double ypos)
{if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to toplastX = xpos;lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}unsigned int loadTexture(char const * path)
{unsigned int textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);if (data){GLenum format;if (nrComponents == 1)format = GL_RED;else if (nrComponents == 3)format = GL_RGB;else if (nrComponents == 4)format = GL_RGBA;glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);stbi_image_free(data);}else{std::cout << "Texture failed to load at path: " << path << std::endl;stbi_image_free(data);}return textureID;
}

fragmentshader:

#version 330 core
out vec4 FragColor;in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;struct Material {sampler2D diffuse;sampler2D specular;float shininess;
};
uniform Material material;struct DirLight{vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;
};
uniform DirLight dirLight;struct PointLight {vec3 position;float constant;float linear;float quadratic;vec3 ambient;vec3 diffuse;vec3 specular;
};
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];struct SpotLight {vec3 position;vec3 direction;float cutOff;float outerCutOff;float constant;float linear;float quadratic;vec3 ambient;vec3 diffuse;vec3 specular;
};
uniform SpotLight spotLight;uniform vec3 viewPos;vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);void main()
{// 属性vec3 normal = normalize(Normal);vec3 viewDir = normalize(viewPos - FragPos);// 第一阶段:定向光照vec3 result = CalcDirLight(dirLight, normal, viewDir);// 第二阶段:点光源for(int i = 0; i < NR_POINT_LIGHTS; i++)result += CalcPointLight(pointLights[i], normal, FragPos, viewDir);    // 第三阶段:聚光result += CalcSpotLight(spotLight, normal, FragPos, viewDir);    FragColor = vec4(result, 1.0);
}vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{vec3 lightDir = normalize(-light.direction);// 漫反射着色float diff = max(dot(normal, lightDir), 0.0);// 镜面光着色vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// 合并结果vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));return (ambient + diffuse + specular);
}vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);// 漫反射着色float diff = max(dot(normal, lightDir), 0.0);// 镜面光着色vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// 衰减float distance    = length(light.position - fragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    // 合并结果vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));ambient  *= attenuation;diffuse  *= attenuation;specular *= attenuation;return (ambient + diffuse + specular);
}vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{vec3 lightDir = normalize(light.position - fragPos);//diffusefloat diff = max(dot(normal, lightDir), 0.0);//specular vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// 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);vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));float distance = length(light.position - fragPos);float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); ambient *= attenuation * intensity;diffuse *= attenuation * intensity;specular *= attenuation * intensity;return (ambient + diffuse + specular);
}

其实这一部分自己写的内容挺少,基本上都是一些复习,因为作者翻译的都比较好理解,多看两遍都是能看得懂的。

OK,后天就回学校了,要继续学习之后模型加载的内容,学完了再接着写!!!

2018/8/05--OpenGL学习笔记(七)Light Caster/Multiple Light相关推荐

  1. OpenGL学习笔记(八):进一步理解VAO、VBO和SHADER,并使用VAO、VBO和SHADER绘制一个三角形

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7888 ...

  2. 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】

    ✅ 重点参考了 LearnOpenGL CN 的内容,但大部分知识内容,小编已作改写,以方便读者理解. 文章目录 零. 成果预览图 一. 光照原理与投光物的配置 1.1 光照原理 1.2 投光物 二. ...

  3. websocket 获取连接id_Swoole学习笔记七:搭建WebSocket长连接 之 使用 USER_ID 作为身份凭证...

    Swoole学习笔记七:搭建WebSocket长连接 之 使用 USER_ID 作为身份凭证 2年前 阅读 3678 评论 0 喜欢 0 ### 0.前言 前面基本的WebSocket操作,我们基本都 ...

  4. 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】

    ✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...

  5. Typescript 学习笔记七:泛型

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  6. 吴恩达《机器学习》学习笔记七——逻辑回归(二分类)代码

    吴恩达<机器学习>学习笔记七--逻辑回归(二分类)代码 一.无正则项的逻辑回归 1.问题描述 2.导入模块 3.准备数据 4.假设函数 5.代价函数 6.梯度下降 7.拟合参数 8.用训练 ...

  7. ROS学习笔记七:使用rqt_console和roslaunch

    ROS学习笔记七:使用rqt_console和roslaunch 本节主要介绍在调试时使用的rqt_console和rqt_logger_level,以及一次性打开多个节点的工具roslaunch. ...

  8. 【K210】K210学习笔记七——使用K210拍摄照片并在MaixHub上进行训练

    [K210]K210学习笔记七--使用K210拍摄照片并在MaixHub上进行训练 前言 K210准备工作 K210如何拍摄照片 准备工作 拍摄相关代码定义 用K210拍摄到的照片在MaixHub平台 ...

  9. OpenGL学习笔记:矩阵变换

    文章目录 缩放 glm矩阵表示 glm缩放矩阵实现 位移 齐次坐标 glm位移矩阵实现 旋转 沿x轴旋转 沿y轴旋转 沿z轴旋转 沿任意轴旋转 glm旋转矩阵实现 矩阵的组合 glm矩阵组合使用 接上 ...

最新文章

  1. 一个下载Windows镜像的地址
  2. viso 画背景框_3分钟漫画教程 | Q版漫画少女线稿,画完暖暖的
  3. c语言按行存储数据库,C语言实现按行读写文件
  4. 位向量(bit vector)(算法导论第十一章11.1-2)
  5. 使用Selenium和HTML中的动态ID进行Java测试
  6. oracle增加字段为主键自增_在 Oracle 中设置自增列
  7. 感谢贫穷!707分考入北大,她的这篇文章看哭所有人…
  8. Apache JMeter 下载
  9. python-Generalization of Hops
  10. 系统的本地策略不允许您采用交互式登录
  11. vmware中nat模式中使用静态ip后无法上网的问题
  12. 评价类模型:1.层次分析法
  13. [我的阿里云服务器] —— 安装LAMP
  14. 【图频处理】基于matlab GUI界面环图像处理与音乐播放系统【含Matlab源码 185期】
  15. 自动驾驶3-3 自动驾驶的安全框架 Safety Frameworks for Self-Driving
  16. 数学建模论文写作要求
  17. suse linux 11 sp3 的安装
  18. 如何写数据分析岗位简历?看完瞬间秒懂
  19. 配置微软Azure Kinect DK 录制器k4arecorder
  20. Win11双系统引导怎么设置?Win11双系统引导设置方法介绍

热门文章

  1. js正则表达式 验证手机号,email地址和邮政编码
  2. Python3实践项目一:生成0-5岁宝宝生长发育报告
  3. python控制机械臂6轴_在ROS环境下,怎么使用moveit!来驱动真实的六轴机械臂?
  4. 从软件公司的异同点讲起,聊聊未来的程序员该如何选公司和谋规划
  5. 梦幻哪个服务器最多牛人,盘点梦幻西游十大火区 你所在的服务器上榜了?
  6. 【Homeassistant 的Node-red插件之小爱同学语音功能开通】
  7. 计算机系统结构——非线性流水线调度
  8. VirtualBox 使用总结
  9. 大文件传输有哪些方式可用?大文件传输有哪些方式?
  10. 用PHP实现最小公倍数 2-200素数