目标:
有效地读取和写入相同的纹理,就像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());
}
如果这是一个清除的问题,我真的很想知道为什么。改变哪个帧缓冲区被清除似乎没有改变任何事情。我会继续试验在此期间。谢谢!
1条答案
按热度按时间ntjbwcob1#
问题是您创建的纹理的精度太低,无法使指数移动平均计算最终离散为零。
在您的呼叫中:
您使用的是 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本身使用的格式)。