• 友链

  • 首页

  • 文章归档
h u a n b l o g
h u a n b l o g

欢

HI,Friend

05月
20
Shader
C++

OpenGL笔记9-材质

发表于 2024-05-20 • 字数统计 23240 • 被 2,281 人看爆

序言

以glew、glfw库
OpenGL学习网站
glfw官网
OpenGL-API文档
glew官网

材质

在真实世界里,每个物体会对光产生不同的反应。钢看起来比陶瓷花瓶更闪闪发光,一个木头箱子不会像钢箱子一样对光产生很强的反射。每个物体对镜面高光也有不同的反应。有些物体不会散射(Scatter)很多光却会反射(Reflect)很多光,结果看起来就有一个较小的高光点(Highlight),有些物体散射了很多,它们就会产生一个半径更大的高光。

在前面的OpenGL笔记8-光照中,我们指定一个物体和一个光的颜色来定义物体的图像输出,并使之结合环境(Ambient)和镜面强度(Specular Intensity)元素。当描述物体的时候,我们可以使用3种光照元素:**环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)、镜面光照(Specular Lighting)**定义一个材质颜色。通过为每个元素指定一个颜色,我们已经对物体的颜色输出有了精密的控制。现在把一个镜面高光元素添加到这三个颜色里,这是我们需要的所有材质属性:

片段着色器

#version 330 core
struct Material
{
    vec3 ambient;       //环境光下反射什么颜色 
    vec3 diffuse;       //漫反射光照下物体的颜色
    vec3 specular;      //受到镜面反射的颜色
    float shininess;    //镜面反射的散射/半径
};
uniform Material material;

在片段着色器中,我们创建一个结构体(Struct),来储存物体的材质属性。我们也可以把它们储存为独立的uniform值,但是作为一个结构体来储存可以更有条理。我们首先定义结构体的布局,然后简单声明一个uniform变量,以新创建的结构体作为它的类型。

就像你所看到的,我们为每个冯氏光照模型的元素都定义一个颜色向量。ambient材质向量定义了在环境光照下这个物体反射的是什么颜色;通常这是和物体颜色相同的颜色。diffuse材质向量定义了在漫反射光照下物体的颜色。漫反射颜色被设置为(和环境光照一样)我们需要的物体颜色。specular材质向量设置的是物体受到的镜面光照的影响的颜色(或者可能是反射一个物体特定的镜面高光颜色)。最后,shininess影响镜面高光的散射/半径。

这四个元素定义了一个物体的材质,通过它们我们能够模拟很多真实世界的材质。这里有一个列表devernay.free.fr展示了几种材质属性,这些材质属性模拟外部世界的真实材质。下面的图片展示了几种真实世界材质对我们的立方体的影响:
几种真实世界材质对我们的立方体的影响.png

如你所见,正确地指定一个物体的材质属性,似乎就是改变我们物体的相关属性的比例。效果显然很引人注目,但是对于大多数真实效果,我们最终需要更加复杂的形状,而不单单是一个立方体。

设置材质

我们在片段着色器中创建了一个uniform材质结构体,所以下面我们希望改变光照计算来顺应新的材质属性。由于所有材质元素都储存在结构体中,我们可以从uniform变量material取得它们:

片段着色器

void main()
{
    // 环境光
    vec3 ambient = lightColor * material.ambient;

    // 漫反射光
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = lightColor * (diff * material.diffuse);

    // 镜面高光
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = lightColor * (spec * material.specular);  

    //光照
    vec3 result = ambient + diffuse + specular;
    color = vec4(result, 1.0f);
}

你可以看到,我们现在获得所有材质结构体的属性,无论在哪儿我们都需要它们,这次通过材质颜色的帮助,计算结果输出的颜色。物体的每个材质属性都乘以它们对应的光照元素。

通过设置适当的uniform,我们可以在应用中设置物体的材质。当设置uniform时,GLSL中的一个结构体并不会被认为有什么特别之处。一个结构体值扮演uniform变量的封装体,所以如果我们希望填充这个结构体,我们就仍然必须设置结构体中的各个元素的uniform值,但是这次带有结构体名字作为前缀:

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, 1.0f, 0.5f, 0.31f);
glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
glUniform1f(matShineLoc, 32.0f);

我们将ambient和diffuse元素设置成我们想要让物体所呈现的颜色,设置物体的specular元素为中等亮度颜色;我们不希望specular元素对这个指定物体产生过于强烈的影响。我们同样设置shininess为32。我们现在可以简单的在应用中影响物体的材质。

