从计算着色器更新顶点

Updating vertices from compute shader

据我所知(如果我错了请纠正我)我可以通过将计算着色器和顶点着色器绑定到同一个缓冲区来共享数据。

顶点程序能够显示我绘制为点的粒子,但它们未被计算着色器更新。我假设我只是错过了一些明显的东西,但我一辈子都看不到它。

片段着色器:

#version 430 core

// The color of the line
uniform vec4 u_color;

out vec4 FragColor;

void main()
{
    FragColor = u_color;
}

顶点着色器:

#version 430 core
layout (location = 0) in vec2 aPos;

// TODO: Refactor into using a UBO
uniform mat4 model;


void main()
{
    gl_Position = model * vec4(aPos, 0.0, 1.0);
}

计算着色器:

#version 430 core

struct Particle{
    vec2 pos;
};

layout(std430, binding = 1) buffer particleBuffer
{
    Particle particles[];
};

layout(local_size_x = 1024, local_size_y = 1, local_size_z = 1) in;

void main()
{
    uint i = gl_GlobalInvocationID.x;

    particles[i].pos += vec2(0.01, 0.0); //particles[i].pos + vec2(0.01, 0.0);
}

主要:


// Your First C++ Program

#include <iostream>
#include <cmath>

#include "../include/glad/glad.h" 
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include "Shader.hpp" 
#include "camera.h"
#include "Arrow.hpp"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>


#include <iostream>

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

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 800;


int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_SAMPLES, 4);
    // glEnable(GL_MULTISAMPLE);  

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);


    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // configure global opengl state
    // -----------------------------
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_MULTISAMPLE); // enabled by default on some drivers, but not all so always enable to make sure

    // build and compile our shader zprogram
    // ------------------------------------
    Shader particleShader("../shaders/particle.vert", "../shaders/particle.frag", "../shaders/particle.glsl");



    particleShader.use();
    glm::mat4 model = glm::mat4(1.0f);
    particleShader.setMat4("model", model);

    int numberOfParticles = 1024;
    float particles[numberOfParticles* 2];
    glPointSize(2.0f);

    for(int i = 0; i < numberOfParticles * 2; i++){
        // TODO: Use propeor good randomness instead...
        particles[i] = static_cast <float> ( 2 * rand()) / static_cast <float> (RAND_MAX);
    }
    unsigned int PARTICLE_VAO, PARTICLE_VBO;
    glGenVertexArrays(1, &PARTICLE_VAO);
    glGenBuffers(1, &PARTICLE_VBO);

    glBindVertexArray(PARTICLE_VAO);

    glBindBuffer(GL_ARRAY_BUFFER, PARTICLE_VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(particles), particles, GL_DYNAMIC_DRAW);

    // position attribute
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);

    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, PARTICLE_VBO);




    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {


        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

        // activate shader
        particleShader.use();
        glBindVertexArray(PARTICLE_VAO);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, PARTICLE_VBO);
        glDispatchCompute(numberOfParticles / 1024, 1, 1);

        glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);

        particleShader.setVec4f("u_color", 1.0f, 1.0f, 1.0f, 1.0f);
        glDrawArrays(GL_POINTS, 0, numberOfParticles);



        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &PARTICLE_VAO);
    glDeleteBuffers(1, &PARTICLE_VBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}
#ifndef SHADER_H
#define SHADER_H

#include "../include/glad/glad.h" 
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#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, const char* computePath = "")
    {
        // 1. retrieve the vertex/fragment source code from filePath
        std::string vertexCode = loadSourceCode(vertexPath);
        unsigned int vertex = compileShaderCode("VERTEX", vertexCode);
        
        std::string fragmentCode = loadSourceCode(fragmentPath);
        unsigned int fragment = compileShaderCode("FRAGMENT", fragmentCode);

        unsigned int compute; 

        // shader Program
        ID = glCreateProgram();
        glAttachShader(ID, vertex);
        glAttachShader(ID, fragment);

        if(computePath != ""){
            std::string computeCode = loadSourceCode(computePath);
            compute = compileShaderCode("COMPUTE", computeCode);
        }

        glLinkProgram(ID);
        checkCompileErrors(ID, "PROGRAM");
        // delete the shaders as they're linked into our program now and no longer necessary
        glDeleteShader(vertex);
        glDeleteShader(fragment);

        if(computePath != ""){
            glDeleteShader(compute);
        }

    }

    // 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); 
    } 
    // ------------------------------------------------------------------------
    void setVec2f(const std::string &name, float value1, float value2) const
    { 
        glUniform2f(glGetUniformLocation(ID, name.c_str()), value1, value2); 
    }
    // ------------------------------------------------------------------------
    void setVec3f(const std::string &name, float value1, float value2, float value3) const
    { 
        glUniform3f(glGetUniformLocation(ID, name.c_str()), value1, value2, value3); 
    }
    // ------------------------------------------------------------------------
    void setVec4f(const std::string &name, float value1, float value2, float value3, float value4) const
    { 
        glUniform4f(glGetUniformLocation(ID, name.c_str()), value1, value2, value3, value4); 
    }
    // ------------------------------------------------------------------------
    void setMat4(const std::string &name, glm::mat4 value) const
    { 
        glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, glm::value_ptr(value)); 
    }

