OpenGL光照教程之 光照贴图

news/2024/11/29 0:31:44/

引言

 前面的教程,我们讨论了让不同的物体拥有各自不同的材质并对光照做出不同的反应的方法。在一个光照场景中,让每个物体拥有和其他物体不同的外观很棒,但是这仍然不能对一个物体的图像输出提供足够多的灵活性。
 前面的教程中我们将一个物体自身作为一个整体为其定义了一个材质,但是现实世界的物体通常不会只有这么一种材质,而是由多种材质组成。想象一辆车:它的外表质地光亮,车窗会部分反射环境,它的轮胎没有specular高光,轮彀却非常闪亮(在洗过之后)。汽车同样有diffuse和ambient颜色,它们在整个车上都不相同;一辆车显示了多种不同的ambient/diffuse颜色。总之,这样一个物体每个部分都有多种材质属性。
 所以,前面的材质系统对于除了最简单的模型以外都是不够的,所以我们需要扩展前面的系统,我们要介绍diffuse和specular贴图。它们允许你对一个物体的diffuse(而对于简洁的ambient成分来说,它们几乎总是是一样的)和specular成分能够有更精确的影响。

漫反射贴图

 我们希望通过某种方式对每个原始像素独立设置diffuse颜色。有可以让我们基于物体原始像素的位置来获取颜色值的系统吗?
 这可能听起来极其相似,坦白来讲我们使用这样的系统已经有一段时间了。听起来很像在一个之前的教程中谈论的纹理,它基本就是一个纹理。我们其实是使用同一个潜在原则下的不同名称:使用一张图片覆盖住物体,以便我们为每个原始像素索引独立颜色值。在光照场景中,通过纹理来呈现一个物体的diffuse颜色,这个做法被称做漫反射贴图(Diffuse texture)(因为3D建模师就是这么称呼这个做法的)。
 为了演示漫反射贴图,我们将会使用下面的图片,它是一个有一圈钢边的木箱:
在这里插入图片描述
 在着色器中使用漫反射贴图和纹理教程介绍的一样。这次我们把纹理以sampler2D类型储存在Material结构体中。我们使用diffuse贴图替代早期定义的vec3类型的diffuse颜色。
 要记住的是sampler2D也叫做模糊类型,这意味着我们不能以某种类型对它实例化,只能用uniform定义它们。如果我们用结构体而不是uniform实例化(就像函数的参数那样),GLSL会抛出奇怪的错误;这同样也适用于其他模糊类型。
 我们也要移除amibient材质颜色向量,因为ambient颜色绝大多数情况等于diffuse颜色,所以不需要分别去储存它:

struct Material
{sampler2D diffuse;vec3 specular;float shininess;
};
...
in vec2 TexCoords;

 如果你非把ambient颜色设置为不同的值不可(不同于diffuse值),你可以继续保留ambient的vec3,但是整个物体的ambient颜色会继续保持不变。为了使每个原始像素得到不同ambient值,你需要对ambient值单独使用另一个纹理。

vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

 同样,不要忘记把ambient材质的颜色设置为diffuse材质的颜色:

vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

 更新的顶点数据可以从这里找到。顶点数据现在包括了顶点位置,法线向量和纹理坐标,每个立方体的顶点都有这些属性。让我们更新顶点着色器来接受纹理坐标作为顶点属性,然后发送到片段着色器:

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoords;
...
out vec2 TexCoords;void main()
{...TexCoords = texCoords;
}

 要保证更新的顶点属性指针,不仅是VAO匹配新的顶点数据,也要把箱子图片加载为纹理。在绘制箱子之前,我们希望首选纹理单元被赋为material.diffuse这个uniform采样器,并绑定箱子的纹理到这个纹理单元:

glUniform1i(glGetUniformLocation(lightingShader.Program, "material.diffuse"), 0);
...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);

 在,使用一个diffuse贴图,我们在细节上再次获得惊人的提升,这次添加到箱子上的光照开始闪光了(名符其实)。你的箱子现在可能看起来像这样:
在这里插入图片描述)

完整代码

 顶点着色器:

#version 330 core
layout( location = 0 ) in vec3 position;
layout( location = 1 ) in vec3 normal;
layout( location = 2 ) in vec2 texCoords;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat3 normatr;out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;void main()
{gl_Position = projection * view * model * vec4(position, 1.0f);FragPos = vec3(model * vec4(position, 1.0f));Normal  = normatr * normal;TexCoords = texCoords; 
}

 片段着色器:

#version 330 core
struct Material
{sampler2D diffuse;vec3 specular;float shininess;
};
uniform Material material;struct Light
{vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
};
uniform Light light;// 输入顶点位置和法向量
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;// 输出顶点的颜色
out vec4 color;// 顶点本身的颜色
uniform vec3 objectColor;
// 光源的颜色和位置
uniform vec3 lightColor;
uniform vec3 lightPos;
// 观察者的位置(世界坐标)
uniform vec3 viewPos;void main()
{// 环境光vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));// 漫反射光vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));// 镜面高光vec3 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 result = ambient + diffuse + specular;color = vec4(result, 1.0f);
}

 主程序:

// 标准输出
#include <string>// GLEW
#define GLEW_STATIC
#include <GL/glew.h>// GLFW
#include <GLFW/glfw3.h>// 着色器类和摄像机类
#include "Shader.h"
#include "Camera.h"// GLM 数学库
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>// 图片数据读取库
#include <SOIL.h>// 定义窗口大小
GLuint screenWidth = 800, screenHeight = 600;// GLFW窗口需要注册的函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
// 处理摄像机位置移动的函数
void Do_Movement();// 定义摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
// 定义数组存储按键信息
bool keys[1024];
// 
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;glm::vec3 lightPos(1.2f, 1.0f, -0.5f);// The MAIN function, from here we start our application and run our Game loop
int main()
{// 初始化GLFW glfwInit();// 设置glfw使用的OpenGL版本glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);// 设置使用OpenGL的核心模式glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// 设置窗口大小可改变性 为不可变glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// GLFW会自动创建一个每像素4个子采样点的深度和样本缓冲。这也意味着所有缓冲的大小都增长了4倍。glfwWindowHint(GLFW_SAMPLES, 4);// 创建GLFW的窗口GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr);// 设置window窗口线程为主线程glfwMakeContextCurrent(window);// 注册窗口的事件glfwSetKeyCallback(window, key_callback);         // 按键检测glfwSetCursorPosCallback(window, mouse_callback); // 鼠标移动检测 glfwSetScrollCallback(window, scroll_callback);   // 滚轮滑动检测// 设置隐藏光标模式glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);// 设置GLEW为更现代的模式glewExperimental = GL_TRUE;// 初始化GLEWglewInit();// 设置视口的位置和大小(位置是相对于窗口左下角的)glViewport(0, 0, screenWidth, screenHeight);// 开启深度测试功能glEnable(GL_DEPTH_TEST);// 根据顶点着色器和片段着色器位置创建着色器程序Shader lightingShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\vertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\fragmentShader.txt");Shader lampShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightVertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightFragmentShader.txt");// 顶点数据(位置+纹理坐标)GLfloat 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};// 加载图片int width, height;unsigned char* image;image = SOIL_load_image("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Resource\\container2.png", &width, &height, 0, SOIL_LOAD_RGB);// 将图片绑定为纹理GLuint diffuseMap;glGenTextures(1, &diffuseMap);glBindTexture(GL_TEXTURE_2D, diffuseMap);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);  glGenerateMipmap(GL_TEXTURE_2D);    //为当前绑定的纹理自动生成所有需要的多级渐远纹理SOIL_free_image_data(image);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_NEAREST_MIPMAP_NEAREST);glBindTexture(GL_TEXTURE_2D, 0);// 设置VBO和VAOGLuint VBO, VAO;// 先申请VAO和VBO的显存glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);// 绑定VAOglBindVertexArray(VAO);// 绑定VBOglBindBuffer(GL_ARRAY_BUFFER, VBO);// 将顶点数据传输到显存中glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 位置数据解析glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));glEnableVertexAttribArray(2);glBindVertexArray(0); // 解绑VAOGLuint lightVAO;glGenVertexArrays(1, &lightVAO);glBindVertexArray(lightVAO);// 只需要绑定VBO不用再次设置VBO的数据,因为容器(物体)的VBO数据中已经包含了正确的立方体顶点数据glBindBuffer(GL_ARRAY_BUFFER, VBO);// 设置灯立方体的顶点属性指针(仅设置灯的顶点数据)glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glBindVertexArray(0);// 游戏循环while (!glfwWindowShouldClose(window)){// 计算帧相差时间GLfloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;// 窗口的事件检测glfwPollEvents();// 根据事件移动摄像机Do_Movement();// 设置清空屏幕颜色缓存所使用颜色glClearColor(0.1f, 0.1f, 0.1f, 1.0f);// 清空 屏幕颜色缓存、深度缓存glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 被光源照亮的普通物体的着色器lightingShader.Use();GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");GLint lightColorLoc = glGetUniformLocation(lightingShader.Program, "lightColor");GLint lightPosLoc = glGetUniformLocation(lightingShader.Program, "lightPos");GLint viewPosLoc = glGetUniformLocation(lightingShader.Program, "viewPos");glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);// 我们所熟悉的珊瑚红glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // 依旧把光源设置为白色glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);// 创建观察矩阵( 只要创建矩阵记得初始化= glm::mat4(1.0f) )glm::mat4 view = glm::mat4(1.0f);// 获取观察矩阵(根据摄像机的状态)view = camera.GetViewMatrix();// 创建投影矩阵glm::mat4 projection = glm::mat4(1.0f);// 计算投影矩阵(fov视野为摄像机的属性camera.Zoom)projection = glm::perspective(camera.Zoom, (float)screenWidth / (float)screenHeight, 0.1f, 1000.0f);// 计算顶点着色器中矩阵的位置值GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");GLint viewLoc = glGetUniformLocation(lightingShader.Program, "view");GLint projLoc = glGetUniformLocation(lightingShader.Program, "projection");// 将观察矩阵和投影矩阵传入对应的位置(记得转换)glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));// 激活纹理单元并绑定纹理glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, diffuseMap);// 绑定VAOglBindVertexArray(VAO);   // 计算模型矩阵glm::mat4 model = glm::mat4(1.0f);glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));// 根据模型矩阵model计算正规矩阵normatr。inverse取逆和transpose转置 函数使用glm对应函数GLuint normalLoc = glGetUniformLocation(lightingShader.Program, "normatr");glm::mat3 normatr = (glm::mat3)(glm::transpose(glm::inverse(model)));// 向物体的顶点着色器传入正规矩阵normatrglUniformMatrix3fv(normalLoc, 1, GL_FALSE, glm::value_ptr(normatr));// 设置材质GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");glUniform3f(matAmbientLoc, 0.135f, 0.2225f, 0.1575f);glUniform3f(matDiffuseLoc, 0.54f, 0.89f, 0.63f);glUniform3f(matSpecularLoc, 0.316228f, 0.316228f, 0.316228f);glUniform1f(matShineLoc, 32.0f);// 设置光源属性GLint lightAmbientLoc = glGetUniformLocation(lightingShader.Program, "light.ambient");GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diffuse");GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");glUniform3f(glGetUniformLocation(lightingShader.Program, "light.ambient"), 0.2f, 0.2f, 0.2f);glUniform3f(glGetUniformLocation(lightingShader.Program, "light.diffuse"), 0.5f, 0.5f, 0.5f);glUniform3f(glGetUniformLocation(lightingShader.Program, "light.specular"), 1.0f, 1.0f, 1.0f);// Set material propertiesglUniform3f(glGetUniformLocation(lightingShader.Program, "material.specular"), 0.5f, 0.5f, 0.5f);glUniform1f(glGetUniformLocation(lightingShader.Program, "material.shininess"), 64.0f);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);// 使用光源着色器lampShader.Use();modelLoc = glGetUniformLocation(lampShader.Program, "model");viewLoc = glGetUniformLocation(lampShader.Program, "view");projLoc = glGetUniformLocation(lampShader.Program, "projection");glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));model = glm::mat4(1.0f);GLfloat lightX = sin(glfwGetTime());GLfloat lightZ = cos(glfwGetTime());GLfloat lightY = 1;lightPos = glm::vec3(lightX, lightY, lightZ);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.1f)); // Make it a smaller cubeglUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));glBindVertexArray(lightVAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);// 交换前后缓冲区glfwSwapBuffers(window);}// 释放VAO和VBO的显存glDeleteVertexArrays(1, &VAO);glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &VBO);// 释放glfw的内存glfwTerminate();return 0;
}// 根据按键信息移动摄像机
void Do_Movement()
{// 如果某个键按下,就执行摄像机对应的方法(更新摄像机的位置)if (keys[GLFW_KEY_W])camera.ProcessKeyboard(FORWARD, deltaTime);if (keys[GLFW_KEY_S])camera.ProcessKeyboard(BACKWARD, deltaTime);if (keys[GLFW_KEY_A])camera.ProcessKeyboard(LEFT, deltaTime);if (keys[GLFW_KEY_D])camera.ProcessKeyboard(RIGHT, deltaTime);
}// 按键回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{// 如果按下ESE则设置窗口应该关闭if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);if (key >= 0 && key < 1024){// 如果按下某一个键,则设置其keys为true,如果松开则设置回falseif (action == GLFW_PRESS)keys[key] = true;else if (action == GLFW_RELEASE)keys[key] = false;}
}// 鼠标移动的回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{// 第一次进窗口鼠标坐标很大,所以第一次调用函数我们需要把它设置为一个正常的值if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}// 计算鼠标在竖直方向和水平方向的偏移(屏幕坐标系往右x大,但往下是y大,所以运算顺序不同)GLfloat xoffset = xpos - lastX;GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left// 更新鼠标的坐标lastX = xpos;lastY = ypos;// 调用摄像机的鼠标移动函数(根据位置偏移更新摄像机方式)camera.ProcessMouseMovement(xoffset, yoffset);
}// 鼠标滚动的回调函数
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{// 调用摄像机的鼠标滚动函数(根据位置偏移更新摄像机fov视野大小)camera.ProcessMouseScroll(yoffset);
}