添加材质.png

看起来很奇怪不是吗?

完整代码

#include <glew.h>
#include <glfw3.h>
#include <iostream>
#include <SOIL2.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

//顶点着色器
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec3 normal;\n"

"out vec3 Normal;\n"            //法向量
"out vec3 FragPos;\n"           //片段位置(如立方体六个面,每个面两个三角形,三角形形成的那个片段)

"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"

"void main()\n"
"{\n"
"   gl_Position = projection * view * model * vec4(position, 1.0f);\n"       //数学库矩阵转换
"   FragPos = vec3(model * vec4(position, 1.0f));\n"               //片段位置,将片段位置转化为世界坐标
"   Normal = normal;\n"
"}\n";

//片段着色器
const char* fragmentShaderSource = "#version 330 core\n"
"in vec3 Normal;\n"
"in vec3 FragPos;\n"

"out vec4 color;\n"

"uniform vec3 objectColor;\n"
"uniform vec3 lightColor;\n"
"uniform vec3 lightPos;\n"
"uniform vec3 viewPos;\n"           //摄像机位置

//材质属性
"struct Material\n"
"{\n"
"     vec3 ambient;\n"          //环境光
"     vec3 diffuse;\n"          //在漫反射光照下物体的颜色
"     vec3 specular;\n"         //受到镜面反射的颜色
"     float shininess;\n"       //反射半径
"};\n"
"uniform Material material;\n"

"void main()\n"
"{\n"

      //环境光
"     vec3 ambient = material.ambient * lightColor;\n"       //环境光

      //漫反射
"     vec3 norm = normalize(Normal);\n"         //法向量单位化
"     vec3 lightDir = normalize(lightPos - FragPos);\n"        //计算光的方向,即方向向量  指向光源方向lightPos, 朝向片段方向FragPos
"     float diff = max(dot(norm, lightDir), 0.0);\n"           //计算光对当前片段的实际散射影响
"     vec3 diffuse = (diff * material.diffuse) * lightColor;\n"          //得到散射因子
        
      //镜面高光
"     vec3 viewDir = normalize(viewPos - FragPos);\n"     //指向摄像机viewPos,朝向FragPos
"     vec3 reflectDir = reflect(-lightDir, norm);\n"      //反射的方向
"     float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);\n"
"     vec3 specular = (material.specular * spec) * lightColor;\n"  //计算镜面分量

      //光照
"     vec3 result = ambient + diffuse + specular;\n"   //光照(冯氏光照模型) = (环境光 + 漫反射 + 镜面反射) * 物体颜色
"     color = vec4(result, 1.0f) ;\n"
"}\n";

//光源-顶点着色器
const char* lightVertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"


"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"

"void main()\n"
"{\n"
"   gl_Position = projection * view * model * vec4(position, 1.0f);\n"       //数学库矩阵转换
"}\n";

//光源-片段着色器
const char* lightFragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"

"void main()\n"
"{\n"
"     color = vec4(1.0f);\n"
"}\n";

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();

const GLuint WIDTH = 800, HEIGHT = 600;

//摄像机信息
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);          //摄像机位置
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);       //摄像机朝向
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);           //上向量

GLfloat deltaTime = 0.0f;   //当前帧遇上一帧的时间差
GLfloat lastFrame = 0.0f;   //上一帧的时间

GLfloat lastX = WIDTH / 2, lastY = HEIGHT / 2;       //鼠标上一次位置,默认屏幕中心
GLfloat pitch = 0.0f;       //俯仰角
GLfloat yaw = -90.0f;       //偏航角
GLfloat aspect = 45.0f;     //视角大小
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

bool keys[1024];

