【(一)OpenGL入门】3.着色器

目录

1.基础知识

1-1 什么是着色器

1-2 数据类型

1-3 什么是重组

1-4 画一个红色三角形

2.uniform

2-1 画一个动态渐变色的三角形

3.把颜色数据加进顶点数据中

4.封装shader类

5.练习

5-1 修改顶点着色器让三角形上下颠倒

5-2 使用uniform定义一个水平偏移量,在顶点着色器中使用这个偏移量把三角形移动到屏幕右侧

​编辑

5-3 使用out关键字把顶点位置输出到片段着色器,并将片段的颜色设置为与顶点位置相等(来看看连顶点位置值都在三角形中被插值的结果)。做完这些后,尝试回答下面的问题:为什么在三角形的左下角是黑的?


1.基础知识

1-1 什么是着色器

        着色器是运行在gpu核上的小程序。这些小程序针对渲染管线中的每个特定步骤运行。从本质上说,着色器只不过是将输入转换为输出的程序。着色器也是非常孤立的程序,他们唯一的交流是通过输入和输出。它是使用一种为计算机图形学量身定制的GLSL语言写成的,GLSL有两种容器类型,向量vector和矩阵matrix。

1-2 数据类型

        大多数时候我们使用vecn,因为float足够满足大多数要求了。向量的分量可以通过xyzw分别访问。GLSL还允许对颜色使用rgba,或对纹理坐标使用stpq。

1-3 什么是重组

        向量允许一些有趣和灵活的分量选择方式,叫重组(swizzling)。

1-4 画一个红色三角形

        通过改写着色器实现一个红色的三角形,需要修改顶点着色器和片段着色器的数据,不清楚这两个着色器的可以去看上一篇的<3-1认识顶点着色器和片段着色器>。我们通过改写上一篇3-2的程序中的着色器来实现,改动如下1~3处。

//顶点着色器源码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"out vec4 vertexColor;\n"   //1.在顶点着色器中添加out
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
" vertexColor = vec4(0.5, 0.0, 0.0, 1.0);\n"
"}\0";

//片段着色器源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 vertexColor;\n"  //2.在片段着色器中添加in
"void main()\n"
"{\n"
" FragColor = vertexColor;\n" //3.使用接收到的变量作为颜色值
"}\n\0";

运行结果,我们成功的将一个值从顶点着色器发送到片段着色器。

2.uniform

        方才我们从顶点着色器向片段着色器发送数据,还有另一种从CPU应用向GPU中的着色器发送数据的方式uniform,可以被任意阶段的着色器访问。

2-1 画一个动态渐变色的三角形

        下来我们通过一个案例来理解uniform的真正用途,去掉顶点着色器的属性,直接使用uniforom来对三角形着色。基于上面程序修改顶点着色器、片段着色器、并在whil(1)渲染中的添加使用着色器的代码,添加着色器的具体位置在我们绑定流水线之后。

//顶点着色器源码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
//"out vec4 vertexColor;\n"  不需要从顶点着色器输出了,因为我们会用uniform
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
//" vertexColor = vec4(0.5, 0.0, 0.0, 1.0);\n" 同理,不注释会报错
"}\0";

//片段着色器源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
//"in vec4 vertexColor;\n"
"uniform vec4 ourColor;\n"  //从CPU端传入
"void main()\n"
"{\n"
//" FragColor = vertexColor;\n" 
" FragColor = ourColor;\n"
"}\n\0";
while (!glfwWindowShouldClose(window))                              //检查一次GLFW是否被要求退出,循环一次就是刷新一帧
    {
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);                           //设置颜色:深绿色
        glClear(GL_COLOR_BUFFER_BIT);                                   //刷新屏幕,使用设置的颜色

        //d.选择使用着色器给三角形上色,即上面创建链接好的流水线
        glUseProgram(shaderProgram);

        // 设置uniform值
        float timeValue = glfwGetTime();//获取时间
        float greenValue = (sin(timeValue) / 2.0f + 0.5f);//(-1,1)/2+0.5 = (-0.5,0.5)+0.5 = (0,1)
        int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");//在着色器程序里找到ourColor
        glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);//f表示为float类型


        //9.上面把VAO解绑了,这里必须重新绑定否则绘制不出来三角形
        glBindVertexArray(VAO);
        //8.渲染线程里加上绘制三角形的命令
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);                                        //交换颜色缓冲(双缓冲):储存着GLFW窗口每一个像素颜色值作为输出显示在屏幕上
        glfwPollEvents();                                               //轮训检查输入设备触发事件
    }

运行结果:

3.把颜色数据加进顶点数据中

        我们之前说了VAO可以保存顶点很多属性值,之前只保存了三个顶点,接下来我们要利用这个特点来实现给三个顶点分别着色红绿蓝三种颜色,然后再看看效果。

        首先添加3个float类型数据至顶点数组

        只修改这上面这一部分它会把这些当做三角形的顶点,我们还得修改顶点着色器和片段着色器。

        如何解析?多了一个属性步长就变多了,步长变为6*4 = 24bit(其中4为float类型)。

完整代码如下:

#include <glad.h>   //glad.h需要在glfw.h之前
#include <glfw3.h>
#include <iostream>
#include <cmath> 

float vertices[] = { // 位置 // 颜色 
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下 红色
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下 绿色
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 上 蓝色
};

//a.着色器源码
//顶点着色器源码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"out vec3 ourColor;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos, 1.0); \n"
"   ourColor = aColor;\n"
"}\0";

//片段着色器源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(ourColor, 1.0); \n"
"}\n\0";

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

int main()
{
    //初始化glfw:选择opengl 3.3~4.2核心模式,取决于你的显卡驱动
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__                                                         //如果是苹果操作系统
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); //创建窗体对象:参数1:opengl窗口大小;参数2:窗口标题
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);                                     //设置当前窗口上下文设置为当前线程的上下文,之后才能在窗口进行绘制

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))            //glad:加载所有OpenGL函数指针,固定写法就这一行
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    glViewport(0, 0, 600, 400);                                         //设置opengl实际渲染的区域大小;0,0为左下角坐标

    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);  //注册窗口大小改变回调函数

    //b.创建并编译着色器
    //顶点着色器
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//创建顶点着色器,arg:顶点着色器
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//获取顶点着色器源码
    //arg1:着色器类型
    //arg2:源码是一个字符串表示
    //arg3:字符串长度
    glCompileShader(vertexShader);//编译顶点着色器
    // 检查编译错误
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 片段着色器
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//创建顶点着色器
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//获取片段着色器源码
    glCompileShader(fragmentShader);//编译片段着色器
    // 检查编译错误
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    //c.把顶点着色器和片段着色器链接成一个着色器程序,即一个流水线
    //着色器程序
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    //链接错误检查
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }
    //链接成一个完整着色器程序后释放掉顶点着色器和片段着色器
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    //2.创建VBO和VAO对象并赋予ID值
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    //3.绑定VBO,VAO对象
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    //4.开辟并填充数据
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    //arg1:目标buffer对象,数组对象
    //arg2:数据大小
    //arg3:数据地址,如果为NULL则该函数只是开辟内存
    //arg4:作用,静态数据绘制

    //5.告知Shader如何解析缓冲里的属性值
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    //arg1:第1个VAO从0开始,只有一个VAO表示坐标属性。
    //arg2:该属性里有3个坐标
    //arg3:浮点类型
    //arg4:是否标准化
    //arg5:步长
    //arg6:偏移量,顶点数组offset从0开始就是0

    //6.开启VAO管理的第一个属性值,第一个VAO编号为0
    glEnableVertexAttribArray(0);

    //新增VAO颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float))); //颜色属性开始跨了3个顶点属性
    glEnableVertexAttribArray(1);

    //7.解绑VBO,VAO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window))                              //检查一次GLFW是否被要求退出,循环一次就是刷新一帧
    {
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);                           //设置颜色:深绿色
        glClear(GL_COLOR_BUFFER_BIT);                                   //刷新屏幕,使用设置的颜色

        //d.选择使用着色器给三角形上色,即上面创建链接好的流水线
        glUseProgram(shaderProgram);

        /* 设置uniform值
        float timeValue = glfwGetTime();//获取时间
        float greenValue = (sin(timeValue) / 2.0f + 0.5f);//(-1,1)/2+0.5 = (-0.5,0.5)+0.5 = (0,1)
        int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");//在着色器程序里找到ourColor
        glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);//f表示为float类型
        */
        //9.上面把VAO解绑了,这里必须重新绑定否则绘制不出来三角形
        glBindVertexArray(VAO);
        //8.渲染线程里加上绘制三角形的命令
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);                                        //交换颜色缓冲(双缓冲):储存着GLFW窗口每一个像素颜色值作为输出显示在屏幕上
        glfwPollEvents();                                               //轮训检查输入设备触发事件
    }
    //e.渲染结束后把VAO,VBO和着色器都释放掉
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);

    glfwTerminate();                                                    //释放分配的所有glfw资源
    return 0;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)   //窗口大小被改变就重新设置渲染区域大小
{
    glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)              //按Esc键关闭界面
        glfwSetWindowShouldClose(window, true);
}

