Я хочу выполнить два этапа постобработки для моего алгоритма обнаружения краев: 1. Найти края с помощью алгоритма обнаружения Sobel-Edge 2. Утончить края с помощью морфологического утонения
Итак, чего я хочу добиться, так это визуализировать всю сцену в отдельном буфере кадра, сэмплировать созданную текстуру, чтобы найти все края, и, наконец, снова сэмплировать вновь созданную текстуру, чтобы сузить края.
Поэтому я предположил, что мне нужны три фреймбуфера (один для определения границ, один для прореживания и стандартный буфер кадра) для выполнения этой задачи. Первый дополнительный буфер кадра использует три текстуры, а именно цвет, нормали и глубину. Второй дополнительный буфер кадра должен использовать сгенерированные изображения первого буфера кадра для прореживания и вывода его снова.
Если я использую два фреймбуфера (fb по умолчанию и fb для определения границ), все работает нормально. Но как только я добавляю дополнительный третий fb, текстура, сгенерированная этим третьим фреймбуфером, не отображается. Вместо этого отображается черное окно.
Итак, вот код для инициализации первого фреймбуфера:
private void GenerateFramebuffer()
{
//Generate framebuffer
framebuffer = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, framebuffer);
// create a RGBA color texture
GL.GenTextures(1, out textureColorBuffer);
GL.BindTexture(TextureTarget.Texture2D, textureColorBuffer);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
glControl1.Width, glControl1.Height,
0, (PixelFormat)PixelInternalFormat.Rgba, PixelType.UnsignedByte,
IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
GL.BindTexture(TextureTarget.Texture2D, 0);
// create a RGBA color texture for normals
... generated like texture above
// create a depth texture
... generated like texture above
////Create color attachment texture
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, textureColorBuffer, 0);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment1, normalBuffer, 0);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, depthBuffer, 0);
DrawBuffersEnum[] bufs = new DrawBuffersEnum[2] { (DrawBuffersEnum)FramebufferAttachment.ColorAttachment0, (DrawBuffersEnum)FramebufferAttachment.ColorAttachment1 };
GL.DrawBuffers(bufs.Length, bufs);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
}
Все работает хорошо, никаких проблем не возникает. Второй фреймбуфер создается одинаково, за исключением того, что мне нужно только одно прикрепление цвета:
private void GenerateFramebuffer2()
{
//Generate framebuffer
framebuffer2 = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, framebuffer2);
// create a RGBA color texture
GL.GenTextures(1, out edgeBuffer);
GL.BindTexture(TextureTarget.Texture2D, edgeBuffer);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
glControl1.Width, glControl1.Height,
0, (PixelFormat)PixelInternalFormat.Rgba, PixelType.UnsignedByte,
IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
GL.BindTexture(TextureTarget.Texture2D, 0);
////Create color attachment texture
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, edgeBuffer, 0);
DrawBuffersEnum[] bufs = new DrawBuffersEnum[1] { (DrawBuffersEnum)FramebufferAttachment.ColorAttachment0};
GL.DrawBuffers(bufs.Length, bufs);
//No need for renderbuffer object since we don't need stencil buffer yet
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
}
А теперь часть рендеринга: сначала я привязываю первый буфер кадра и использую шейдер фрагмента для вывода нормалей и значений цвета в два вложения:
GL.BindFramebuffer(FramebufferTarget.Framebuffer, framebuffer);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // We're not using stencil buffer so why bother with clearing?
GL.Enable(EnableCap.DepthTest);
Shader.Use();
Model.Draw();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
Затем я привязываю второй fb и использую текстуры, которые сгенерировал предыдущий фреймбуфер:
GL.BindFramebuffer(FramebufferTarget.Framebuffer, framebuffer2);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // We're not using stencil buffer so why bother with clearing?
//Find edges and put them in separate texture (colors not needed here)
edgeDetectionShader.Use();
GL.ActiveTexture(TextureUnit.Texture1);
GL.BindTexture(TextureTarget.Texture2D, normalBuffer);
GL.Uniform1(GL.GetUniformLocation(edgeDetectionShader.program, "normalTexture"), 1);
GL.ActiveTexture(TextureUnit.Texture2);
GL.BindTexture(TextureTarget.Texture2D, depthBuffer);
GL.Uniform1(GL.GetUniformLocation(edgeDetectionShader.program, "depthTexture"), 2);
И, наконец, я снова привязываюсь к фреймбуферу по умолчанию и рисую сгенерированные текстуры на простом квадроцикле:
/////////////////////////////////////////////////////
// Bind to default framebuffer again and draw the
// quad plane with attched screen texture.
// //////////////////////////////////////////////////
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
// Clear all relevant buffers
GL.ClearColor(1, 1, 1, 1); // Set clear color to white
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Disable(EnableCap.DepthTest); // We don't care about depth information when rendering a single quad
//Combine edges with color values
finalImageProzessingShader.Use();
GL.BindVertexArray(quadVAO);
//Testing purpose: Send color texture to the shader
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, textureColorBuffer);
GL.Uniform1(GL.GetUniformLocation(finalImageProzessingShader.program, "screenTexture"), 0);
//Testing purpose: Send normal texture to the shader
GL.ActiveTexture(TextureUnit.Texture1);
GL.BindTexture(TextureTarget.Texture2D, normalBuffer);
GL.Uniform1(GL.GetUniformLocation(finalImageProzessingShader.program, "normalTexture"), 1);
//Testing purpose: Send depth texture to the shader
GL.ActiveTexture(TextureUnit.Texture3);
GL.BindTexture(TextureTarget.Texture2D, depthBuffer);
GL.Uniform1(GL.GetUniformLocation(finalImageProzessingShader.program, "depthTexture"), 2);
//Send the texture of fb2 to shader (generates black screen)
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, edgeBuffer);
GL.Uniform1(GL.GetUniformLocation(finalImageProzessingShader.program, "depthTexture"), 3);
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
GL.BindVertexArray(0);
Таким образом, вывод цветовой текстуры, текстуры глубины или нормальной текстуры, сгенерированной первым кадровым буфером, отлично работает, но если я выведу текстуру, сгенерированную вторым кадровым буфером, я получу черный экран. Я также пробовал это с TextureUnit.Texture4 при использовании edgeBuffer, но это тоже не работает.
Это вообще правильный подход, когда я хочу выполнить 2 этапа постобработки?