int main()
{
    glfwInit();     //必须要将glfw初始化
    //告诉GLFW使用OpenGL版本
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);  //主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //次版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);  //使用的是OpenGL核心模式
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);   //不允许调整窗口大小

    //创建窗口
    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);
    if (window == nullptr) {
        std::cout << "Failed to create GLFW Window" << std::endl;

        glfwTerminate();    //销毁窗口与数据

        return -1;
    }

    glfwMakeContextCurrent(window);     //将OpenGL指向为当前窗口

    glewExperimental = GL_TRUE;     //用于告知GLEW使用现化OpenGL技术

    //glew初始化
    if (glewInit() != GLEW_OK) {
        std::cout << "Failed to initialize GLEW" << std::endl;

        return -1;
    }

    //视口
    int width = 800, height = 600;
    glfwGetFramebufferSize(window, &width, &height);        //设置OpenGL渲染窗口的尺寸
    glViewport(0, 0, width, height);    //设置窗口的维度 前两个参数控制窗口左下角的位置, 第三、四个参数控制渲染窗口的宽度和高度
    glfwSetKeyCallback(window, key_callback);       //注册按键回调事件
    glfwSetCursorPosCallback(window, mouse_callback);       //注册鼠标回调事件
    //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);        //隐藏鼠标,并捕获他
    glfwSetScrollCallback(window, scroll_callback);                 //鼠标滚动回调事件

    glEnable(GL_DEPTH_TEST);            //深度缓存区

    //顶点着色器
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    GLint vertexSuccess;
    GLchar vertexInfoLog[512];

    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexSuccess);
    if (!vertexSuccess) {
        glGetShaderInfoLog(vertexShader, 512, nullptr, vertexInfoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << vertexInfoLog << std::endl;
    }

    //片段着色器
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    GLint fragmentSuccess;
    GLchar fragmentInfoLog[512];

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentSuccess);
    if (!fragmentSuccess) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, fragmentInfoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << fragmentInfoLog << std::endl;
    }


    //着色器链接程序
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    GLint programSuccess;
    GLchar programInfoLog[512];
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &programSuccess);
    if (!programSuccess) {
        glGetProgramInfoLog(shaderProgram, 512, nullptr, programInfoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << programInfoLog << std::endl;
    }

    //光源-顶点着色器
    GLuint lightVertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(lightVertexShader, 1, &lightVertexShaderSource, nullptr);
    glCompileShader(lightVertexShader);

    GLint lightVertexSuccess;
    GLchar lightVertexInfoLog[512];

    glGetShaderiv(lightVertexShader, GL_COMPILE_STATUS, &lightVertexSuccess);
    if (!vertexSuccess) {
        glGetShaderInfoLog(lightVertexShader, 512, nullptr, lightVertexInfoLog);
        std::cout << "ERROR::SHADER::LIGHT::VERTEX::COMPILATION_FAILED\n" << lightVertexInfoLog << std::endl;
    }

    //片段着色器
    GLuint lightFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(lightFragmentShader, 1, &lightFragmentShaderSource, nullptr);
    glCompileShader(lightFragmentShader);

    GLint lightFragmentSuccess;
    GLchar lightFragmentInfoLog[512];

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &lightFragmentSuccess);
    if (!fragmentSuccess) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, lightFragmentInfoLog);
        std::cout << "ERROR::SHADER::LIGHT::FRAGMENT::COMPILATION_FAILED\n" << lightFragmentInfoLog << std::endl;
    }


    //光源着色器链接程序
    GLuint lightShaderProgram = glCreateProgram();
    glAttachShader(lightShaderProgram, lightVertexShader);
    glAttachShader(lightShaderProgram, lightFragmentShader);
    glLinkProgram(lightShaderProgram);

    GLint lightProgramSuccess;
    GLchar lightProgramInfoLog[512];
    glGetProgramiv(lightShaderProgram, GL_LINK_STATUS, &lightProgramSuccess);
    if (!programSuccess) {
        glGetProgramInfoLog(lightShaderProgram, 512, nullptr, lightProgramInfoLog);
        std::cout << "ERROR::SHADER::LIGHT::PROGRAM::LINKING_FAILED\n" << lightProgramInfoLog << std::endl;
    }


    GLfloat vertices[] = {
        //-----位置           //法向量
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };

    //顶点数据
    GLuint VBO, VAO;
    glGenBuffers(1, &VBO);      //顶点缓冲对象
    glGenVertexArrays(1, &VAO); //顶点数组对象

    glBindVertexArray(VAO);

    //顶点数据
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    //顶点属性
    //顶点坐标
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    //法向量
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    //光源
    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBindVertexArray(lightVAO);

    //顶点属性
    //顶点坐标
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);


    while (!glfwWindowShouldClose(window)) {
        //检查GLFW是否退出,即窗口是否关闭了,true代表结束了

        glfwPollEvents();       //检查有没有事件发生(键盘输入、鼠标移动),如发生调用对应的回调函数  键盘事件:glfwSetKeyCallback(window, key_callback);  key_callback即设定的回调函数
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);       //清空屏幕所用的颜色,即清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色。
        //glClear(GL_COLOR_BUFFER_BIT);       //清空屏幕缓冲,这里是颜色缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        do_movement();
        //渲染指令
        //glBindTexture(GL_TEXTURE_2D, texture);
        glUseProgram(shaderProgram);


        glm::mat4 model;        //模型矩阵
        glm::mat4 view;         //观察矩阵
        glm::mat4 projection;   //投影矩阵-透视投影
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        projection = glm::perspective(aspect, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);  //缩放效果

        //光颜色和物体颜色
        GLint objectColorLoc = glGetUniformLocation(shaderProgram, "objectColor");
        GLint lightColorLoc = glGetUniformLocation(shaderProgram, "lightColor");
        glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);// 我们所熟悉的珊瑚红
        glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // 依旧把光源设置为白色

        //材质
        GLint matAmbientLoc = glGetUniformLocation(shaderProgram, "material.ambient");
        GLint matDiffuseLoc = glGetUniformLocation(shaderProgram, "material.diffuse");
        GLint matSpecularLoc = glGetUniformLocation(shaderProgram, "material.specular");
        GLint matShineLoc = glGetUniformLocation(shaderProgram, "material.shininess");

        glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
        glUniform1f(matShineLoc, 32.0f);

        GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
        GLint viewLoc = glGetUniformLocation(shaderProgram, "view");
        GLint projLoc = glGetUniformLocation(shaderProgram, "projection");
        GLint lightPosLoc = glGetUniformLocation(shaderProgram, "lightPos");
        GLint viewPosLoc = glGetUniformLocation(shaderProgram, "viewPos");

        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
        glUniform3f(viewPosLoc, cameraPos.x, cameraPos.y, cameraPos.z);

        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        //光源
        glUseProgram(lightShaderProgram);       //光源
        modelLoc = glGetUniformLocation(lightShaderProgram, "model");
        viewLoc = glGetUniformLocation(lightShaderProgram, "view");
        projLoc = glGetUniformLocation(lightShaderProgram, "projection");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(lightVAO);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f));
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        glfwSwapBuffers(window);  //交换颜色缓冲,用来绘制,输出显示在屏幕上
    }

    glDeleteBuffers(1, &VBO);
    glDeleteVertexArrays(1, &VAO);

    glfwTerminate();
    return 0;
}