运行结果:

4.封装shader类

        之前我们都是把shader以字符串的方式直接写到应用程序中,用起来不方便,风格看起来也很像C语言,我们希望把shader封装起来然后以文件的形式加载到应用程序中。

<shader.vs>

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
}

<shader.fs>

#version 330 core
out vec4 FragColor;

in vec3 ourColor;

void main()
{
    FragColor = vec4(ourColor, 1.0f);
}

<shader.h>

#ifndef SHADER_H
#define SHADER_H

#include <glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
    unsigned int ID;//着色器程序ID,即流水线ID

    // 构造函数生成着色器对象
    Shader(const char* vertexPath, const char* fragmentPath)
    {
        // 1.从文件路径中获取顶点/片段源代码
        std::string vertexCode;
        std::string fragmentCode; 
        std::ifstream vShaderFile;
        std::ifstream fShaderFile;
        
        // 确保ifstream对象可以引发异常:
        vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        try
        {
            // 打开文件
            vShaderFile.open(vertexPath);         
            fShaderFile.open(fragmentPath);
            std::stringstream vShaderStream, fShaderStream;
            // 将文件的缓冲区内容读入流
            vShaderStream << vShaderFile.rdbuf();
            fShaderStream << fShaderFile.rdbuf();
            // 关闭文件
            vShaderFile.close();
            fShaderFile.close();
            // 将流转换为字符串
            vertexCode = vShaderStream.str();
            fragmentCode = fShaderStream.str();
        }
        catch (std::ifstream::failure& e)
        {
            std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ: " << e.what() << std::endl;
        }
        //将字符串转换为C风格字符串
        const char* vShaderCode = vertexCode.c_str();
        const char* fShaderCode = fragmentCode.c_str();
        // 2. 编译着色器
        unsigned int vertex, fragment;
        // 创建并编译顶点着色器
        vertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex, 1, &vShaderCode, NULL);
        glCompileShader(vertex);
        checkCompileErrors(vertex, "VERTEX");
        // 创建并编译片段着色器
        fragment = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment, 1, &fShaderCode, NULL);
        glCompileShader(fragment);
        checkCompileErrors(fragment, "FRAGMENT");
        // 链接着色器程序
        ID = glCreateProgram();
        glAttachShader(ID, vertex);
        glAttachShader(ID, fragment);
        glLinkProgram(ID);
        checkCompileErrors(ID, "PROGRAM");
        // 删除着色器,因为它们现在链接到着色器程序中,不再需要了
        glDeleteShader(vertex);
        glDeleteShader(fragment);
    }
    
    // 选择着色器程序
    void use()
    {
        glUseProgram(ID);
    } 
    
    // uniform的实用函数 
    void setBool(const std::string& name, bool value) const
    {
        glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
    }
    
    void setInt(const std::string& name, int value) const
    {
        glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
    }
   
    void setFloat(const std::string& name, float value) const
    {
        glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
    }

private:
    // 用于检查着色器编译/链接错误的实用函数。
    void checkCompileErrors(GLuint shader, std::string type)
    {
        GLint success;
        GLchar infoLog[1024];
        if (type != "PROGRAM")
        {
            glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
            if (!success)
            {
                glGetShaderInfoLog(shader, 1024, NULL, infoLog);
                std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
            }
        }
        else
        {
            glGetProgramiv(shader, GL_LINK_STATUS, &success);
            if (!success)
            {
                glGetProgramInfoLog(shader, 1024, NULL, infoLog);
                std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
            }
        }
    }
};
#endif

<main.cpp>

#include <glad.h>   //glad.h需要在glfw.h之前
#include <glfw3.h>
#include <iostream>
#include <cmath> 
#include "shader.h"

float vertices[] = { // 位置 // 颜色 
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下 红色
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下 绿色
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 上 蓝色
};

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

