OpenGL乒乓反馈纹理未完全清除自身,留下痕迹

a6b3iqyw  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(135)

目标

有效地读取和写入相同的纹理,就像Shadertoy处理缓冲区一样。

设置:

我有一个基本的反馈系统,有两个纹理,每个纹理都连接到一个帧缓冲区。当我渲染到帧缓冲区1时,我绑定纹理2以在着色器中采样。然后,当我渲染到帧缓冲区2时,我绑定纹理1以在着色器中采样,并重复。最后,我用默认的帧缓冲区和sperate着色器将纹理1输出到整个屏幕。

问题:

这几乎可以像预期的那样工作,因为我可以从着色器中的纹理读取,也可以输出到着色器,创建所需的反馈循环。
问题是,帧缓冲区似乎没有完全清除到黑色。
为了测试,我做了一个简单的拖尾效果。
在shadertoy中,轨迹完全按预期消失:

Live in shadertoy
但在我的应用程序中,痕迹开始消失,但留下了少量:

我的想法是我没有正确地清除帧缓冲区,或者我没有在这个例子中正确地使用GLFW的双缓冲。我已经尝试了清除帧缓冲区的每一种组合,但我肯定在这里遗漏了一些东西。

代码:

这是一个带有移动圆圈的拖尾效果着色器(与上图相同)


# version 330

precision highp float;

uniform sampler2D samplerA; // Texture sampler

uniform float uTime; // current execution time
uniform vec2 uResolution; // resolution of window

void main()
{
    vec2 uv = gl_FragCoord.xy / uResolution.xy; // Coordinates from 0 - 1
    vec3 tex = texture(samplerA, uv).xyz;// Read ping pong texture that we are writing to 

    vec2 pos = .3*vec2(cos(uTime), sin(uTime)); // Circle position (circular motion around screen)
    vec3 c = mix(vec3(1.), vec3(0), step(.0, length(uv - pos)-.07)); // Circle color

    tex = mix(c, tex, .981); // Replace some circle color with the texture color

    gl_FragColor = vec4(tex, 1.0); // Output to texture
}

帧缓冲区和纹理创建:

// -- Generate frame buffer 1 --
glGenFramebuffers(1, &frameBuffer1);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer1);

// Generate texture 1
glGenTextures(1, &texture1);

// Bind the newly created texture
glBindTexture(GL_TEXTURE_2D, texture1);

// Create an empty image
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_FLOAT, 0);

// Nearest filtering, for sampling
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

// Attach output texture to frame buffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);

// -- Generate frame buffer 2 --
glGenFramebuffers(1, &frameBuffer2);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer2);

// Generate texture 2
glGenTextures(1, &texture2);

// Bind the newly created texture
glBindTexture(GL_TEXTURE_2D, texture2);

// Create an empty image
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_FLOAT, 0);

// Nearest filtering, for sampling
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

// Attach texture 2 to frame buffer 2
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);

主回路:

while(programIsRunning){

// Draw scene twice, once to frame buffer 1 and once to frame buffer 2 
for (int i = 0; i < 2; i++)
{
    // Start trailing effect shader program
    glUseProgram(program);

    glViewport(0, 0, platform.windowWidth(), platform.windowHeight());

    // Write to frame buffer 1
    if (i == 0)
    {
        // Bind and clear frame buffer 1
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer1); 
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Bind texture 2 for sampler
        glActiveTexture(GL_TEXTURE0 + 0);
        glBindTexture(GL_TEXTURE_2D, texture2); 
        glUniform1i(uniforms.samplerA, 0);
    }
    else // Write to frame buffer 2
    {
        // Bind and clear frame buffer 2
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer2);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Bind texture 1 for sampler
        glActiveTexture(GL_TEXTURE0 + 0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(uniforms.samplerA, 0);
    }

    // Render to screen
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

// Start screen shader program
glUseProgram(screenProgram);

// Bind default frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glViewport(0, 0, platform.windowWidth(), platform.windowHeight());

// Bind texture 1 for sampler (binding texture 2 should be the same?)
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(uniforms.samplerA, 0);

// Draw final rectangle to screen
glDrawArrays(GL_TRIANGLES, 0, 6);

// Swap glfw buffers
glfwSwapBuffers(platform.window());

}

如果这是一个清除的问题,我真的很想知道为什么。改变哪个帧缓冲区被清除似乎没有改变任何事情。我会继续试验在此期间。谢谢!

ntjbwcob

ntjbwcob1#

问题是您创建的纹理的精度太低,无法使指数移动平均计算最终离散为零。
在您的呼叫中:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_FLOAT, 0);

您使用的是 unsized 内部格式GL_RGBA(第三个参数),这很可能最终导致实际使用GL_RGBA8内部格式。因此,所有通道的精度都是8位。
您可能认为,使用GL_FLOAT作为type参数的自变量会导致分配32位浮点纹理:它不会。type参数用于向OpenGL指示,当/如果您实际指定要上传的数据时,它应该如何解释您的数据(函数的最后一个参数)。您使用0/NULL,这样type参数实际上不会影响调用,因为没有内存可解释为要上传的浮点值。
因此,纹理的精度为每个通道8位,因此每个通道最多可以容纳256个不同的值。
假设在您显示的RGB图像中,每个通道的RGB值为24,我们可以计算OpenGL如何达到该值以及为什么它不会低于该值:
首先,让我们在(0, 0, 0)(24, 24, 24)/255之间做另一轮指数移动平均线,其中因子为0.981
d = (24, 24, 24)/255 * 0.981
如果我们有无限精度,这个值d将是0.09232941176
现在,让我们看看可表示范围[0, 255]内的RGB值接近于:0.09232941176 * 255 = 23.5439999988 .
因此,这个值实际上是(当在[0, 255]离散化中正确舍入到最接近的可表示值时)24 *,这就是它的位置。
为了解决此问题,您可能需要使用更高精度的内部纹理格式,如GL_RGBA32F(这实际上是ShaderToy本身使用的格式)。

相关问题