Home > Back-end >  Updating vertices from compute shader
Updating vertices from compute shader

Time:10-09

As I understand it (correct me if I'm wrong) I can share data between a compute shader and a vertex shader by binding both to the same buffer.

The vertex program is able to display my particles which are drawn as points but they are not being updated by the compute shader. I assume that I've simply missed something obvious but I can't for the life of me see it.

Fragment Shader:

#version 430 core

// The color of the line
uniform vec4 u_color;

out vec4 FragColor;

void main()
{
    FragColor = u_color;
}

Vertex Shader:

#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);
}

Compute Shader:

#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);
}

Main:


// 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

CodePudding user response:

See 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.

You need to create 2 separate shader programs. One for primitive rendering (vertex and fragment shaders) and one for computing vertices (compute shader):

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");

Switch the shader program after computing the vertices:

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);
  • Related