int main()
{
    //初始化glfw:选择opengl 3.3~4.2核心模式,取决于你的显卡驱动
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__                                                         //如果是苹果操作系统
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); //创建窗体对象:参数1:opengl窗口大小;参数2:窗口标题
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);                                     //设置当前窗口上下文设置为当前线程的上下文,之后才能在窗口进行绘制

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))            //glad:加载所有OpenGL函数指针,固定写法就这一行
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    glViewport(0, 0, 600, 400);                                         //设置opengl实际渲染的区域大小;0,0为左下角坐标

    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);  //注册窗口大小改变回调函数

    Shader shaderobject("shaders/shader.vs", "shaders/shader.fs");

    //2.创建VBO和VAO对象并赋予ID值
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    //3.绑定VBO,VAO对象
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    //4.开辟并填充数据
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    //arg1:目标buffer对象,数组对象
    //arg2:数据大小
    //arg3:数据地址,如果为NULL则该函数只是开辟内存
    //arg4:作用,静态数据绘制

    //5.告知Shader如何解析缓冲里的属性值
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    //arg1:第1个VAO从0开始,只有一个VAO表示坐标属性。
    //arg2:该属性里有3个坐标
    //arg3:浮点类型
    //arg4:是否标准化
    //arg5:步长
    //arg6:偏移量,顶点数组offset从0开始就是0

    //6.开启VAO管理的第一个属性值,第一个VAO编号为0
    glEnableVertexAttribArray(0);

    //新增VAO颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float))); //颜色属性开始跨了3个顶点属性
    glEnableVertexAttribArray(1);

    //7.解绑VBO,VAO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window))                              //检查一次GLFW是否被要求退出,循环一次就是刷新一帧
    {
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);                           //设置颜色:深绿色
        glClear(GL_COLOR_BUFFER_BIT);                                   //刷新屏幕,使用设置的颜色

        //d.选择使用着色器给三角形上色,即上面创建链接好的流水线
        shaderobject.use();

        // 设置uniform值
        //float timeValue = glfwGetTime();//获取时间
        //float greenValue = (sin(timeValue) / 2.0f + 0.5f);//(-1,1)/2+0.5 = (-0.5,0.5)+0.5 = (0,1)
        //int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");//在着色器程序里找到ourColor
        //glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);//f表示为float类型


        //9.上面把VAO解绑了,这里必须重新绑定否则绘制不出来三角形
        glBindVertexArray(VAO);
        //8.渲染线程里加上绘制三角形的命令
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);                                        //交换颜色缓冲(双缓冲):储存着GLFW窗口每一个像素颜色值作为输出显示在屏幕上
        glfwPollEvents();                                               //轮训检查输入设备触发事件
    }
    //e.渲染结束后把VAO,VBO和着色器都释放掉
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderobject.ID);

    glfwTerminate();                                                    //释放分配的所有glfw资源
    return 0;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)   //窗口大小被改变就重新设置渲染区域大小
{
    glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)              //按Esc键关闭界面
        glfwSetWindowShouldClose(window, true);
}

工程目录

5.练习

5-1 修改顶点着色器让三角形上下颠倒

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

void main()
{
    //gl_Position = vec4(aPos, 1.0);
    gl_Position = vec4(aPos.x, -aPos.y, aPos.z, 1.0); //修改y值为-
    ourColor = aColor;
}

5-2 使用uniform定义一个水平偏移量,在顶点着色器中使用这个偏移量把三角形移动到屏幕右侧

/*shader.vs*/
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;
uniform float offsetx;

void main()
{
    //gl_Position = vec4(aPos, 1.0);
    gl_Position = vec4(aPos.x + offsetx, -aPos.y, aPos.z, 1.0); //添加offset
    ourColor = aColor;
}
/*main.cpp*/
// 设置uniform值
        //float timeValue = glfwGetTime();//获取时间
        //float greenValue = (sin(timeValue) / 2.0f + 0.5f);//(-1,1)/2+0.5 = (-0.5,0.5)+0.5 = (0,1)
        //int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");//在着色器程序里找到ourColor
        //glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);//f表示为float类型
        shaderobject.setFloat("offsetx", 0.5);

5-3 使用out关键字把顶点位置输出到片段着色器,并将片段的颜色设置为与顶点位置相等(来看看连顶点位置值都在三角形中被插值的结果)。做完这些后,尝试回答下面的问题:为什么在三角形的左下角是黑的?

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;
uniform float offsetx;

void main()
{
    //gl_Position = vec4(aPos, 1.0);
    //gl_Position = vec4(aPos.x + offsetx, -aPos.y, aPos.z, 1.0); //添加offset
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    //ourColor = aColor;
    ourColor = aPos; //把位置值当颜色传入
}

解释左下角为什么是黑色?

因为颜色值是(0, 1)之间,而左下角顶点的x,y值都是负数,负数也被看做0,所以是黑色的。

原作:着色器 - LearnOpenGL CN

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值