在片段着色器问题中读取 image1d

Posted

技术标签:

【中文标题】在片段着色器问题中读取 image1d【英文标题】:Reading image1d in fragment shader problem 【发布时间】:2018-10-31 04:01:33 【问题描述】:

我正在尝试从 sampler1D 的片段着色器中读取 voronoi 图的每个单元格的相应颜色,结果如下:

然后我创建一个观察和透视矩阵并使用键盘在 z 和 x 轴上移动相机,如果我在 x 轴上移动每个单元格仍然有其相应的颜色,但是当我在 z 轴上移动时似乎它丢失了我创建的颜色缓冲区的索引:

沿 z 轴移动之前的 Voronoi 图

沿z轴移动后的Voronoi图

为了检查我在使用相机在 z 轴上移动后是否丢失了片段着色器中的索引,我尝试使用每个点的索引(在客户端随机生成)作为每个单元格的颜色,如下所示(我生成 5 分):

带相机的 Voronoi 图,在使用索引作为颜色在 z 轴上移动之前

带有相机的 Voronoi 图,在使用索引作为颜色在 z 轴上移动之后

而且它不会丢失每个单元格的索引,但我希望能够从我创建的纹理中读取颜色。

这是我的客户端代码:

class VoronoiJFA
    : public Core

public:
    VoronoiJFA()
        : Core(512, 512, "VoronoiJFA"), size_space(512)
    

    virtual void Start() override
    
        srand(time(nullptr));

        shader_points = new Shader("draw_points.vert", "draw_points.frag");
        shader_voronoi_jfa = new Shader("voronoi_jfa.vert", "voronoi_jfa.frag");
        shader_display_voronoi = new Shader("display_voronoi.vert", "display_voronoi.frag");

        size_points = 5;
        size_triangles = 16000;

        eye = vec3(0.0f, 0.0f, 3.0f);
        target = vec3(0.0f, 0.0f, -1.0f);
        up = vec3(0.0f, 1.0f, 0.0f);

        vec4* p = new vec4[size_points];
        vec4* c = new vec4[size_points];
        // Generating  random point for voronoi diagram
        for (size_t i 0 ; i < size_points; i++)
        
            float x (float(rand()) / RAND_MAX * 2.0f - 1.0f) ;
            float y (float(rand()) / RAND_MAX * 2.0f - 1.0f) ;
            //the "i" variable is used to store the index of each point to reference the color in the display_voronoi.frag shader
            p[i] = vec4(x, y, 0.0f, i);

            float r float(rand()) / RAND_MAX ;
            float g float(rand()) / RAND_MAX ;
            float b float(rand()) / RAND_MAX ;
            c[i] = vec4(r, g, b, 1.0f);
        

        // The space_texture is where i'm gonna paint voronoi using offscreen rendering for voronoi JFA
        glCreateTextures(GL_TEXTURE_2D, 1, &space_texture);
        glTextureStorage2D(space_texture, 1, GL_RGBA32F, size_space, size_space);
        glBindImageTexture(0, space_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);

        // Here i store the colors for each voronoi cell
        glCreateTextures(GL_TEXTURE_1D, 1, &colors_texture);
        glTextureStorage1D(colors_texture, 1, GL_RGBA32F, size_points);
        glTextureSubImage1D(colors_texture, 0, 0, size_points, GL_RGBA, GL_FLOAT, c);
        glBindTextureUnit(2, colors_texture);


        // This is the fbo for the offscreen rendering to draw generate voronoi
        glCreateFramebuffers(1, &framebuffer_object);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
        glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, space_texture, 0);
        glDrawBuffer(GL_COLOR_ATTACHMENT0);

        glCreateBuffers(1, &vertex_buffer_points);
        glNamedBufferStorage(vertex_buffer_points, sizeof(vec4) * size_points, p, GL_DYNAMIC_STORAGE_BIT);
        glCreateVertexArrays(1, &vertex_array_points);
        glBindVertexArray(vertex_array_points);
        glVertexArrayAttribFormat(vertex_array_points, 0, 4, GL_FLOAT, GL_FALSE, 0);
        glEnableVertexArrayAttrib(vertex_array_points, 0);
        glVertexArrayAttribBinding(vertex_array_points, 0, 0);
        glVertexArrayVertexBuffer(vertex_array_points, 0, vertex_buffer_points, 0, sizeof(vec4));

        Window::current->setColor(vec4(-1000000000.0f));
        delete[] p;
        delete[] c;
    

    virtual void Update() override
    
        static float time 0.0f ;
        time += Time::deltaTime;

        if (Input::getKeyDown(KeyCode::Escape))
            Window::current->shouldClose(true);

        // Camera controls
        if (Input::getKey(KeyCode::A))
            eye = eye - normalize(cross(target, up)) * Time::deltaTime * 1.0f;
        if (Input::getKey(KeyCode::D))
            eye = eye + normalize(cross(target, up)) * Time::deltaTime * 1.0f;
        if (Input::getKey(KeyCode::S))
            eye = eye - target * Time::deltaTime * 1.0f;
        if (Input::getKey(KeyCode::W))
            eye = eye + target * Time::deltaTime * 1.0f;

        mat4 M;
        mat4 V;
        V = mat4::lookAt(eye, eye + target, up); // Virtual camera
        mat4 P;
        P = mat4::perspective(radians(45.0f), float(Window::current->getWidth()) / Window::current->getHeight(), 0.1f, 100.0f); // projection matrix

        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
        vec4 distance(-1000000000.0f);
        glClearBufferfv(GL_COLOR, 0, &distance[0]);
        glViewport(0, 0, size_space, size_space);
        glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
        shader_points->use(); // Here i just paint the points generated previously in a texture 2D
        glUniform1f(0, time);
        vec2 mouse = (vec2(Input::mousePosition.x, Input::mousePosition.y) / vec2(Window::current->getWidth(), Window::current->getHeight())) * 2.0f - 1.0f;
        glUniform2fv(1, 1, &mouse[0]);
        glUniformMatrix4fv(2, 1, GL_FALSE, &M[0][0]);
        glUniformMatrix4fv(3, 1, GL_FALSE, &V[0][0]);
        glUniformMatrix4fv(4, 1, GL_FALSE, &P[0][0]);
        glBindVertexArray(vertex_array_points);
        glDrawArrays(GL_POINTS, 0, size_points);
        glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);

        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glViewport(0, 0, size_space, size_space);
        int step size_space / 2 ;
        while(step >= 1)
        
            shader_voronoi_jfa->use(); // Here i compute the voronoi diagram using JFA
            glUniform1i(2, step);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
            step /= 2;
        
        step =  size_space / 2 ;
        while(step >= 1)
        
            shader_voronoi_jfa->use(); // Again using JFA to eliminate "islands"
            glUniform1i(2, step);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
            step /= 2;
        

        glViewport(0, 0, Window::current->getWidth(), Window::current->getHeight());
        shader_display_voronoi->use(); // Display voronoi diagram
        glUniform1i(0, size_space);
        glUniform1i(1, size_points);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    virtual void End() override
    
        delete shader_points;
        delete shader_voronoi_jfa;

        glDeleteBuffers(1, &vertex_buffer_points);
        glDeleteVertexArrays(1, &vertex_array_points);

        glDeleteTextures(1, &space_texture);
        glDeleteTextures(1, &colors_texture);


        glDeleteFramebuffers(1, &framebuffer_object);
    