/*
* 按键回调事件
* @param window 窗口
* @param key 按键
* @param scancode 扫描码
* @param action 表示这个按键是被按下还是释放
* @param mode 是否有Ctrl、Shift、Alt、Super等按钮的操作
*/
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {

    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
        //esc键按下,关闭窗口
        glfwSetWindowShouldClose(window, GL_TRUE);
    }

    if (action == GLFW_PRESS)   //按下
        keys[key] = true;
    else if (action == GLFW_RELEASE)    //松开
        keys[key] = false;

}

void do_movement() {
    GLfloat currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;
    GLfloat cameraSpeed = 5.0f * deltaTime;    //速度

    if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_A])
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_D])
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_SPACE])       //向上
        cameraPos += cameraSpeed * cameraUp;
}

bool firstMouse = true;     //除了第一次鼠标移动外,其他不能移动
/*
* 鼠标回调事件
* @param window 窗口
* @param xpos 鼠标x坐标
* @param ypos 鼠标y坐标
*/
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;
    lastX = xpos;
    lastY = ypos;

    GLfloat sensitivity = 0.05;         //鼠标转向速度
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw += xoffset;
    pitch += yoffset;

    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);
}

/*
* 鼠标滚轮回调事件
*/
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    if (aspect >= 1.0f && aspect <= 45.0f)
        aspect -= yoffset;
    if (aspect <= 1.0f)
        aspect = 1.0f;
    if (aspect >= 45.0f)
        aspect = 45.0f;
}

光的属性

这个物体太亮了。物体过亮的原因是环境、漫反射和镜面三个颜色任何一个光源都会去全力反射。光源对环境、漫反射和镜面元素同时具有不同的强度。前面的教程(OpenGL笔记8-光照中片段着色器设置的ambientStrength和specularStrength),我们通过使用一个强度值改变环境和镜面强度的方式解决了这个问题。我们想做一个相同的系统,但是这次为每个光照元素指定了强度向量。如果我们想象lightColor是vec3(1.0),代码看起来像是这样:

vec3 ambient = vec3(1.0f) * material.ambient;
vec3 diffuse = vec3(1.0f) * (diff * material.diffuse);
vec3 specular = vec3(1.0f) * (spec * material.specular);

所以物体的每个材质属性返回了每个光照元素的全强度。这些vec3(1.0)值可以各自独立的影响各个光源,这通常就是我们想要的。现在物体的ambient元素完全地展示了立方体的颜色,可是环境元素不应该对最终颜色有这么大的影响,所以我们要设置光的ambient亮度为一个小一点的值,从而限制环境色:

vec3 result = vec3(0.1f) * material.ambient;

我们可以用同样的方式影响光源diffuse和specular的强度。这和我们前面教程所做的极为相似;你可以说我们已经创建了一些光的属性来各自独立地影响每个光照元素。我们希望为光的属性创建一些与材质结构体相似的东西:

struct Light
{
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform Light light;

一个光源的ambient、diffuse和specular光都有不同的亮度。环境光通常设置为一个比较低的亮度,因为我们不希望环境色太过显眼。光源的diffuse元素通常设置为我们希望光所具有的颜色;经常是一个明亮的白色。specular元素通常被设置为vec3(1.0f)类型的全强度发光。要记住的是我们同样把光的位置添加到结构体中。

就像材质uniform一样,需要更新片段着色器:

vec3 ambient = light.ambient * material.ambient;
vec3 diffuse = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);

然后我们要在应用里设置光的亮度:

GLint lightAmbientLoc = glGetUniformLocation(lightingShader.Program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");

glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
glUniform3f(lightDiffuseLoc, 0.5f, 0.5f, 0.5f);     //让我们把这个光调暗一点,这样会看起来更自然
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);

现在,我们调整了光是如何影响物体所有的材质的,我们得到一个更像前面教程的视觉输出。这次我们完全控制了物体光照和材质:
添加光属性.png

不同的光源颜色

目前为止,我们使用光源的颜色仅仅去改变物体各个元素的强度(通过选用从白到灰到黑范围内的颜色),并没有影响物体的真实颜色(只是强度)。由于现在能够非常容易地访问光的属性了,我们可以随着时间改变它们的颜色来获得一些有很意思的效果。由于所有东西都已经在片段着色器做好了,改变光的颜色很简单,我们可以立即创建出一些有趣的效果:


如你所见,不同光的颜色极大地影响了物体的颜色输出。由于光的颜色直接影响物体反射的颜色(你可能想起在颜色教程中有讨论过),它对视觉输出有显著的影响。

利用sin和glfwGetTime改变光的环境和漫反射颜色,我们可以随着时间流逝简单的改变光源颜色:

glm::vec3 lightColor; lightColor.x = sin(glfwGetTime() * 2.0f);
lightColor.y = sin(glfwGetTime() * 0.7f);
lightColor.z = sin(glfwGetTime() * 1.3f);

glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);

glUniform3f(lightAmbientLoc, ambientColor.x, ambientColor.y, ambientColor.z);
glUniform3f(lightDiffuseLoc, diffuseColor.x, diffuseColor.y, diffuseColor.z);

完整代码

#include <glew.h>
#include <glfw3.h>
#include <iostream>
#include <SOIL2.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

//顶点着色器
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec3 normal;\n"

"out vec3 Normal;\n"            //法向量
"out vec3 FragPos;\n"           //片段位置(如立方体六个面,每个面两个三角形,三角形形成的那个片段)

"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"

"void main()\n"
"{\n"
"   gl_Position = projection * view * model * vec4(position, 1.0f);\n"       //数学库矩阵转换
"   FragPos = vec3(model * vec4(position, 1.0f));\n"               //片段位置,将片段位置转化为世界坐标
"   Normal = normal;\n"
"}\n";

//片段着色器
const char* fragmentShaderSource = "#version 330 core\n"
"in vec3 Normal;\n"
"in vec3 FragPos;\n"

"out vec4 color;\n"

"uniform vec3 objectColor;\n"
"uniform vec3 lightColor;\n"
"uniform vec3 lightPos;\n"
"uniform vec3 viewPos;\n"           //摄像机位置

//材质属性
"struct Material\n"
"{\n"
"     vec3 ambient;\n"          //环境光
"     vec3 diffuse;\n"          //在漫反射光照下物体的颜色
"     vec3 specular;\n"         //受到镜面反射的颜色
"     float shininess;\n"       //反射半径
"};\n"
"uniform Material material;\n"

//光强度
"struct Light\n"
"{\n"
"     vec3 position;\n"
"     vec3 ambient;\n"          //环境光强度
"     vec3 diffuse;\n"          //漫反射强度
"     vec3 specular;\n"         //镜面反射强度
"};\n"

"uniform Light light;\n"

"void main()\n"
"{\n"

      //环境光
"     vec3 ambient = material.ambient * light.ambient;\n"       //环境光

      //漫反射
"     vec3 norm = normalize(Normal);\n"         //法向量单位化
"     vec3 lightDir = normalize(lightPos - FragPos);\n"        //计算光的方向,即方向向量  指向光源方向lightPos, 朝向片段方向FragPos
"     float diff = max(dot(norm, lightDir), 0.0);\n"           //计算光对当前片段的实际散射影响
"     vec3 diffuse = (diff * material.diffuse) * light.diffuse;\n"          //得到散射因子

      //镜面高光
"     vec3 viewDir = normalize(viewPos - FragPos);\n"     //指向摄像机viewPos,朝向FragPos
"     vec3 reflectDir = reflect(-lightDir, norm);\n"      //反射的方向
"     float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);\n"
"     vec3 specular = (material.specular * spec) * light.specular;\n"  //计算镜面分量

      //光照
"     vec3 result = ambient + diffuse + specular;\n"   //冯氏光照模型 = 环境光 + 漫反射 + 镜面反射
"     color = vec4(result, 1.0f) ;\n"
"}\n";


//光源-顶点着色器
const char* lightVertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"

"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"

"void main()\n"
"{\n"
"   gl_Position = projection * view * model * vec4(position, 1.0f);\n"       //数学库矩阵转换
"}\n";

//光源-片段着色器
const char* lightFragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"

"void main()\n"
"{\n"
"     color = vec4(1.0f);\n"
"}\n";

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();

const GLuint WIDTH = 800, HEIGHT = 600;

//摄像机信息
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);          //摄像机位置
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);       //摄像机朝向
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);           //上向量

GLfloat deltaTime = 0.0f;   //当前帧遇上一帧的时间差
GLfloat lastFrame = 0.0f;   //上一帧的时间

GLfloat lastX = WIDTH / 2, lastY = HEIGHT / 2;       //鼠标上一次位置,默认屏幕中心
GLfloat pitch = 0.0f;       //俯仰角
GLfloat yaw = -90.0f;       //偏航角
GLfloat aspect = 45.0f;     //视角大小
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

bool keys[1024];

