zoukankan      html  css  js  c++  java
  • OpenGL学习(3)——Shader

    之前已经接触过Vertex Shader和Fragment Shader,这次学习如何编写Shader并封装成类。

    Shader源码主要有四部分:

    1. 版本声明 #version xxx core
    2. 使用in和out关键字定义输入输出变量,上一个Shader的输出变量必须和下一个Shader的输入变量保持一致;
    3. 有时使用uniform关键字定义全局变量;
    4. main主函数。

    看一个Vertex Shader的例子:

    #version 330 core
    layout (location = 0) in vec3 Pos;
    out vec4 Color;
    void main()
    {
        gl_Position = vec4(Pos, 1.0f);
        Color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
    }
    

    首先定义一个location为0的三维输入变量Pos,表示顶点的位置属性。之后用一个四维的Color变量表示输出的颜色。
    在main函数中需要将Pos变换成齐次坐标,输出变量Color直接设置为红色。
    Fragment Shader就很容易编写了,只需要把Vertex Shader的输出变量作为输入:

    #version 330 core
    in vec4 Color;
    out vec4 fragColor;
    void main()
    {
        fragColor = Color;
    }
    

    深入一点,考虑如何实现三角形颜色渐变的效果。基本想法是颜色变量在渲染循环中随着时间的变化而变化,这就需要uniform定义一个全局变量来表示颜色。
    首先是Vertex Shader:

    #version 330 core
    layout (location = 0) in vec3 Pos;
    void main()
    {
        gl_Position = vec4(Pos, 1.0f);
    }
    

    在Fragment Shader中使用uniform关键字取代in关键字:

    #version 330 core
    uniform vec4 Color;
    out vec4 fragColor;
    void main()
    {
        fragColor = Color;
    } 
    

    在渲染循环中,调用glfwGetTime函数获取从GLFW被初始化后经过的时间,使用sin函数根据时间计算绿色分量的值,并映射到[0, 1]。不要忘记添加cmath头文件。
    接下来调用glGetUniformLocation函数获取Color变量在Shader Program中的位置,并调用glUniform函数更新Color的值。注意glUniform函数后的后缀4f,表示Color的值是一个float类型的四维向量:

    glUseProgram(shaderProgram);
    float timeValue = glfwGetTime();
    float greenValue = sin(timeValue)/2.0f+0.5f;
    int uniformlocation = glGetUniformLocation(shaderProgram, "Color");
    glUniform4f(uniformlocation, 0.0f, greenValue, 0.0f, 1.0f);
    

    运行代码后会发现三角形颜色逐渐由绿变成黑,再变成绿。


    之前所有的代码都只涉及了一个顶点属性,即Pos用来表示顶点的位置属性。如果在Vertex Shader中再定义一个location为1的三维输入变量,表示顶点的颜色属性,那么该如何配置VBO和顶点属性指针,并和VAO绑定呢?
    首先在顶点数据中添加颜色数据,位置(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.5f, 0.5f, 0.0f)的顶点颜色为(0.0f, 0.0f, 1.0f):

    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.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f};
    

    编写Vertex Shader,Pos的属性位置为0,表示位置,Col的属性位置为1,表示颜色:

    #version 330 core
    layout (location = 0) in vec3 Pos;
    layout (location = 1) in vec3 Col;
    out vec4 Color;
    void main()
    {
        gl_Position = vec4(Pos, 1.0f);
        Color = vec4(Col, 1.0f);
    }
    

    不需要更改Fragment Shader:

    #version 330 core
    in vec4 Color;
    out vec4 fragColor;
    void main()
    {
        fragColor = Color;
    }
    

    最后配置VBO和顶点属性指针并绑定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), (void*)0); //配置位置
    glEnableVertexAttribArray(0); //使能属性位置0
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float))); //配置颜色
    glEnableVertexAttribArray(1); //使能属性位置1
    

    Shadr的创建,源码编译和链接成Shader Program需要编写很多重复代码,为了减少工作量,将这部分代码封装成类。
    这个Shader类很简单,只包含了两个函数:

    1. shader函数负责处理Shader的创建,源码编译和链接;
    2. use函数负责激活Shader Program。

    代码如下:

    #ifndef SHADER_H
    #define SHADER_H
    
    #include <glad/glad.h>
    #include <iostream>
    #include <fstream>
    #include <sstream>
    
    using namespace std;
    
    class Shader{
    public:
        unsigned int ID;
        void shader(const char *vertexPath, const char *fragmentPath)
        {
            string vertexCode;
            string fragmentCode;
            ifstream vertexShaderFile;
            ifstream fragmentShaderFile;
            stringstream vertexStream;
            stringstream fragmentStream;
            const char *vertexShaderSource;
            const char *fragmentShaderSource;
            vertexShaderFile.open(vertexPath);
            fragmentShaderFile.open(fragmentPath);
            vertexStream << vertexShaderFile.rdbuf();
            fragmentStream << fragmentShaderFile.rdbuf();
            vertexShaderFile.close();
            fragmentShaderFile.close();
            vertexCode = vertexStream.str();
            fragmentCode = fragmentStream.str();
            vertexShaderSource = vertexCode.c_str();
            fragmentShaderSource = fragmentCode.c_str();
    
            int vertexShader;
            int fragmentShader;
            int success;
            char infoLog[512];
            vertexShader = glCreateShader(GL_VERTEX_SHADER);
            glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
            glCompileShader(vertexShader);
            glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
            if(!success){
                glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
                cout << "ERROR::VERTEXSHADER::COMPILATION_FAILED
    " << infoLog << endl;
            }
            else cout << "VERTEXSHADER_COMPILATION_SUCCESS" << endl;
            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);
                cout << "ERROR::FRAGMENTSHADER::COMPILATION_FAILED
    " << infoLog << endl;
            }
            else cout << "FRAGMENTSHADER_COMPILATION_SUCCESS" << endl;
    
            ID = glCreateProgram();
            glAttachShader(ID, vertexShader);
            glAttachShader(ID, fragmentShader);
            glLinkProgram(ID);
            glGetProgramiv(ID, GL_LINK_STATUS, &success);
            if(!success){
                glGetProgramInfoLog(ID, 512, NULL, infoLog);
                cout << "ERROR::LINKING_FAILED
    " << infoLog << endl;
            }
            else cout << "LINKING_SUCCESS" << endl;
    
            glDeleteShader(vertexShader);
            glDeleteShader(fragmentShader);
        }
        void use()
        {
            glUseProgram(ID);
        }
    };
    
    #endif // SHADER_H
    

    这样在使用时,只需要将其添加到工程中,并添加头文件“shader.h”。

    补:今天看书时有提到,定义class最好不要使用using namespace xx。懒得改了。。。

  • 相关阅读:
    自我介绍
    秋季学期总结
    第七周编程总结
    第六周作业
    第五周编程总结
    第四周编程总结
    第三周作业
    第二周作业
    抓老鼠啊~亏了还是赚了
    币值转换
  • 原文地址:https://www.cnblogs.com/yiqian/p/10776360.html
Copyright © 2011-2022 走看看