学习opengl(三)着色器

1GLSL
着色器是使用一种叫做GLSL的类c语言来编写的,GLSL是为图形计算量身定制的,包含一些针对向量和矩阵操作的有用特性
开头是声明版本,接着是输入和输出变量,uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
一个经典的着色器有着下面的结构

#version version_number
in type in_varible_name;
in type in_varible_name;

out type out_varible_name;

uniform type uniform_name;
int main()
{
  //处理输入并进行一些图形操作
  ...
  //输出处理的结果
  out_varible_name=weird_stuff_we_processed;
}

顶点着色器中每个输入变量也称为顶点属性,我们能声明的顶点属性是由上线的,一般由硬件来决定。OpenGL中保证一般有16个包含4分量的顶点属性可用,当然可以用GL_MAX_VERTEX_ATTRIBS来获取具体的上限。

2Uniform
数据类型,GLSL语言有数据类型来指定变量的种类,GLSL中包含了c等其它语言的大部分默认基础数据类型,GLSL也有两种容器类型,分别是向量和矩阵。
而Uniform是一种由cpu向GPU发送数据的方式,但Uniform和顶点属性有些不同,首先,uniform是全局变量,代表他在每个着色器里都是独一无二的,而且可以被任何着色器在任何阶段访问,第二,无论你把uniform设置成什么值,他都会一直保持他的数据,知道它们被重置或者更新。
我们可以在着色器中添加Uniform关键字类型和变量名来声明一个GLSL的uniform。

#version 330 core
out vec4 fragColor ;
uniform vec4 ourColor;//在OpenGL代码中设置此变量

void main()
{
  fragColor = ourColor;
}

在openGL代码中设置uniform变量

float timeValue = glfwGetTime();//获取当前时间的秒数
float greenValue =(sin(timeValue)/2.0f)+0.5f;
int vertexColorLocation =glGetUniformLocation(shaderProgram,"ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation,0.0f,greenValue,0.0f,1.0f);

接下来的话颜色就可以跟着时间变化了

//渲染循环
while(!glfwWindowShouldClose(window))
{
   processInput(window);

   glClearColor(0.2f,0.3f,0.2f,1.0f);
   glClear(GL_COLOR_BUFFER_BIT); 

    //激活着色器
   glUseProgram(shaderProgram);  

   float timeValue =glfwGetTime();
   float greenValue =(sin(timeValue)/2.0f)+0.5f;
   int vertexShaderLocation =glGetUniformLocation(ShaderProgram,"ourColor");
   glUniform4f(vertexShaderLocation,0.0f,greenValue,0.0f,1.0f);
   ...//画你想要的图形
}

uniform对于设置一个在迭代中会改变的属性来说是一个强有力的工具,也是在程序和着色器数据之间交互的一个很好的工具。
3更多属性
如果我们打算把颜色加到顶点数据中去的话,我们把颜色数据加入到vertices数组中,这就要在属性配置上去调整一下。

//顶点数据
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    // 顶部
};

由于现在有了更多的数据要发送到顶点着色器中去,我们要调整一下顶点着色器

#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.0f);
  ourColor = aColor;
}

由于不用uniform来传递数据了,我们还得修改一下片段着色器

#version 330 core
out vec4 FragColor;
in vec3 ourColor;

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

由于我们添加了另一个顶点属性,并且更新了VBO的内存,我们必须重新配置顶点属性指针。

glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(void*)0);
glEnableVertexAttriArray(0);

glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(void*)(3*sizeof(float)));

4着色器类
编写,管理和编译着色器是一件十分麻烦的事情,我们可以将这些方法封装成一个类,来使我们的变成变得简洁一点。

#ifndef SHADER_H
#define SHADER_H

#include<glad/glad.h>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
    unsigned int ID;
    Shader(const GLchar *vertexPath, const GLchar *fragment)
    {
        std::string vertexCode;
        std::string fragmentCode;
        std::ifstream vShaderFile;
        std::ifstream fShaderFile;
        //保证文件正确的抛出异常
        vShaderFile.expection(std::ifstream::FailBit|std::ifstream::BadBit);
        fShaderFile.expection(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();
            //转换数据流到string
            vertexCode =vShaderStream.str();
            fragmentCode =fShaderStream.str();
        }
        catch(std::ifstream::failure e)
        {
            std::cout<<"ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ"<<std::endl;
        }
        const char *vShaderCode = vertexCode.c_str();
        const char *fShaderCode = fragmentCode.c_str();
        //编译着色器
        unsigned int vertex,fragment;
        vertex = glcreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex,1,&vShaderCode,NULL);
        glcompileShader(vertex);
        checkCompileError(vertex,"VERTEX");
        fragment = glcreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment,1,&fShaderCode,NULL);
        glcompileShader(fragment);
        checkCompileError(fragment,"FRAGMENT");
        ID =glCreateProgram();
        glAttachShader(ID,vertex);
        glAttachShader(ID,fragment);
        glLinkProgram(ID);
        checkCompileError(ID,"Program");
        //删除着色器对象
        glDeleteShader(vertex);
        glDeleteShader(fragment);
    }
    void use()
    {
        glUseProgram(ID);
    }
    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(unsigned int shader, std::string type)
    {
        int success;
        char 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;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/shine10076/article/details/82596746