当前位置: 首页 > article >正文

OpenGL(三)——着色器

目录

一、前言

二、Shader 2 Shader

2.1 顶点着色器

2.2 片段着色器

三、APP 2 Shader

 四、顶点颜色属性

五、着色器类C++


一、前言

        着色器Shader是运行在GPU上的小程序,为图形渲染管线的某个特定部分而运行。各阶段着色器之间无法通信,只有输入和输出。着色器程序需要使用GLSL语言编写。GLSL语言转为图形计算量身定制,定义了一些向量和矩阵操作。

        着色器开头需要有声明版本,结束输入、输出、uniform变量、main函数。main是着色器入口点,在函数中需要处理输入变量,然后输出到输出变量中。

二、Shader 2 Shader

2.1 顶点着色器

顶点着色器的输的顶点数据直接接收输入。使用location这一元数据指定输入变量,定义顶点数据该如何管理,才可以在CPU上配置顶点属性,layout (location = 0)表示位置属性。

#version 330 core
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为0

out vec4 vertexColor; // 为片段着色器指定一个颜色输出

void main()
{
    gl_Position = vec4(aPos, 1.0); // 注意我们如何把一个vec3作为vec4的构造器的参数
    vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 把输出变量设置为暗红色
}

2.2 片段着色器

片段着色器,它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

#version 330 core
out vec4 FragColor;

in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)

void main()
{
    FragColor = vertexColor;
}

三、APP 2 Shader

 APP向Opengl的传参属性有三种:

Attribute:属性通道,传递可变参数,如颜色数据、顶点数据,纹理坐标、光照法线

uniform 统一变量通道,传递不变的参数,如变换矩阵,RGBA->YUV

Texture Data:纹理数据

Uniform标识符是一种从CPU到GPU的着色器发送数据方式。Uniform是全局变量,可以被任何着色器程序访问。在片段着色器种使用变量Uniform。

#version 330 core
out vec4 FragColor;

uniform vec4 ourColor; // 在OpenGL程序代码中设定这个变量

void main()
{
    FragColor = ourColor;
}

