计算着色器写入纹理。

6
我已经实现了一个CPU代码,将一个投影纹理复制到3D对象上的一个更大的纹理中,如果您愿意,可以称之为“贴花烘焙”,但现在我需要在GPU上实现它。为此,我希望使用计算着色器,因为在我的当前设置中添加FBO相当困难。 我当前实现的示例图像 这个问题更多地涉及如何使用计算着色器,但对于任何感兴趣的人,这个想法基于用户jozxyqk的答案,可以在这里看到:https://stackoverflow.com/a/27124029/2579996 在我的代码中,被写入的纹理称为_texture,而被投影的纹理称为_textureProj 简单的计算着色器
const char *csSrc[] = {
    "#version 440\n",
    "layout (binding = 0, rgba32f) uniform image2D destTex;\
     layout (local_size_x = 16, local_size_y = 16) in;\
     void main() {\
           ivec2 storePos = ivec2(gl_GlobalInvocationID.xy);\
           imageStore(destTex, storePos, vec4(0.0,0.0,1.0,1.0));\
    }"
};

如您所见,我目前只想将纹理更新为一些任意的(蓝色)颜色。

更新函数

void updateTex(){ 
    glUseProgram(_computeShader);
    const GLint location = glGetUniformLocation(_computeShader, "destTex"); 
    if (location == -1){
        printf("Could not locate uniform location for texture in CS");
    }
    // bind texture
    glUniform1i(location, 0); 
    glBindImageTexture(0, *_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
    // ^second param returns the GLint id - that im sure of. 

    glDispatchCompute(_texture->width() / 16, _texture->height() / 16, 1); 
    glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

    glUseProgram(0);
    printOpenGLError(); // reports no errors.
}

问题 如果我在主程序对象之外调用updateTex(),我看不到任何效果,但是如果我在其作用域内调用它,就像这样:

 glUseProgram(_id); // vert, frag shader pipe
  updateTex();
  // Pass uniforms to shader 
  // bind _textureProj & _texture (latter is the one im trying to update) 
 glUseProgram(0);

然后在渲染时我看到了这个:

问题: 我意识到在主程序对象范围内设置更新方法并不是正确的方法,但这是获得任何视觉结果的唯一方法。我觉得发生的事情是它基本上消除了片段着色器并绘制到屏幕空间中...

我该怎么做才能使其正常工作?(我的主要关注点是能够写入任何内容到纹理并进行更新)

如果需要发布更多代码,请告诉我。


我不确定你所说的“主程序对象作用域”是什么意思。_programObject->activate(); 是做什么的? - GuyRT
你的“主程序范围”是否在单独的线程上?当你在不同线程之间进行调用时,OpenGL 将无法正常工作。 - new Objekt
@GuyRT - 哦,抱歉,也许我在那里使用了错误的术语。_programObject->activate() 的本质是调用 glUseProgram(id),其中 id 是 GLint,我会编辑我的问题。 - mike
@newObjekt 它不在一个单独的线程上,我可能在那里使用了错误的术语,我的意思是在 glUseProgram() 内部。 - mike
1个回答

4
我认为在这种情况下,使用FBO会更容易更快,建议使用它。但问题本身仍然非常重要。
看到一个球体很惊讶,因为你正在将蓝色写入整个纹理(如果纹理大小不是16的倍数,则减去任何边缘位)。我猜这是来自其他代码的结果。
无论如何,似乎你的主要问题是能够从计算着色器中写入纹理,而不是在常规渲染的设置代码之外。我怀疑这与你绑定destTex图像的方式有关。我不确定你的TexUnitactivate方法做什么,但要将GL纹理绑定到图像单元,请执行以下操作:
int imageUnitIndex = 0; //something unique
int uniformLocation = glGetUniformLocation(...);
glUniform1i(uniformLocation, imageUnitIndex); //program must be active
glBindImageTexture(imageUnitIndex, textureHandle, ...);

请参见:

最后,由于你正在使用image2D,所以需要使用GL_SHADER_IMAGE_ACCESS_BARRIER_BIT作为屏障。而GL_SHADER_STORAGE_BARRIER_BIT则是用于 存储缓冲区对象

谢谢您的快速回复。我实际上已经尝试过显式地执行它(编辑了我的帖子以符合您的建议)。我对格式不是很确定,但检查后发现它是GL_RGB,似乎不受load/store方法支持。因此,由于使用GL_RGBA32F时没有glError,我认为这是正确的方法。此外,您现在可以看到我正在使用布局限定符显式绑定到0,但仍然无法正常工作。我怀疑这要么与格式有关(不应该),要么是加载纹理的步骤缺失或其他原因。 - mike
继续:在我看来,我已经完成了一个“教科书式”的实现,所以我将尝试使用FBO(并在另一个线程中发布问题),但我会保留这段代码,并进行注释。如果需要我发布更多的代码,请告诉我,因为现在我有点不知所措,不知道为什么它没有按照预期执行。也许我省略了关键代码,请告诉我(谢谢!)。 - mike
哦,还有一件事:我们看到“蓝色”球的原因只是因为我将updateTex()方法插入了glUseProgram()调用中 - 也就是说,它似乎删除了片段着色器。现在我知道颜色是基于投影顶点着色器vec4 position的,这就是为什么它是蓝色的 - 运行时它会根据投影仪与表面的角度而改变颜色 - 完全错误,但这是我唯一能看到与计算着色器有关的东西... :( - mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接