OpenGL / OpenTK - несколько кадровых буферов

Я хочу выполнить два этапа постобработки для моего алгоритма обнаружения краев: 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 этапа постобработки?


person enne87    schedule 03.08.2016    source источник


Ответы (1)


Что ж, ответ - визуализировать сцену в квадроцикле, хотя это не окончательное изображение, которое должно выводиться на дисплей.

Итак, шаги для выполнения нескольких почтовых процессов:

-> Bind 1st render texture
-> Draw scene

-> Bind 2nd render texture
-> Feed 1st render texture to a shader and draw quad

-> Bind 1st render texture
-> Feed 2nd render texture to another shader and draw quad

-> Bind 2nd render texture
-> Feed 1st render texture to a third shader and draw quad

...
-> Unbind
-> Draw quad

(см. Постобработка и результирующая текстура)

Таким образом, приведенный выше код правильный, отсутствует только отрисовка четырехугольника после первого фреймбуфера (на случай, если это понадобится кому-то еще).

person enne87    schedule 03.08.2016