int main()
{
    glfwInit();     //必须要将glfw初始化
    //告诉GLFW使用OpenGL版本
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);  //主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //次版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);  //使用的是OpenGL核心模式
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);   //不允许调整窗口大小

    //创建窗口
    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);
    if (window == nullptr) {
        std::cout << "Failed to create GLFW Window" << std::endl;

        glfwTerminate();    //销毁窗口与数据

        return -1;
    }

    glfwMakeContextCurrent(window);     //将OpenGL指向为当前窗口

    glewExperimental = GL_TRUE;     //用于告知GLEW使用现化OpenGL技术

    //glew初始化
    if (glewInit() != GLEW_OK) {
        std::cout << "Failed to initialize GLEW" << std::endl;

        return -1;
    }

    //视口
    int width = 800, height = 600;
    glfwGetFramebufferSize(window, &width, &height);        //设置OpenGL渲染窗口的尺寸
    glViewport(0, 0, width, height);    //设置窗口的维度 前两个参数控制窗口左下角的位置, 第三、四个参数控制渲染窗口的宽度和高度
    glfwSetKeyCallback(window, key_callback);       //注册按键回调事件
    glfwSetCursorPosCallback(window, mouse_callback);       //注册鼠标回调事件
    //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);        //隐藏鼠标,并捕获他
    glfwSetScrollCallback(window, scroll_callback);                 //鼠标滚动回调事件

    glEnable(GL_DEPTH_TEST);            //深度缓存区

    //顶点着色器
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    GLint vertexSuccess;
    GLchar vertexInfoLog[512];

    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexSuccess);
    if (!vertexSuccess) {
        glGetShaderInfoLog(vertexShader, 512, nullptr, vertexInfoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << vertexInfoLog << std::endl;
    }

    //片段着色器
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    GLint fragmentSuccess;
    GLchar fragmentInfoLog[512];

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentSuccess);
    if (!fragmentSuccess) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, fragmentInfoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << fragmentInfoLog << std::endl;
    }


    //着色器链接程序
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    GLint programSuccess;
    GLchar programInfoLog[512];
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &programSuccess);
    if (!programSuccess) {
        glGetProgramInfoLog(shaderProgram, 512, nullptr, programInfoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << programInfoLog << std::endl;
    }

    //光源-顶点着色器
    GLuint lightVertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(lightVertexShader, 1, &lightVertexShaderSource, nullptr);
    glCompileShader(lightVertexShader);

    GLint lightVertexSuccess;
    GLchar lightVertexInfoLog[512];

    glGetShaderiv(lightVertexShader, GL_COMPILE_STATUS, &lightVertexSuccess);
    if (!vertexSuccess) {
        glGetShaderInfoLog(lightVertexShader, 512, nullptr, lightVertexInfoLog);
        std::cout << "ERROR::SHADER::LIGHT::VERTEX::COMPILATION_FAILED\n" << lightVertexInfoLog << std::endl;
    }

    //片段着色器
    GLuint lightFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(lightFragmentShader, 1, &lightFragmentShaderSource, nullptr);
    glCompileShader(lightFragmentShader);

    GLint lightFragmentSuccess;
    GLchar lightFragmentInfoLog[512];

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &lightFragmentSuccess);
    if (!fragmentSuccess) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, lightFragmentInfoLog);
        std::cout << "ERROR::SHADER::LIGHT::FRAGMENT::COMPILATION_FAILED\n" << lightFragmentInfoLog << std::endl;
    }


    //光源着色器链接程序
    GLuint lightShaderProgram = glCreateProgram();
    glAttachShader(lightShaderProgram, lightVertexShader);
    glAttachShader(lightShaderProgram, lightFragmentShader);
    glLinkProgram(lightShaderProgram);

    GLint lightProgramSuccess;
    GLchar lightProgramInfoLog[512];
    glGetProgramiv(lightShaderProgram, GL_LINK_STATUS, &lightProgramSuccess);
    if (!programSuccess) {
        glGetProgramInfoLog(lightShaderProgram, 512, nullptr, lightProgramInfoLog);
        std::cout << "ERROR::SHADER::LIGHT::PROGRAM::LINKING_FAILED\n" << lightProgramInfoLog << std::endl;
    }


    GLfloat vertices[] = {
        //-----位置           //法向量
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
         0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
         0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };

    //顶点数据
    GLuint VBO, VAO;
    glGenBuffers(1, &VBO);      //顶点缓冲对象
    glGenVertexArrays(1, &VAO); //顶点数组对象

    glBindVertexArray(VAO);

    //顶点数据
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    //顶点属性
    //顶点坐标
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    //法向量
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    //光源
    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBindVertexArray(lightVAO);

    //顶点属性
    //顶点坐标
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);


    while (!glfwWindowShouldClose(window)) {
        //检查GLFW是否退出,即窗口是否关闭了,true代表结束了

        glfwPollEvents();       //检查有没有事件发生(键盘输入、鼠标移动),如发生调用对应的回调函数  键盘事件:glfwSetKeyCallback(window, key_callback);  key_callback即设定的回调函数
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);       //清空屏幕所用的颜色,即清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色。
        //glClear(GL_COLOR_BUFFER_BIT);       //清空屏幕缓冲,这里是颜色缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        do_movement();
        //渲染指令
        //glBindTexture(GL_TEXTURE_2D, texture);
        glUseProgram(shaderProgram);


        glm::mat4 model;        //模型矩阵
        glm::mat4 view;         //观察矩阵
        glm::mat4 projection;   //投影矩阵-透视投影
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        projection = glm::perspective(aspect, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);  //缩放效果

        //物体颜色和光颜色
        GLint objectColorLoc = glGetUniformLocation(shaderProgram, "objectColor");
        GLint lightColorLoc = glGetUniformLocation(shaderProgram, "lightColor");
        glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);// 我们所熟悉的珊瑚红
        glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // 依旧把光源设置为白色

        //材质
        GLint matAmbientLoc = glGetUniformLocation(shaderProgram, "material.ambient");
        GLint matDiffuseLoc = glGetUniformLocation(shaderProgram, "material.diffuse");
        GLint matSpecularLoc = glGetUniformLocation(shaderProgram, "material.specular");
        GLint matShineLoc = glGetUniformLocation(shaderProgram, "material.shininess");

        glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
        glUniform1f(matShineLoc, 32.0f);

        //光强度
        GLint lightAmbientLoc = glGetUniformLocation(shaderProgram, "light.ambient");
        GLint lightDiffuseLoc = glGetUniformLocation(shaderProgram, "light.diffuse");
        GLint lightSpecularLoc = glGetUniformLocation(shaderProgram, "light.specular");

        glm::vec3 lightColor; lightColor.x = sin(glfwGetTime() * 2.0f);
        lightColor.y = sin(glfwGetTime() * 0.7f);
        lightColor.z = sin(glfwGetTime() * 1.3f);

        glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);
        glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);

        glUniform3f(lightAmbientLoc, ambientColor.x, ambientColor.y, ambientColor.z);
        glUniform3f(lightDiffuseLoc, diffuseColor.x, diffuseColor.y, diffuseColor.z);
        glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);

        GLint modelLoc = glGetUniformLocation(shaderProgram, "model");
        GLint viewLoc = glGetUniformLocation(shaderProgram, "view");
        GLint projLoc = glGetUniformLocation(shaderProgram, "projection");
        GLint lightPosLoc = glGetUniformLocation(shaderProgram, "lightPos");
        GLint viewPosLoc = glGetUniformLocation(shaderProgram, "viewPos");

        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
        glUniform3f(viewPosLoc, cameraPos.x, cameraPos.y, cameraPos.z);

        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        //光源
        glUseProgram(lightShaderProgram);       //光源
        modelLoc = glGetUniformLocation(lightShaderProgram, "model");
        viewLoc = glGetUniformLocation(lightShaderProgram, "view");
        projLoc = glGetUniformLocation(lightShaderProgram, "projection");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(lightVAO);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f));
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        glfwSwapBuffers(window);  //交换颜色缓冲,用来绘制,输出显示在屏幕上
    }

    glDeleteBuffers(1, &VBO);
    glDeleteVertexArrays(1, &VAO);

    glfwTerminate();
    return 0;
}