镜面贴图

 你可能注意到,specular高光看起来不怎么样,由于我们的物体是个箱子,大部分是木头,我们知道木头是不应该有镜面高光的。我们通过把物体设置specular材质设置为vec3(0.0f)来修正它。但是这样意味着铁边会不再显示镜面高光,我们知道钢铁是会显示一些镜面高光的。我们会想要控制物体部分地显示镜面高光,它带有修改了的亮度。这个问题看起来和diffuse贴图的讨论一样。这是巧合吗?我想不是。
 我们同样用一个纹理贴图,来获得镜面高光。这意味着我们需要生成一个黑白(或者你喜欢的颜色)纹理来定义specular亮度,把它应用到物体的每个部分。下面是一个镜面贴图(Specular Map)的例子:
在这里插入图片描述)
 一个specular高光的亮度可以通过图片中每个纹理的亮度来获得。specular贴图的每个像素可以显示为一个颜色向量,比如:在那里黑色代表颜色向量vec3(0.0f),灰色是vec3(0.5f)。在片段着色器中,我们采样相应的颜色值,把它乘以光的specular亮度。像素越“白”,乘积的结果越大,物体的specualr部分越亮。
 由于箱子几乎是由木头组成,木头作为一个材质不会有镜面高光,整个木头部分的diffuse纹理被用黑色覆盖:黑色部分不会包含任何specular高光。箱子的铁边有一个修改的specular亮度,它自身更容易受到镜面高光影响,木纹部分则不会。
 从技术上来讲,木头也有镜面高光,尽管这个闪亮值很小(更多的光被散射),影响很小,但是为了学习目的,我们可以假装木头不会有任何specular光反射。
 使用Photoshop或Gimp之类的工具,通过将图片进行裁剪,将某部分调整成黑白图样,并调整亮度/对比度的做法,可以非常容易将一个diffuse纹理贴图处理为specular贴图。

