OpenGL多点光源加手电筒

转自

https://learnopengl-cn.github.io/02%20Lighting/06%20Multiple%20lights/

1,多光源

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

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

实际的代码对每一种实现都可能不同,但大体的结构都是差不多的。我们定义了几个函数,用来计算每个光源的影响,并将最终的结果颜色加到输出颜色向量上。例如,如果两个光源都很靠近一个片段,那么它们所结合的贡献将会形成一个比单个光源照亮时更加明亮的片段。

2, 定向光

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

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

struct DirLight {
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};  
uniform DirLight dirLight;

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

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);

你可以看到,这个函数需要一个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);
}

3,点光源

和定向光一样,我们也希望定义一个用于计算点光源对相应片段贡献,以及衰减,的函数。同样,我们定义一个包含了点光源所需所有变量的结构体:

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 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);

这个函数从参数中获取所需的所有数据,并返回一个代表该点光源对片段的颜色贡献的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);
}

4,合并结果

现在我们已经定义了一个计算定向光的函数和一个计算点光源的函数了,我们可以将它们合并放到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);
}

每个光源类型都将它们的贡献加到了最终的输出颜色上,直到所有的光源都处理完了。最终的颜色包含了场景中所有光源的颜色影响所合并的结果。如果你想的话,你也可以实现一个聚光,并将它的效果加到输出颜色中。我们会将CalcSpotLight函数留给读者作为练习。

设置定向光结构体的uniform应该非常熟悉了,但是你可能会在想我们该如何设置点光源的uniform值,因为点光源的uniform现在是一个PointLight的数组了。这并不是我们以前讨论过的话题。

很幸运的是,这并不是很复杂,设置一个结构体数组的uniform和设置一个结构体的uniform是很相似的,但是这一次在访问uniform位置的时候,我们需要定义对应的数组下标值:

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

在这里我们索引了pointLights数组中的第一个PointLight,并获取了constant变量的位置。但这也意味着不幸的是我们必须对这四个点光源手动设置uniform值,这让点光源本身就产生了28个uniform调用,非常冗长。你也可以尝试将这些抽象出去一点,定义一个点光源类,让它来为你设置uniform值,但最后你仍然要用这种方式设置所有光源的uniform值。