private:

    unsigned int compileShaderCode(std::string type, std::string sourceCode){
        const char* shaderCode = sourceCode.c_str();
        unsigned int shader;
        if (type == "VERTEX"){
            shader = glCreateShader(GL_VERTEX_SHADER);
        } else if (type == "FRAGMENT"){
            shader = glCreateShader(GL_FRAGMENT_SHADER);
        } else if (type == "COMPUTE"){
            shader = glCreateShader(GL_COMPUTE_SHADER);
        } else {
            std::cout << "ERROR::UNKNOWN_SHADER_TYPE '" << type << "'" << std::endl;
        }
        
        glShaderSource(shader, 1, &shaderCode, NULL);
        glCompileShader(shader);
        checkCompileErrors(shader, type);
        return shader;
    }

    std::string loadSourceCode(const char* shaderPath) {
        std::string shaderCode;
        std::ifstream shaderFile;
        // ensure ifstream objects can throw exceptions:
        shaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
        try 
        {
            // open files
            shaderFile.open(shaderPath);
            std::stringstream shaderStream;
            // read file's buffer contents into streams
            shaderStream << shaderFile.rdbuf();
            // close file handlers
            shaderFile.close();
            // convert stream into string
            return shaderStream.str();
        }
        catch (std::ifstream::failure& e)
        {
            std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ '" << shaderPath <<"'" << std::endl;
            return "";
        }
    }

    // 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;
            }
        }
    }
};
#endif

参见OpenGL 4.6 API Core Profile Specification - 7.3 Program Objects

[...]

Linking can fail for a variety of reasons [...], as well as any of the following reasons:

[...]

  • program contains objects to form a compute shader (see section 19) and, program also contains objects to form any other type of shader.

您需要创建 2 个独立的着色器程序。一种用于原始渲染(顶点和片段着色器),一种用于计算顶点(计算着色器):

class Shader
{
public:
    unsigned int ID;
    
    Shader(const char* vertexPath, const char* fragmentPath)
    {
        // 1. retrieve the vertex/fragment source code from filePath
        std::string vertexCode = loadSourceCode(vertexPath);
        unsigned int vertex = compileShaderCode("VERTEX", vertexCode);

        std::string fragmentCode = loadSourceCode(fragmentPath);
        unsigned int fragment = compileShaderCode("FRAGMENT", fragmentCode);

        // shader Program
        ID = glCreateProgram();
        glAttachShader(ID, vertex);
        glAttachShader(ID, fragment);

        glLinkProgram(ID);
        checkCompileErrors(ID, "PROGRAM");
        glDeleteShader(vertex);
        glDeleteShader(fragment);
    }
    Shader(const char* computePath)
    {
        std::string computeCode = loadSourceCode(computePath);
        unsigned int compute; compute = compileShaderCode("COMPUTE", computeCode);

        // shader Program
        ID = glCreateProgram();
        glAttachShader(ID, compute);

        glLinkProgram(ID);
        checkCompileErrors(ID, "PROGRAM");
        glDeleteShader(compute);
    }

    // [...]
Shader particleComputeShader("../shaders/particle.glsl");
Shader particleShader("../shaders/particle.vert", "../shaders/particle.frag");

计算顶点后切换着色器程序:

particleComputeShader.use();
glBindVertexArray(PARTICLE_VAO);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, PARTICLE_VBO);
glDispatchCompute(numberOfParticles / 1024, 1, 1);

glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);

particleShader.use();
particleShader.setVec4f("u_color", 1.0f, 1.0f, 1.0f, 1.0f);
glDrawArrays(GL_POINTS, 0, numberOfParticles);