如何使用计算着色器计算值并将它们存储在 3D 纹理中?
Posted
技术标签:
【中文标题】如何使用计算着色器计算值并将它们存储在 3D 纹理中?【英文标题】:How can I use a compute shader to calculate values and store them in a 3D texture? 【发布时间】:2021-04-22 19:38:25 【问题描述】:我正在尝试使用计算着色器进行 3D 物理模拟,但无法将任何内容存储到我的 3D 纹理中。我的着色器编译成功,但是当从 3D 纹理读回任何值时,我得到一个零向量。我之前也没有使用过计算着色器,所以我不确定我是否正确分配工作负载以实现我想要的。
我在这里的一个小例子中隔离了这个问题。基本上,compute.glsl 着色器具有统一的 image3D 并使用 imageStore 将 vec4(1,1,0,1) 写入 gl_WorkGroupID。在 C++ 中,我创建了一个 100x100x100 的 3D 纹理并将其绑定到着色器的制服,然后我调用 glDispatchCompute(100,100,100) - 据我所知,这将创建 1,000,000 个作业/着色器调用,一个用于纹理中的每个坐标。在我的 view.glsl 片段着色器中,我读取了一个随机坐标的值(在本例中为 (3,5,7))并将其输出。我使用这个阴影立方体对象。
我尝试过的所有方法都会输出一个黑色立方体:
这是我的代码(我一直在关注 learnopengl.com,所以除了我扩展了着色器类来处理计算着色器之外,它几乎是相同的样板文件):
#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);
计算.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(顶点着色器):
#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(片段着色器):
#version 430 core
out vec4 FragColor;
layout(rgba8_snorm, binding=0) uniform image3D img;
void main()
FragColor = imageLoad(img, ivec3(3,5,7));
【问题讨论】:
我在您的代码中没有看到任何 glGetError() 调用。你确定你的设置和纹理初始化工作正常吗? @HughFisher - 是的,在我的完整(更复杂的)程序中,我在每次 opengl 调用后调用 glGetError 并且没有错误。我可以快速将它添加到上面的重现代码中,但我怀疑它会出现任何问题。 @HughFisher - glGetError 返回 GL_NO_ERROR 【参考方案1】:原来我错过了对 glBindImageTexture 的调用 - 我认为为了将我的纹理绑定到着色器的图像变量,我需要设置统一并调用 glActiveTexture+glBindTexture 但似乎只需要 glBindImageTexture。
我换了:
computeShader.setInt("img", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, texId)
与:
glBindImageTexture(0, texId, 0, true, 0, GL_WRITE_ONLY, GL_RGBA16);
【讨论】:
以上是关于如何使用计算着色器计算值并将它们存储在 3D 纹理中?的主要内容,如果未能解决你的问题,请参考以下文章