别忘了,我们还需要为每个点光源定义一个位置向量,所以我们让它们在场景中分散一点。我们会定义另一个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)
};

 5,最后放上所有源代码:

  1 #include <glad/glad.h>
  2 #include <GLFW/glfw3.h>
  3   
  4 #include <glm/glm.hpp>
  5 #include <glm/gtc/matrix_transform.hpp>
  6 #include <glm/gtc/type_ptr.hpp>
  7 
  8 #include "stb_image.h"
  9 #include "shader_s.h"
 10 #include "Camera.h"
 11 #include <iostream>
 12 //("../res/light/colors.vs", "../res/light/colors.fs")
 13 
 14 void framebuffer_size_callback(GLFWwindow* window, int width, int height);
 15 void mouse_callback(GLFWwindow* window, double xpos, double ypos);
 16 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
 17 void processInput(GLFWwindow *window);
 18 unsigned int loadTexture(const char *path);
 19 
 20 // settings
 21 const unsigned int SCR_WIDTH = 800;
 22 const unsigned int SCR_HEIGHT = 600;
 23 
 24 // camera
 25 Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
 26 float lastX = SCR_WIDTH / 2.0f;
 27 float lastY = SCR_HEIGHT / 2.0f;
 28 bool firstMouse = true;
 29 
 30 // timing
 31 float deltaTime = 0.0f;
 32 float lastFrame = 0.0f;
 33 
 34 // lighting
 35 glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
 36 
 37 int main()
 38 {
 39     // glfw: initialize and configure
 40     // ------------------------------
 41     glfwInit();
 42     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
 43     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 44     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 45 
 46 #ifdef __APPLE__
 47     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
 48 #endif
 49 
 50     // glfw window creation
 51     // --------------------
 52     GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
 53     if (window == NULL)
 54     {
 55         std::cout << "Failed to create GLFW window" << std::endl;
 56         glfwTerminate();
 57         return -1;
 58     }
 59     glfwMakeContextCurrent(window);
 60     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
 61     glfwSetCursorPosCallback(window, mouse_callback);
 62     glfwSetScrollCallback(window, scroll_callback);
 63 
 64     // tell GLFW to capture our mouse
 65     glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
 66 
 67     // glad: load all OpenGL function pointers
 68     // ---------------------------------------
 69     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
 70     {
 71         std::cout << "Failed to initialize GLAD" << std::endl;
 72         return -1;
 73     }
 74 
 75     // configure global opengl state
 76     // -----------------------------
 77     glEnable(GL_DEPTH_TEST);
 78 
 79 
 80     // build and compile our shader zprogram
 81     // ("../res/light/colors.vs", "../res/light/colors.fs")
 82     Shader lightingShader("../res/light/materials.vs", "../res/light/materials.fs");
 83     Shader lampShader("../res/light/lamp.vs", "../res/light/lamp.fs");
 84     // set up vertex data (and buffer(s)) and configure vertex attributes
 85    // ------------------------------------------------------------------
 86     float vertices[] = {
 87         // positions          // normals           // texture coords
 88         -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
 89          0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,
 90          0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
 91          0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
 92         -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,
 93         -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
 94 
 95         -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
 96          0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,
 97          0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
 98          0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
 99         -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,
100         -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
101 
102         -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
103         -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
104         -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
105         -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
106         -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
107         -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
108 
109          0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
110          0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
111          0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
112          0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
113          0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
114          0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
115 
116         -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
117          0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,
118          0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
119          0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
120         -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,
121         -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
122 
123         -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
124          0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,
125          0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
126          0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
127         -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,
128         -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
129     };
130     // positions all containers
131     glm::vec3 cubePositions[] = {
132         glm::vec3(0.0f,  0.0f,  0.0f),
133         glm::vec3(2.0f,  5.0f, -15.0f),
134         glm::vec3(-1.5f, -2.2f, -2.5f),
135         glm::vec3(-3.8f, -2.0f, -12.3f),
136         glm::vec3(2.4f, -0.4f, -3.5f),
137         glm::vec3(-1.7f,  3.0f, -7.5f),
138         glm::vec3(1.3f, -2.0f, -2.5f),
139         glm::vec3(1.5f,  2.0f, -2.5f),
140         glm::vec3(1.5f,  0.2f, -1.5f),
141         glm::vec3(-1.3f,  1.0f, -1.5f)
142     };
143     // positions of the point lights
144     glm::vec3 pointLightPositions[] = {
145         glm::vec3(0.7f,  0.2f,  2.0f),
146         glm::vec3(2.3f, -3.3f, -4.0f),
147         glm::vec3(-4.0f,  2.0f, -12.0f),
148         glm::vec3(0.0f,  0.0f, -3.0f)
149     };
150     // first, configure the cube's VAO (and VBO)
151     unsigned int VBO, cubeVAO;
152     glGenVertexArrays(1, &cubeVAO);
153     glGenBuffers(1, &VBO);
154 
155     glBindBuffer(GL_ARRAY_BUFFER, VBO);
156     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
157 
158     glBindVertexArray(cubeVAO);
159     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
160     glEnableVertexAttribArray(0);
161     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
162     glEnableVertexAttribArray(1);
163     glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
164     glEnableVertexAttribArray(2);
165 
166     // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)
167     unsigned int lightVAO;
168     glGenVertexArrays(1, &lightVAO);
169     glBindVertexArray(lightVAO);
170 
171     glBindBuffer(GL_ARRAY_BUFFER, VBO);
172     // note that we update the lamp's position attribute's stride to reflect the updated buffer data
173     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
174     glEnableVertexAttribArray(0);
175 
176     // load textures (we now use a utility function to keep the code more organized)
177     // -----------------------------------------------------------------------------
178     unsigned int diffuseMap = loadTexture("../res/light/container2.png");
179     unsigned int specularMap = loadTexture("../res/light/container2_specular.png");
180     //std::cout << diffuseMap;
181     //unsigned int diffuseMap = loadTexture(FileSystem::getPath("res/light/container2.png").c_str());
182 
183     // shader configuration
184     // --------------------
185     lightingShader.use();
186     lightingShader.setInt("material.diffuse", 0);
187     lightingShader.setInt("material.specular", 1);
188 
189 
190     // render loop
191     // -----------
192     while (!glfwWindowShouldClose(window))
193     {
194         // per-frame time logic
195         // --------------------
196         float currentFrame = glfwGetTime();
197         deltaTime = currentFrame - lastFrame;
198         lastFrame = currentFrame;
199 
200         // input
201         // -----
202         processInput(window);
203 
204         // render
205         // ------
206         glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
207         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
208 
209         // be sure to activate shader when setting uniforms/drawing objects
210         lightingShader.use();
211         lightingShader.setVec3("viewPos", camera.Position);
212         lightingShader.setFloat("material.shininess", 32.0f);
213 
214         /*
215            Here we set all the uniforms for the 5/6 types of lights we have. We have to set them manually and index
216            the proper PointLight struct in the array to set each uniform variable. This can be done more code-friendly
217            by defining light types as classes and set their values in there, or by using a more efficient uniform approach
218            by using 'Uniform buffer objects', but that is something we'll discuss in the 'Advanced GLSL' tutorial.
219         */
220         // directional light
221         lightingShader.setVec3("dirLight.direction", -0.2f, -1.0f, -0.3f);
222         lightingShader.setVec3("dirLight.ambient", 0.05f, 0.05f, 0.05f);
223         lightingShader.setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f);
224         lightingShader.setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f);
225         // point light 1
226         lightingShader.setVec3("pointLights[0].position", pointLightPositions[0]);
227         lightingShader.setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f);
228         lightingShader.setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f);
229         lightingShader.setVec3("pointLights[0].specular", 1.0f, 1.0f, 1.0f);
230         lightingShader.setFloat("pointLights[0].constant", 1.0f);
231         lightingShader.setFloat("pointLights[0].linear", 0.09);
232         lightingShader.setFloat("pointLights[0].quadratic", 0.032);
233         // point light 2
234         lightingShader.setVec3("pointLights[1].position", pointLightPositions[1]);
235         lightingShader.setVec3("pointLights[1].ambient", 0.05f, 0.05f, 0.05f);
236         lightingShader.setVec3("pointLights[1].diffuse", 0.8f, 0.8f, 0.8f);
237         lightingShader.setVec3("pointLights[1].specular", 1.0f, 1.0f, 1.0f);
238         lightingShader.setFloat("pointLights[1].constant", 1.0f);
239         lightingShader.setFloat("pointLights[1].linear", 0.09);
240         lightingShader.setFloat("pointLights[1].quadratic", 0.032);
241         // point light 3
242         lightingShader.setVec3("pointLights[2].position", pointLightPositions[2]);
243         lightingShader.setVec3("pointLights[2].ambient", 0.05f, 0.05f, 0.05f);
244         lightingShader.setVec3("pointLights[2].diffuse", 0.8f, 0.8f, 0.8f);
245         lightingShader.setVec3("pointLights[2].specular", 1.0f, 1.0f, 1.0f);
246         lightingShader.setFloat("pointLights[2].constant", 1.0f);
247         lightingShader.setFloat("pointLights[2].linear", 0.09);
248         lightingShader.setFloat("pointLights[2].quadratic", 0.032);
249         // point light 4
250         lightingShader.setVec3("pointLights[3].position", pointLightPositions[3]);
251         lightingShader.setVec3("pointLights[3].ambient", 0.05f, 0.05f, 0.05f);
252         lightingShader.setVec3("pointLights[3].diffuse", 0.8f, 0.8f, 0.8f);
253         lightingShader.setVec3("pointLights[3].specular", 1.0f, 1.0f, 1.0f);
254         lightingShader.setFloat("pointLights[3].constant", 1.0f);
255         lightingShader.setFloat("pointLights[3].linear", 0.09);
256         lightingShader.setFloat("pointLights[3].quadratic", 0.032);
257         // spotLight
258         lightingShader.setVec3("spotLight.position", camera.Position);
259         lightingShader.setVec3("spotLight.direction", camera.Front);
260         lightingShader.setVec3("spotLight.ambient", 0.0f, 0.0f, 0.0f);
261         lightingShader.setVec3("spotLight.diffuse", 1.0f, 1.0f, 1.0f);
262         lightingShader.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f);
263         lightingShader.setFloat("spotLight.constant", 1.0f);
264         lightingShader.setFloat("spotLight.linear", 0.09);
265         lightingShader.setFloat("spotLight.quadratic", 0.032);
266         lightingShader.setFloat("spotLight.cutOff", glm::cos(glm::radians(12.5f)));
267         lightingShader.setFloat("spotLight.outerCutOff", glm::cos(glm::radians(15.0f)));
268 
269         // view/projection transformations
270         glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
271         glm::mat4 view = camera.GetViewMatrix();
272         lightingShader.setMat4("projection", projection);
273         lightingShader.setMat4("view", view);
274 
275         // world transformation
276         glm::mat4 model = glm::mat4(1.0f);
277         lightingShader.setMat4("model", model);
278 
279         // bind diffuse map
280         glActiveTexture(GL_TEXTURE0);
281         glBindTexture(GL_TEXTURE_2D, diffuseMap);
282         // bind specular map
283         glActiveTexture(GL_TEXTURE1);
284         glBindTexture(GL_TEXTURE_2D, specularMap);
285 
286         // render containers
287         glBindVertexArray(cubeVAO);
288         for (unsigned int i = 0; i < 10; i++)
289         {
290             // calculate the model matrix for each object and pass it to shader before drawing
291             glm::mat4 model = glm::mat4(1.0f);
292             model = glm::translate(model, cubePositions[i]);
293             float angle = 20.0f * i;
294             model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
295             lightingShader.setMat4("model", model);
296 
297             glDrawArrays(GL_TRIANGLES, 0, 36);
298         }
299 
300         // also draw the lamp object(s)
301         lampShader.use();
302         lampShader.setMat4("projection", projection);
303         lampShader.setMat4("view", view);
304 
305         // we now draw as many light bulbs as we have point lights.
306         glBindVertexArray(lightVAO);
307         for (unsigned int i = 0; i < 4; i++)
308         {
309             model = glm::mat4(1.0f);
310             model = glm::translate(model, pointLightPositions[i]);
311             model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
312             lampShader.setMat4("model", model);
313             glDrawArrays(GL_TRIANGLES, 0, 36);
314         }
315 
316 
317         // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
318         // -------------------------------------------------------------------------------
319         glfwSwapBuffers(window);
320         glfwPollEvents();
321     }
322 
323     // optional: de-allocate all resources once they've outlived their purpose:
324     // ------------------------------------------------------------------------
325     glDeleteVertexArrays(1, &cubeVAO);
326     glDeleteVertexArrays(1, &lightVAO);
327     glDeleteBuffers(1, &VBO);
328 
329     // glfw: terminate, clearing all previously allocated GLFW resources.
330     // ------------------------------------------------------------------
331     glfwTerminate();
332     return 0;
333 }
334 
335 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
336 // ---------------------------------------------------------------------------------------------------------
337 void processInput(GLFWwindow *window)
338 {
339     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
340         glfwSetWindowShouldClose(window, true);
341 
342     if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
343         camera.ProcessKeyboard(FORWARD, deltaTime);
344     if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
345         camera.ProcessKeyboard(BACKWARD, deltaTime);
346     if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
347         camera.ProcessKeyboard(LEFT, deltaTime);
348     if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
349         camera.ProcessKeyboard(RIGHT, deltaTime);
350 }
351 
352 // glfw: whenever the window size changed (by OS or user resize) this callback function executes
353 // ---------------------------------------------------------------------------------------------
354 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
355 {
356     // make sure the viewport matches the new window dimensions; note that width and 
357     // height will be significantly larger than specified on retina displays.
358     glViewport(0, 0, width, height);
359 }
360 
361 
362 // glfw: whenever the mouse moves, this callback is called
363 // -------------------------------------------------------
364 void mouse_callback(GLFWwindow* window, double xpos, double ypos)
365 {
366     if (firstMouse)
367     {
368         lastX = xpos;
369         lastY = ypos;
370         firstMouse = false;
371     }
372 
373     float xoffset = xpos - lastX;
374     float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
375 
376     lastX = xpos;
377     lastY = ypos;
378 
379     camera.ProcessMouseMovement(xoffset, yoffset);
380 }
381 
382 // glfw: whenever the mouse scroll wheel scrolls, this callback is called
383 // ----------------------------------------------------------------------
384 void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
385 {
386     camera.ProcessMouseScroll(yoffset);
387 }
388 unsigned int loadTexture(char const * path)
389 {
390     unsigned int textureID;
391     glGenTextures(1, &textureID);
392 
393     int width, height, nrComponents;
394     unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
395     if (data)
396     {
397         GLenum format;
398         if (nrComponents == 1)
399             format = GL_RED;
400         else if (nrComponents == 3)
401             format = GL_RGB;
402         else if (nrComponents == 4)
403             format = GL_RGBA;
404 
405         glBindTexture(GL_TEXTURE_2D, textureID);
406         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
407         glGenerateMipmap(GL_TEXTURE_2D);
408 
409         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
410         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
411         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
412         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
413 
414         stbi_image_free(data);
415     }
416     else
417     {
418         std::cout << "Texture failed to load at path: " << path << std::endl;
419         stbi_image_free(data);
420     }
421 
422     return textureID;
423 }
TestShader.cpp
 1 #version 330 core
 2 layout (location = 0) in vec3 aPos;
 3 layout (location = 1) in vec3 aNormal;
 4 layout (location = 2) in vec2 aTexCoords;
 5 
 6 out vec3 FragPos;
 7 out vec3 Normal;
 8 out vec2 TexCoords;
 9 
10 uniform mat4 model;
11 uniform mat4 view;
12 uniform mat4 projection;
13 
14 void main()
15 {
16     FragPos = vec3(model * vec4(aPos, 1.0));
17     Normal = mat3(transpose(inverse(model))) * aNormal;  
18     TexCoords = aTexCoords;
19     
20     gl_Position = projection * view * vec4(FragPos, 1.0);
21 }
material.vs
  1 #version 330 core
  2 out vec4 FragColor;
  3 
  4 struct Material {
  5     sampler2D diffuse;
  6     sampler2D specular;
  7     float shininess;
  8 }; 
  9 
 10 struct DirLight {
 11     vec3 direction;
 12     
 13     vec3 ambient;
 14     vec3 diffuse;
 15     vec3 specular;
 16 };
 17 
 18 struct PointLight {
 19     vec3 position;
 20     
 21     float constant;
 22     float linear;
 23     float quadratic;
 24     
 25     vec3 ambient;
 26     vec3 diffuse;
 27     vec3 specular;
 28 };
 29 
 30 struct SpotLight {
 31     vec3 position;
 32     vec3 direction;
 33     float cutOff;
 34     float outerCutOff;
 35   
 36     float constant;
 37     float linear;
 38     float quadratic;
 39   
 40     vec3 ambient;
 41     vec3 diffuse;
 42     vec3 specular;       
 43 };
 44 
 45 #define NR_POINT_LIGHTS 4
 46 
 47 in vec3 FragPos;
 48 in vec3 Normal;
 49 in vec2 TexCoords;
 50 
 51 uniform vec3 viewPos;
 52 uniform DirLight dirLight;
 53 uniform PointLight pointLights[NR_POINT_LIGHTS];
 54 uniform SpotLight spotLight;
 55 uniform Material material;
 56 
 57 // function prototypes
 58 vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
 59 vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
 60 vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
 61 
 62 void main()
 63 {    
 64     // properties
 65     vec3 norm = normalize(Normal);
 66     vec3 viewDir = normalize(viewPos - FragPos);
 67     
 68     // == =====================================================
 69     // Our lighting is set up in 3 phases: directional, point lights and an optional flashlight
 70     // For each phase, a calculate function is defined that calculates the corresponding color
 71     // per lamp. In the main() function we take all the calculated colors and sum them up for
 72     // this fragment's final color.
 73     // == =====================================================
 74     // phase 1: directional lighting
 75     vec3 result = CalcDirLight(dirLight, norm, viewDir);
 76     // phase 2: point lights
 77     for(int i = 0; i < NR_POINT_LIGHTS; i++)
 78         result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);    
 79     // phase 3: spot light
 80     result += CalcSpotLight(spotLight, norm, FragPos, viewDir);    
 81     
 82     FragColor = vec4(result, 1.0);
 83 }
 84 
 85 // calculates the color when using a directional light.
 86 vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
 87 {
 88     vec3 lightDir = normalize(-light.direction);
 89     // diffuse shading
 90     float diff = max(dot(normal, lightDir), 0.0);
 91     // specular shading
 92     vec3 reflectDir = reflect(-lightDir, normal);
 93     float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
 94     // combine results
 95     vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
 96     vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
 97     vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
 98     return (ambient + diffuse + specular);
 99 }