镜面贴图采样

 一个specular贴图和其他纹理一样,所以代码和diffuse贴图的代码也相似。确保合理的加载了图片,生成一个纹理对象。由于我们在同样的片段着色器中使用另一个纹理采样器,我们必须为specular贴图使用一个不同的纹理单元(参见纹理),所以在渲染前让我们把它绑定到合适的纹理单元

glUniform1i(glGetUniformLocation(lightingShader.Program, "material.specular"), 1);
...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);

然后更新片段着色器材质属性,接受一个sampler2D作为这个specular部分的类型,而不是vec3:

struct Material
{sampler2D diffuse;sampler2D specular;float shininess;
};

 最后我们希望采样这个specular贴图,来获取原始像素相应的specular亮度:

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));
color = vec4(ambient + diffuse + specular, 1.0f);

 通过使用一个specular贴图我们可以定义极为精细的细节,物体的这个部分会获得闪亮的属性,我们可以设置它们相应的亮度。specular贴图给我们一个附加的高于diffuse贴图的控制权限。
 如果你不想成为主流,你可以在specular贴图里使用颜色,不单单为每个原始像素设置specular亮度,同时也设置specular高光的颜色。从真实角度来说,specular的颜色基本是由光源自身决定的,所以它不会生成真实的图像(这就是为什么图片通常是黑色和白色的:我们只关心亮度)。
 如果你现在运行应用,你可以清晰地看到箱子的材质现在非常类似真实的铁边的木头箱子了。
 运行结果:
在这里插入图片描述)
 使用diffuse和specular贴图,我们可以给相关但简单物体添加一个极为明显的细节。我们可以使用其他纹理贴图,比如法线/bump贴图或者反射贴图,给物体添加更多的细节。但是这些在后面教程才会涉及。把你的箱子给你所有的朋友和家人看,有一天你会很满足,我们的箱子会比现在更漂亮!

练习

 增强环境光照和镜面反射的效果,即增大ambient、diffuse的值。效果如下所示:
在这里插入图片描述)
 尝试在片段着色器中反转镜面贴图(Specular Map)的颜色值,然后木头就会变得反光而边框不会反光了:
在这里插入图片描述)
 使用漫反射纹理(Diffuse Texture)原本的颜色而不是黑白色来创建镜面贴图,并观察,你会发现结果显得并不那么真实了。原因是高光并不会显现出一个光源圈,而是使得高光处的物体更亮(不该这样,因为高光本就是光滑表面映射出光源)。运行结果:
在这里插入图片描述)
 加一个叫做放射光贴图(Emission Map)的东西,即记录每个片段发光值(Emission Value)大小的贴图,发光值是(模拟)物体自身发光(Emit)时可能产生的颜色。这样的话物体就可以忽略环境光自身发光。通常在你看到游戏里某个东西(比如 机器人的眼,或是箱子上的小灯)在发光时,使用的就是放射光贴图。使用这个贴图(作者为 creativesam)作为放射光贴图并使用在箱子上,你就会看到箱子上有会发光的字了。
在这里插入图片描述)
 游戏里的箱子:
在这里插入图片描述)
 由于游戏中箱子上的灯光一定会亮,即不依赖于其他光源的存在,所以将其箱子上发光的地方设为放射光(自身发光)。至于放射光贴图灯以外的地方可以设为和反射光贴图一样。它之所以要独立出来作为放射光贴图,是因为它不像反射光需要依赖其他光源照射,它是自发性的,和环境光本质一样,所以使用环境光实现。

完整代码

 片段着色器:

#version 330 core
struct Material
{sampler2D diffuse;sampler2D specular;sampler2D emission;float shininess;
};
uniform Material material;struct Light
{vec3 position;vec3 ambient;vec3 diffuse;vec3 specular;
};
uniform Light light;// 输入顶点位置和法向量
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;// 输出顶点的颜色
out vec4 color;// 顶点本身的颜色
uniform vec3 objectColor;
// 光源的颜色和位置
uniform vec3 lightColor;
uniform vec3 lightPos;
// 观察者的位置(世界坐标)
uniform vec3 viewPos;void main()
{// 环境光vec3 ambient = light.ambient * vec3(texture(material.emission, TexCoords));// 漫反射光vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));// 镜面高光vec3 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));  vec3 result = ambient + diffuse + specular;color = vec4(result, 1.0f);
}

 主程序:

// 标准输出
#include <string>// GLEW
#define GLEW_STATIC
#include <GL/glew.h>// GLFW
#include <GLFW/glfw3.h>// 着色器类和摄像机类
#include "Shader.h"
#include "Camera.h"// GLM 数学库
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>// 图片数据读取库
#include <SOIL.h>// 定义窗口大小
GLuint screenWidth = 800, screenHeight = 600;// GLFW窗口需要注册的函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
// 处理摄像机位置移动的函数
void Do_Movement();// 定义摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
// 定义数组存储按键信息
bool keys[1024];
// 
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;glm::vec3 lightPos(1.2f, 1.0f, -0.5f);// The MAIN function, from here we start our application and run our Game loop
int main()
{// 初始化GLFW glfwInit();// 设置glfw使用的OpenGL版本glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);// 设置使用OpenGL的核心模式glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// 设置窗口大小可改变性 为不可变glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// GLFW会自动创建一个每像素4个子采样点的深度和样本缓冲。这也意味着所有缓冲的大小都增长了4倍。glfwWindowHint(GLFW_SAMPLES, 4);// 创建GLFW的窗口GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr);// 设置window窗口线程为主线程glfwMakeContextCurrent(window);// 注册窗口的事件glfwSetKeyCallback(window, key_callback);         // 按键检测glfwSetCursorPosCallback(window, mouse_callback); // 鼠标移动检测 glfwSetScrollCallback(window, scroll_callback);   // 滚轮滑动检测// 设置隐藏光标模式glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);// 设置GLEW为更现代的模式glewExperimental = GL_TRUE;// 初始化GLEWglewInit();// 设置视口的位置和大小(位置是相对于窗口左下角的)glViewport(0, 0, screenWidth, screenHeight);// 开启深度测试功能glEnable(GL_DEPTH_TEST);// 根据顶点着色器和片段着色器位置创建着色器程序Shader lightingShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\vertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\fragmentShader.txt");Shader lampShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightVertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightFragmentShader.txt");// 顶点数据(位置+纹理坐标)GLfloat 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};// 加载图片int width, height;unsigned char* image;image = SOIL_load_image("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Resource\\container2.png", &width, &height, 0, SOIL_LOAD_RGB);// 将图片绑定为纹理GLuint diffuseMap;glGenTextures(1, &diffuseMap);glBindTexture(GL_TEXTURE_2D, diffuseMap);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);  glGenerateMipmap(GL_TEXTURE_2D);    //为当前绑定的纹理自动生成所有需要的多级渐远纹理SOIL_free_image_data(image);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_NEAREST_MIPMAP_NEAREST);glBindTexture(GL_TEXTURE_2D, 0);image = SOIL_load_image("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Resource\\container2_specular.png", &width, &height, 0, SOIL_LOAD_RGB);GLuint specularMap;glGenTextures(1, &specularMap);glBindTexture(GL_TEXTURE_2D, specularMap);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);    //为当前绑定的纹理自动生成所有需要的多级渐远纹理SOIL_free_image_data(image);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_NEAREST_MIPMAP_NEAREST);glBindTexture(GL_TEXTURE_2D, 0);image = SOIL_load_image("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Resource\\matrix.jpg", &width, &height, 0, SOIL_LOAD_RGB);GLuint emissionMap;glGenTextures(1, &emissionMap);glBindTexture(GL_TEXTURE_2D, emissionMap);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);glGenerateMipmap(GL_TEXTURE_2D);    //为当前绑定的纹理自动生成所有需要的多级渐远纹理SOIL_free_image_data(image);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_NEAREST_MIPMAP_NEAREST);glBindTexture(GL_TEXTURE_2D, 0);// 设置VBO和VAOGLuint VBO, VAO;// 先申请VAO和VBO的显存glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);// 绑定VAOglBindVertexArray(VAO);// 绑定VBOglBindBuffer(GL_ARRAY_BUFFER, VBO);// 将顶点数据传输到显存中glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 位置数据解析glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));glEnableVertexAttribArray(2);glBindVertexArray(0); // 解绑VAOGLuint lightVAO;glGenVertexArrays(1, &lightVAO);glBindVertexArray(lightVAO);// 只需要绑定VBO不用再次设置VBO的数据,因为容器(物体)的VBO数据中已经包含了正确的立方体顶点数据glBindBuffer(GL_ARRAY_BUFFER, VBO);// 设置灯立方体的顶点属性指针(仅设置灯的顶点数据)glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glBindVertexArray(0);// 游戏循环while (!glfwWindowShouldClose(window)){// 计算帧相差时间GLfloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;// 窗口的事件检测glfwPollEvents();// 根据事件移动摄像机Do_Movement();// 设置清空屏幕颜色缓存所使用颜色glClearColor(0.1f, 0.1f, 0.1f, 1.0f);// 清空 屏幕颜色缓存、深度缓存glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 被光源照亮的普通物体的着色器lightingShader.Use();GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");GLint lightColorLoc = glGetUniformLocation(lightingShader.Program, "lightColor");GLint lightPosLoc = glGetUniformLocation(lightingShader.Program, "lightPos");GLint viewPosLoc = glGetUniformLocation(lightingShader.Program, "viewPos");glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);// 我们所熟悉的珊瑚红glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // 依旧把光源设置为白色glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);// 创建观察矩阵( 只要创建矩阵记得初始化= glm::mat4(1.0f) )glm::mat4 view = glm::mat4(1.0f);// 获取观察矩阵(根据摄像机的状态)view = camera.GetViewMatrix();// 创建投影矩阵glm::mat4 projection = glm::mat4(1.0f);// 计算投影矩阵(fov视野为摄像机的属性camera.Zoom)projection = glm::perspective(camera.Zoom, (float)screenWidth / (float)screenHeight, 0.1f, 1000.0f);// 计算顶点着色器中矩阵的位置值GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");GLint viewLoc = glGetUniformLocation(lightingShader.Program, "view");GLint projLoc = glGetUniformLocation(lightingShader.Program, "projection");// 将观察矩阵和投影矩阵传入对应的位置(记得转换)glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));// 绑定VAOglBindVertexArray(VAO);   // 计算模型矩阵glm::mat4 model = glm::mat4(1.0f);glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));// 根据模型矩阵model计算正规矩阵normatr。inverse取逆和transpose转置 函数使用glm对应函数GLuint normalLoc = glGetUniformLocation(lightingShader.Program, "normatr");glm::mat3 normatr = (glm::mat3)(glm::transpose(glm::inverse(model)));// 向物体的顶点着色器传入正规矩阵normatrglUniformMatrix3fv(normalLoc, 1, GL_FALSE, glm::value_ptr(normatr));// 设置材质GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");glUniform3f(matAmbientLoc, 0.135f, 0.2225f, 0.1575f);glUniform3f(matDiffuseLoc, 0.54f, 0.89f, 0.63f);glUniform3f(matSpecularLoc, 0.316228f, 0.316228f, 0.316228f);glUniform1f(matShineLoc, 32.0f);// 设置光源属性GLint lightAmbientLoc = glGetUniformLocation(lightingShader.Program, "light.ambient");GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diffuse");GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");// 激活纹理单元并绑定纹理glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, diffuseMap);glUniform1i(glGetUniformLocation(lightingShader.Program, "material.specular"), 1);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, specularMap);glUniform1i(glGetUniformLocation(lightingShader.Program, "material.emission"), 2);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, emissionMap);glUniform3f(glGetUniformLocation(lightingShader.Program, "light.ambient"), 0.3f, 0.3f, 0.3f);glUniform3f(glGetUniformLocation(lightingShader.Program, "light.diffuse"), 0.7f, 0.7f, 0.7f);glUniform3f(glGetUniformLocation(lightingShader.Program, "light.specular"), 1.0f, 1.0f, 1.0f);// Set material propertiesglUniform3f(glGetUniformLocation(lightingShader.Program, "material.specular"), 0.5f, 0.5f, 0.5f);glUniform1f(glGetUniformLocation(lightingShader.Program, "material.shininess"), 64.0f);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);// 使用光源着色器lampShader.Use();modelLoc = glGetUniformLocation(lampShader.Program, "model");viewLoc = glGetUniformLocation(lampShader.Program, "view");projLoc = glGetUniformLocation(lampShader.Program, "projection");glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));model = glm::mat4(1.0f);GLfloat lightX = sin(glfwGetTime());GLfloat lightZ = cos(glfwGetTime());GLfloat lightY = 1;lightPos = glm::vec3(lightX, lightY, lightZ);model = glm::translate(model, lightPos);model = glm::scale(model, glm::vec3(0.1f)); // Make it a smaller cubeglUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));glBindVertexArray(lightVAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);// 交换前后缓冲区glfwSwapBuffers(window);}// 释放VAO和VBO的显存glDeleteVertexArrays(1, &VAO);glDeleteVertexArrays(1, &lightVAO);glDeleteBuffers(1, &VBO);// 释放glfw的内存glfwTerminate();return 0;
}// 根据按键信息移动摄像机
void Do_Movement()
{// 如果某个键按下,就执行摄像机对应的方法(更新摄像机的位置)if (keys[GLFW_KEY_W])camera.ProcessKeyboard(FORWARD, deltaTime);if (keys[GLFW_KEY_S])camera.ProcessKeyboard(BACKWARD, deltaTime);if (keys[GLFW_KEY_A])camera.ProcessKeyboard(LEFT, deltaTime);if (keys[GLFW_KEY_D])camera.ProcessKeyboard(RIGHT, deltaTime);
}// 按键回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{// 如果按下ESE则设置窗口应该关闭if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);if (key >= 0 && key < 1024){// 如果按下某一个键,则设置其keys为true,如果松开则设置回falseif (action == GLFW_PRESS)keys[key] = true;else if (action == GLFW_RELEASE)keys[key] = false;}
}// 鼠标移动的回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{// 第一次进窗口鼠标坐标很大,所以第一次调用函数我们需要把它设置为一个正常的值if (firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}// 计算鼠标在竖直方向和水平方向的偏移(屏幕坐标系往右x大,但往下是y大,所以运算顺序不同)GLfloat xoffset = xpos - lastX;GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left// 更新鼠标的坐标lastX = xpos;lastY = ypos;// 调用摄像机的鼠标移动函数(根据位置偏移更新摄像机方式)camera.ProcessMouseMovement(xoffset, yoffset);
}// 鼠标滚动的回调函数
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{// 调用摄像机的鼠标滚动函数(根据位置偏移更新摄像机fov视野大小)camera.ProcessMouseScroll(yoffset);
}