/*
* 按键回调事件
* @param window 窗口
* @param key 按键
* @param scancode 扫描码
* @param action 表示这个按键是被按下还是释放
* @param mode 是否有Ctrl、Shift、Alt、Super等按钮的操作
*/
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {

    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
        //esc键按下,关闭窗口
        glfwSetWindowShouldClose(window, GL_TRUE);
    }

    if (action == GLFW_PRESS)   //按下
        keys[key] = true;
    else if (action == GLFW_RELEASE)    //松开
        keys[key] = false;

}

void do_movement() {
    GLfloat currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;
    GLfloat cameraSpeed = 5.0f * deltaTime;    //速度

    if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_A])
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_D])
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_SPACE])       //向上
        cameraPos += cameraSpeed * cameraUp;
}

bool firstMouse = true;     //除了第一次鼠标移动外,其他不能移动
/*
* 鼠标回调事件
* @param window 窗口
* @param xpos 鼠标x坐标
* @param ypos 鼠标y坐标
*/
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;
    lastX = xpos;
    lastY = ypos;

    GLfloat sensitivity = 0.05;         //鼠标转向速度
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw += xoffset;
    pitch += yoffset;

    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);
}

/*
* 鼠标滚轮回调事件
*/
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    if (aspect >= 1.0f && aspect <= 45.0f)
        aspect -= yoffset;
    if (aspect <= 1.0f)
        aspect = 1.0f;
    if (aspect >= 45.0f)
        aspect = 45.0f;
}

原教程

颜色原教程
光照基础原教程

该教程源码

git地址

分享到:
OpenGL笔记10-光照贴图
OpenGL笔记8-光照
  • 文章目录
  • 站点概览
欢

网红 欢

你能抓到我么?

Email RSS
看爆 Top5
  • mac系统版本与Xcode版本有冲突 4,080次看爆
  • JAVA_HOME环境配置问题 3,730次看爆
  • AssetBundle使用 3,499次看爆
  • VSCode配置C++开发环境 3,257次看爆
  • Lua反射 3,133次看爆

Copyright © 2025 欢 粤ICP备2020105803号-1

由 Halo 强力驱动 · Theme by Sagiri · 站点地图