ThreeNamesGrace
ThreeNamesGrace

Reputation: 31

OpenTK render objects outside of Viewport to Framebuffer's texture

I am working on a Windows Form application, I am drawing rectangles onto a GLControl in one tab.

After I'm done drawing the rectangles, I am trying to draw them into another GLControl that is inside another tab.

Currently, for the first tab I am using a lot of for loops for each kind of object that is created (to display them accordingly); to make the code tidier, and more efficient, I want to draw everything on the first tab into a Texture bound to a Framebuffer, and later, use that texture on the other GLControls to draw over it without damaging the first layout of rectangles.

To debug after writing into the Texture, I converted the Texture into a Bitmap, and saved the Bitmap as a JPG and looked at the result using the Windows' native image visualizer.

The problem is that only the stuff that is inside the Viewport is drawn into the image, so the half of some rectangles and the rectangles outside of the Viewport are not being drawn, and everything out of the Viewport's bounds are just zeros (found out using the debugger of Visual Studio and copying and pasting the image into PowerPoint).

I already tinkered with the Viewport's x, y, width and height values but nothing changed, only made the background get drawn further out, but the rectangles out of bounds are not drawn into the image.

These are the results that I am getting so far:

This one works well when it is inside the Viewport:

enter image description here

This happens when the image is in the lower left corner (as you can see, there are transparent pixels given that all of their RGBA values are zero:

enter image description here

And this happens when the image is in the upper right corner:

enter image description here

This behavior is given due to my approach to make the texture/image; because there are going to be objects attached to the blue points, I am only reading the pixels that are close to the group of rectangles, giving this clipped appearance (instead of reading the all the pixels of the whole texture).

If it is not clear what I would like to do, I want to render all my objects that are inside and outside the viewport, so I can use the texture on other GLControls.

This is my code:

Button event to create the Framebuffer and do more actions:

private void buttonLoadFrameBuffer_Click(object sender, EventArgs e)
{ 
    this.Enabled = false;
    if (V.tunnelList.Count > 0)
    {
        CalculateTextureSize(
            out V.FrameBufferMáxX,
            out V.FrameBufferMínX,
            out V.FrameBufferMáxY,
            out V.FrameBufferMínY,
            V.tunnelList
        );

        V.FrameBuffer = new FrameBuffer(
            V.FrameBufferMáxX,
            V.FrameBufferMínX,
            V.FrameBufferMáxY,
            V.FrameBufferMínY
        );

        RenderScene();
        CaptureFramebufferData();   
    }

    this.Enabled = true;
}

My code to render the scene:

private void RenderScene()
{
    int width= glControlLayout.Width;
    int height= glControlLayout.Height;

    GL.BindFramebuffer(FramebufferTarget.Framebuffer, V.FrameBuffer.FramebufferHandle);
    GL.Viewport(
        0, //x
        0, //y
        width,
        height
    );

    GL.ClearColor(245f / 255f, 244f / 255f, 239f / 255f, 1f);
    GL.Clear(ClearBufferMask.ColorBufferBit);

    D.Matrix4 projectionMatrix = D.Matrix4.CreateOrthographicOffCenter(
        0,
        width,
        0,
        height,
        -1.0f,
        1.0f
    );
    this.ShaderProgram.SetUniform("Transformation", true, projectionMatrix);

    DrawObjects();

    V.FrameBuffer.Finalized = true;

    GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);

    glControlLayout.Refresh();
}

My code to get the framebuffer data:

 private void CaptureFramebufferData()
 {
     int width= V.FrameBufferMáxX - V.FrameBufferMínX;
     int heigth = V.FrameBufferMáxY - V.FrameBufferMínY;

     GL.BindFramebuffer(FramebufferTarget.Framebuffer, V.FrameBuffer.FramebufferHandle);
     
     byte[] pixels = new byte[width * height * 4];
     GL.ReadPixels(
         V.FrameBufferMínX,
         V.FrameBufferMínY,
         width,
         height,
         OpenTK.Graphics.OpenGL.PixelFormat.Bgra,
         PixelType.UnsignedByte,
         pixels
     );
     GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
     UpdateTexture(pixels, width, height);
 }

My code to turn the texture into a bitmap and later into a JPG:

private void UpdateTexture(byte[] pixels, int width, int height)
{
    GL.BindTexture(TextureTarget.Texture2D, V.FrameBuffer.TextureColorHandle);   
        
    Bitmap bitmap = new Bitmap(
        width,
        height,
        System.Drawing.Imaging.PixelFormat.Format32bppArgb
    );

    BitmapData bitmapData = bitmap.LockBits(
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
        ImageLockMode.WriteOnly,
        bitmap.PixelFormat
    );

    IntPtr ptr = bitmapData.Scan0;

    Marshal.Copy(pixels, 0, ptr, pixels.Length);  

    bitmap.UnlockBits(bitmapData);

    bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);

    bitmap.Save("D:\\some\\folder\\image.jpg");
}

Upvotes: 0

Views: 44

Answers (0)

Related Questions