100 
101 // calculates the color when using a point light.
102 vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
103 {
104     vec3 lightDir = normalize(light.position - fragPos);
105     // diffuse shading
106     float diff = max(dot(normal, lightDir), 0.0);
107     // specular shading
108     vec3 reflectDir = reflect(-lightDir, normal);
109     float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
110     // attenuation
111     float distance = length(light.position - fragPos);
112     float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    
113     // combine results
114     vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
115     vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
116     vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
117     ambient *= attenuation;
118     diffuse *= attenuation;
119     specular *= attenuation;
120     return (ambient + diffuse + specular);
121 }
122 
123 // calculates the color when using a spot light.
124 vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
125 {
126     vec3 lightDir = normalize(light.position - fragPos);
127     // diffuse shading
128     float diff = max(dot(normal, lightDir), 0.0);
129     // specular shading
130     vec3 reflectDir = reflect(-lightDir, normal);
131     float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
132     // attenuation
133     float distance = length(light.position - fragPos);
134     float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    
135     // spotlight intensity
136     float theta = dot(lightDir, normalize(-light.direction)); 
137     float epsilon = light.cutOff - light.outerCutOff;
138     float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
139     // combine results
140     vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
141     vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
142     vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
143     ambient *= attenuation * intensity;
144     diffuse *= attenuation * intensity;
145     specular *= attenuation * intensity;
146     return (ambient + diffuse + specular);
147 }
material.fs
 1 #version 330 core
 2 layout (location = 0) in vec3 aPos;
 3 
 4 uniform mat4 model;
 5 uniform mat4 view;
 6 uniform mat4 projection;
 7 
 8 void main()
 9 {
10     gl_Position = projection * view * model * vec4(aPos, 1.0);
11 }
lamp.vs
1 #version 330 core
2 out vec4 FragColor;
3 
4 void main()
5 {
6     FragColor = vec4(1.0); // set alle 4 vector values to 1.0
7 }
lamp.fs

 container2.png

container2_specular.png

原文地址:https://www.cnblogs.com/icyhusky/p/10881032.html