http://www.ppmy.cn/news/62205.html

相关文章

Communications chemisty|德睿智药工作-用于分子性质预测的药物约束异构图Transformer模型

德睿智药的分子性质预测任务 题目&#xff1a; Pharmacophoric-constrained heterogeneous graph transformer model for molecular property prediction 文献来源&#xff1a;COMMUNICATIONS CHEMISTRY | (2023) 6:60 | 代码&#xff1a;https://github.com/stardj/PharmHG…

2023年全国最新安全员精选真题及答案67

百分百题库提供安全员考试试题、建筑安全员考试预测题、建筑安全员ABC考试真题、安全员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 61.总干线的漏电保护器&#xff08;即总配电箱的漏电保护器&#xff09;&#xff0c;其…

【Unity项目实战】手把手教学:飞翔的小鸟(6)添加障碍

承接上一篇&#xff1a;【Unity项目实战】手把手教学&#xff1a;飞翔的小鸟&#xff08;5&#xff09;背景滚动&#xff0c;我们已经让主角在停止不动的情况下&#xff0c;移动背景图&#xff0c;使得主角小鸟像是自己往前移动了一样&#xff0c;接下来我们将继续往下&#xf…

Netty基础(三)

1.Netty模型 1.1.工作原理示意图1-简单版 Netty主要基于主从Reactors多线程模型(如图)做了一定的改进,其中主从Reactor多线程模型有多个Reactor; 说明: ①.BossGroup线程池中的线程维护了Selector,只关注Accecpt事件; ②.当接收到Accept事件,获取到连接对应的SocketChannel…