private:
    Shader* shader_points;
    Shader* shader_voronoi_jfa;
    Shader* shader_display_voronoi;

    GLuint vertex_buffer_points;
    GLuint vertex_array_points;

    GLuint space_texture;
    GLuint colors_texture;

    GLuint framebuffer_object;

    int size_space;
    int size_points;
    int size_triangles;

    vec3 eye;
    vec3 target;
    vec3 up;
;

#if 1
CORE_MAIN(VoronoiJFA)
#endif

我的着色器是这些:

draw_points.vert

#version 450 core

layout(location = 0) in vec4 a_points;

layout(binding = 1, rgba32f) uniform image1D image_points;

layout(location = 0) uniform float u_time;
layout(location = 1) uniform vec2 u_mouse;
layout(location = 2) uniform mat4 M;
layout(location = 3) uniform mat4 V;
layout(location = 4) uniform mat4 P;

out vec4 color;

void main()

    vec4 vertex = a_points;
    gl_Position = P * V * M * vec4(vertex.xy, 0.0, 1.0);
    color.x = vertex.a; // here i put the index of each point to reference the color

draw_points.frag

#version 450 core

in vec4 color;
out vec4 FragColor;

void main()

    // Here i paint the points with its corresponding fragcoord and in the alpha i'm storing the index of each point
    FragColor = vec4(gl_FragCoord.xy, 0.0, color.x);

display_voronoi.vert

#version 450 core

out vec2 uv;

void main()

    vec2 v[4] = vec2[4]
    (
        vec2(-1.0, -1.0),
        vec2( 1.0, -1.0),
        vec2(-1.0,  1.0),
        vec2( 1.0,  1.0)
    );

    vec4 p = vec4(v[gl_VertexID], 0.9998, 1.0);
    gl_Position = p;

display_voronoi.frag

#version 450 core

layout(binding = 0, rgba32f) uniform image2D space; // here it's voronoi    
layout(binding = 2) uniform sampler1D colors; // color buffer


layout(location = 0) uniform int u_spaceSize;
layout(location = 1) uniform int points_size;

out vec4 FragColor;

void main()

    float index = imageLoad(space, ivec2(gl_FragCoord.xy)).a; // here i get the index of each cell
    vec4 color = texelFetch(colors, int(index), 0); // using the index to get color     
FragColor = color; //vec4(index / (points_size - 1)); The commented part is my "debugger" if i use the index divided by the number of points, it paints in gray scale

【问题讨论】:

只是一种预感 - 如果您将 int(index) 更改为 int(index + 0.5) 会怎样? 【参考方案1】:

您的问题发生是因为您在屏幕空间上对颜色进行采样,使用片段坐标,无论您执行什么缩放都将保持不变(它们将始终是您的窗口坐标)。因此,当您执行缩放时,您不会得到实际的缩放,而只是减少这些片段坐标将采样的索引范围。您应该在第一阶段对颜色进行采样,并将它们存储在 g 缓冲区中,就像处理索引一样。

【讨论】:

但这并不能解释黑白 voronoi(最后两张图像),正如您所看到的,我缩小了并且我没有丢失索引。我用 5 种颜色生成 5 个点,因此每个点在其 alpha 通道(0、1、2、3 和 4)中都有一个索引,然后我在纹理 2D 中绘制这些点,但在绘制点的顶点着色器中,我首先通过索引在一个输出变量中,然后我将我的点乘以这样 gl_Position = PVM*vec4(point.xyz, 1.0) 所以基本上我在移动我的点,因此 voronoi 细胞移动 @JacobCerón 我认为您生成的调试图像的黑白图像是为了确保它们是正确的索引,据我从您的帖子中了解到。你甚至说它是这样工作的。 我已经解决了我的问题,问题是 NVIDIA 驱动程序。我在 Linux Ubuntu 16 中,我有来自 nvidia-324(专有,经过测试)的 NVIDIA 二进制驱动程序版本 384.130 和来自 xserver-xorg-video-nouveau(开源)的 X.Org X 服务器 -Nouveau 显示驱动程序,并且使用 nouveau 它可以正常工作,但是如果我更改为 nvidia 二进制驱动程序,一切都会变糟。所以我认为 Nvidia 二进制驱动程序在 opengl 上有一些错误,或者实现真的不同,这只是我的看法。你怎么看? 但是我仍然需要帮助,nvidia驱动有什么问题? @Nadir 查看第一张和第二张图片中的颜色,我猜这些图片看起来是一样的。

以上是关于在片段着色器问题中读取 image1d的主要内容,如果未能解决你的问题,请参考以下文章

如何在着色器之后读取像素?

为啥保守光栅化无法为某些三角形调用片段着色器?

GLSL:无法从 FBO 读取纹理并使用片段着色器渲染到另一个 FBO

使用制服时 Xamarin OpenGL 片段着色器的奇怪行为

如果几何着色器处于活动状态,如何将信息从顶点着色器传递到片段着色器?

片段着色器是不是处理来自顶点着色器的所有像素?