在APP种如何使用联系着色器种的uniform全局变量?

  1. 找到着色器uniform 属性索引/位置值
  2. 调用函数更新该变量
    while (!glfwWindowShouldClose(window))
    {
        // input
        // -----
        processInput(window);
        // render
        //背景颜色
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        //激活着色器
        glUseProgram(shaderProgram);
        //更新uniform变量
        float timeValue = glfwGetTime();//获取秒级时间
        float greenValue = sin(timeValue) / 2.0f + 0.5f;
        int vertexUniformIndex = glGetUniformLocation(shaderProgram, "ourColor");//索引
        glUniform4f(vertexUniformIndex, 0.0, greenValue, 0.0, 1.0);//更新uniform变量

        //绘制三角形
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

glGetUniformLocation — Returns the location of a uniform variable
GLint glGetUniformLocation(	GLuint program,
 	const GLchar *name);
program:着色器程序
Specifies the program object to be queried.
name: uniform变量名字

glUniform — Specify the value of a uniform variable for the current program object
void glUniform4f(	GLint location,
 	GLfloat v0,
 	GLfloat v1,
 	GLfloat v2,
 	GLfloat v3);

 四、顶点颜色属性

直接使用顶点输入的颜色值来显示:

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

顶点着色器程序:

const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"  //位置属性值0
"layout (location = 1) in vec3 aColor;\n"//颜色属性值1
"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";

更新VBO内存,并重新配置VAO解释器:

    //位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    //颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);

这个图片可能不是你所期望的那种,因为我们只提供了3个颜色,而不是我们现在看到的大调色板。这是在片段着色器中进行的所谓片段插值(Fragment Interpolation)的结果。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置
基于这些位置,它会插值(Interpolate)所有片段着色器的输入变量。比如说,我们有一个线段,上面的端点是绿色的,下面的端点是蓝色的。如果一个片段着色器在线段的70%的位置运行,它的颜色输入属性就会是一个绿色和蓝色的线性结合;更精确地说就是30%蓝 + 70%绿。

五、着色器类C++

#pragma once
#include "glad.h"
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
    unsigned int ID;
    // constructor generates the shader on the fly
    // ------------------------------------------------------------------------
    Shader(const char* vertexPath, const char* fragmentPath)
    {
        // 1. retrieve the vertex/fragment source code from filePath
        std::string vertexCode;
        std::string fragmentCode;
        std::ifstream vShaderFile;
        std::ifstream fShaderFile;
        // ensure ifstream objects can throw exceptions:
        vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        try
        {
            // open files
            vShaderFile.open(vertexPath);
            fShaderFile.open(fragmentPath);
            std::stringstream vShaderStream, fShaderStream;
            // read file's buffer contents into streams
            vShaderStream << vShaderFile.rdbuf();
            fShaderStream << fShaderFile.rdbuf();
            // close file handlers
            vShaderFile.close();
            fShaderFile.close();
            // convert stream into string
            vertexCode = vShaderStream.str();
            fragmentCode = fShaderStream.str();
        }
        catch (std::ifstream::failure& e)
        {
            std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;
        }
        const char* vShaderCode = vertexCode.c_str();
        const char* fShaderCode = fragmentCode.c_str();
        // 2. compile shaders
        unsigned int vertex, fragment;
        // vertex shader
        vertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex, 1, &vShaderCode, NULL);
        glCompileShader(vertex);
        checkCompileErrors(vertex, "VERTEX");
        // fragment Shader
        fragment = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment, 1, &fShaderCode, NULL);
        glCompileShader(fragment);
        checkCompileErrors(fragment, "FRAGMENT");
        // shader Program
        ID = glCreateProgram();
        glAttachShader(ID, vertex);
        glAttachShader(ID, fragment);
        glLinkProgram(ID);
        checkCompileErrors(ID, "PROGRAM");
        // delete the shaders as they're linked into our program now and no longer necessary
        glDeleteShader(vertex);
        glDeleteShader(fragment);
    }
    // activate the shader
    // ------------------------------------------------------------------------
    void use()
    {
        glUseProgram(ID);
    }
    // utility uniform functions
    // ------------------------------------------------------------------------
    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:
    // utility function for checking shader compilation/linking errors.
    // ------------------------------------------------------------------------
    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;
            }
        }
    }
};


类使用:

Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");
...
while(...)
{
    ourShader.use();
    ourShader.setFloat("someUniform", 1.0f);
    DrawStuff();
}

参考:

着色器 - LearnOpenGL CN (learnopengl-cn.github.io)


http://www.kler.cn/a/15921.html

相关文章:

  • More effective C++:杂项
  • Python小试牛刀:第一次爬虫,获取国家编码名称
  • IROS讲座:如何写出受欢迎的论文
  • Win11 终端执行 python xxx.py 没反应
  • 自由学习记录(22)
  • apk反编译修改教程系列-----apk应用反编译中AndroidManifest.xml详细代码释义解析 包含各种权限 代码含义【二】
  • Redis学习笔记01 (数据结构,线程模型,持久化)
  • 分屏视图上线,详情数据秒切换
  • Python小姿势 - # 如何在Python中实现基本的数据类型
  • 如何查看自己是否使用了国产SSL证书?“套牌”SSL证书?
  • GDB 1、超详细的GDB入门笔记,包含演示代码,快速入门
  • Prompt炼丹炉——一系列Prompt自动优化的实践记录
  • python使用公共api下载狗狗图片
  • Cadence基础操作:Schematic编辑
  • vue+element Ui 树型组件tree懒加载+搜索框远程请求数据为平铺类型
  • TF-IDF (BigData, Data Mining)
  • 【LeetCode: 62. 不同路径 | 暴力递归=>记忆化搜索=>动态规划 】
  • 设计模式-适配器模式
  • RabbitMQ 工作队列模式 Work Queue Demo
  • C S S
  • 制药专业转行软件测试,带我的师傅在这干了两年半,最终还是跑路了......
  • 第11届蓝桥杯省赛真题剖析-2020年6月21日Scratch编程初中级组
  • 渗透测试 | 目录扫描
  • 英语基础-名词
  • 基于Mediapipe手势识别
  • 一文吃透Http协议