【Linux下】进程间通信

文章目录 进程间通信进程间通信的目的进程间通信的分类进程间通信的本质 管道初识管道匿名管道创建匿名管道理解协同机制和原子性写入 命名管道命名管道创建的俩种方式使用命名管道实现俩个不同进程之间通信 **管道实现进程间通信的本质**匿名管道vs命名管道 system Vsystem V共…

电子招标采购系统源码之什么是电子招投标系统?

随着互联网时代的到来&#xff0c;各行业都受到不同的影响&#xff0c;其中招投标行业也不例外。为了顺应互联网潮流的发展&#xff0c;电子招投标逐渐取代传统的纸质的招投标方式&#xff0c;给招标方、投标方、招标代理等各方也带来了前所未有的机遇与挑战。那么什么是电子招…

Ceph入门到精通-OSD 故障排除

OSD 故障排除 在对 OSD 进行故障排除之前,请先检查您的显示器和网络。如果你在命令行上 执行or并且 Ceph 显示,这意味着监视器有法定人数。如果您没有监视器法定人数或者如果监视器状态有错误,请首先解决监视器问题。检查您的网络以确保它们正常运行,因为网络可能会对 O…

【ArcGIS Pro二次开发】(25):属性映射

属性映射经常用于属性表或Excel表的赋值&#xff0c;比如按用地用海表对规划用地的用地编码或用地名称赋值&#xff0c;将汇总好的用地指标表赋值给已经制好的Excel模板等。 下面试着在ArcGIS Pro SDK中实现功能上述这两个功能。 一、Excel表格映射到属性表Table 1、要实现的…