简体   繁体   中英

How can I use a compute shader to calculate values and store them in a 3D texture?

I'm trying to use a compute shader to do three-dimensional physical simulations but having trouble storing anything into my 3D texture. My shaders compile successfully but when reading back any value from the 3D texture I get a zero vector. I haven't used compute shaders before either so I'm not sure if I'm even distributing the work load properly in order to achieve what I want.

I've isolated the problem in a small example here. Basically the compute.glsl shader has a uniform image3D and uses imageStore to write a vec4(1,1,0,1) into gl_WorkGroupID. In C++ I create a 100x100x100 3D texture and bind it to the shader's uniform, then I call glDispatchCompute(100,100,100) - to my knowledge, this will create 1,000,000 jobs/shader invocations, one for each coordinate in the texture. In my view.glsl fragment shader I read the value of a random coordinate (in this case (3,5,7)) and output that. I use this shade a cube object.

Everything I've tried results in a black cube being output: 在此处输入图像描述

Here's my code (I've been following along with learnopengl.com so it's mostly the same boiler plate stuff except I extended the shader class to handle compute shaders):

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>

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

#include <learnopengl/shader_m.h>

#include <iostream>

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

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

struct vaoinfo
{
    unsigned int VBO, VAO, EBO;
    vaoinfo() : VAO(0), VBO(0), EBO(0)
    {}
};

void create_vao(vaoinfo& info)
{
    glGenVertexArrays(1, &info.VAO);
    glGenBuffers(1, &info.VBO);
    glGenBuffers(1, &info.EBO);
}

void init_vao(vaoinfo& info, float* vertices, int num_vertices, int* indices, int num_indices)
{
    glGenVertexArrays(1, &info.VAO);
    glGenBuffers(1, &info.VBO);
    glGenBuffers(1, &info.EBO);
    glBindVertexArray(info.VAO);
    glBindBuffer(GL_ARRAY_BUFFER, info.VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * num_vertices, vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * num_indices, indices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

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

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    vaoinfo cube;
    create_vao(cube);

    glm::vec3 c(0.5, 0.5, 0.5);
    float verts[24] =
    {
         c.x, -c.y,  c.z,
         c.x,  c.y,  c.z,
        -c.x,  c.y,  c.z,
        -c.x, -c.y,  c.z,

         c.x, -c.y, -c.z,
         c.x,  c.y, -c.z,
        -c.x,  c.y, -c.z,
        -c.x, -c.y, -c.z,
    };

    int indices[36] =
    {
        7, 4, 5,
        5, 6, 7,

        3, 0, 1,
        1, 2, 3,

        2, 6, 7,
        7, 3, 2,

        1, 5, 4,
        4, 0, 1,

        7, 4, 0,
        0, 3, 7,

        6, 5, 1,
        1, 2, 6
    };

    init_vao(cube, verts, 24, indices, 36);

    // Create a 3D texture
    unsigned int texId;
    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_3D, texId);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8_SNORM, 100, 100, 100, 0, GL_RGBA, GL_FLOAT, nullptr);

    // Create shaders
    Shader computeShader("compute.glsl");
    Shader viewShader("coords.glsl", "view.glsl");
    
    while (!glfwWindowShouldClose(window))
    {
        processInput(window);

        computeShader.use();
        computeShader.setInt("img", 0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, texId);
        glDispatchCompute(100, 100, 100);
        glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);

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


        viewShader.use();
        viewShader.setInt("img", 0);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_3D, texId);

        glm::mat4 model = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
        glm::mat4 view = glm::mat4(1.0f);
        glm::mat4 projection = glm::mat4(1.0f);
        model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));
        view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
        projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        
        unsigned int modelLoc = glGetUniformLocation(viewShader.ID, "model");
        unsigned int viewLoc = glGetUniformLocation(viewShader.ID, "view");
        unsigned int projLoc = glGetUniformLocation(viewShader.ID, "projection");
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // render cube
        glBindVertexArray(cube.VAO);
        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

compute.glsl:

#version 430

layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(rgba8_snorm, binding=0) uniform image3D img;

uniform vec3 position;

void main()
{
    ivec3 voxel_coord = ivec3(gl_WorkGroupID);
    imageStore(img, voxel_coord, vec4(1, 1, 0, 1));
}

coords.glsl (vertex shader):

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

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0f);
}

view.glsl (fragment shader):

#version 430 core
out vec4 FragColor;

layout(rgba8_snorm, binding=0) uniform image3D img;

void main()
{
    FragColor = imageLoad(img, ivec3(3,5,7));
}

It turned out that I was missing a call to glBindImageTexture - I thought that in order to bind my texture to the shader's image variable I needed to set the uniform and call glActiveTexture+glBindTexture but it seems only glBindImageTexture is needed.

I replaced:

computeShader.setInt("img", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, texId)

With:

glBindImageTexture(0, texId, 0, true, 0, GL_WRITE_ONLY, GL_RGBA16);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM