1

I want to draw multiple images in to framebuffer and then also draw framebuffer on screen. I also want to ReadPixels from frame buffer to store image on disk.

Is there some tutorial or example how to use framebuffer in OpenTK. I found one example but is not in C# openTK https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/minimalfbo/minimalfbo.c

Currently my code crashes application, and the ReadPixels returns an empty image.

namespace test
{
    class Class1
    {
        int FramebufferName = -1;
        int depthrenderbuffer;
        int fbo_width = 0;
        int fbo_height = 0;
        public bool createImage = false;

        DirectBitmap masterBitmap = null;
        object masterBitmapSync = new object();
        public int renderedTexture = 0;

        public override void OnRender()
        {
            //https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/minimalfbo/minimalfbo.c
            calculateMasterBitmapSize();

            #region init
            if (FramebufferName == -1)
            {
                //do this only once

                //Framebuffer
                GL.GenFramebuffers(1, out FramebufferName);
                GL.BindFramebuffer(FramebufferTarget.Framebuffer, FramebufferName);
                checkGlError();

                //Color renderbuffer.
                lock (masterBitmapSync)
                {
                    if (masterBitmap != null)
                        createTexture();
                }
                if (renderedTexture <= 0 || FramebufferName < 0)
                {
                    Console.WriteLine("ERROR:");
                }

                checkGlError();
                GL.BindTexture(TextureTarget.Texture2D, renderedTexture);
                checkGlError();
                //GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, renderedTexture);
                //checkGlError();

                /* Storage must be one of: */
                /* GL_RGBA4, GL_RGB565, GL_RGB5_A1, GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8. */
                //GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent16, fbo_width, fbo_height);
                //checkGlError();
                //GL.FramebufferRenderbuffer(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, RenderbufferTarget.Renderbuffer, renderedTexture);
                //checkGlError();

                /* Depth renderbuffer. */
                GL.GenRenderbuffers(1, out depthrenderbuffer);
                GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthrenderbuffer);
                GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent16, fbo_width, fbo_height);
                GL.FramebufferRenderbuffer(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.DepthAttachment, RenderbufferTarget.Renderbuffer, depthrenderbuffer);
                checkGlError();

                GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
                checkGlError();

                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, fbo_width, fbo_height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);

                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
                GL.FramebufferTexture2D(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, renderedTexture, 0); //original texture 1280x720

                checkGlError();
                FramebufferErrorCode errorCode = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
                if (errorCode != FramebufferErrorCode.FramebufferComplete)
                {
                    if (errorCode == FramebufferErrorCode.FramebufferUnsupported)
                        Console.WriteLine("FramebufferUnsupported");
                    GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
                    GL.DeleteFramebuffers(1, ref FramebufferName);
                    GL.DeleteFramebuffers(1, ref depthrenderbuffer);
                    return;
                }
                checkGlError();
            }
            #endregion

            #region drawInFramebuffer
            checkGlError();
            lock (masterBitmapSync)
            {
                if (masterBitmap != null)
                    createTexture();
            }

            checkGlError();
            GL.BindTexture(TextureTarget.Texture2D, 0);
            GL.Enable(EnableCap.Texture2D);
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, FramebufferName);
            checkGlError();

            //GL.ColorMask(true, true, true, true);
            GL.ClearColor(0, 0, 0, 0);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            checkGlError();

            GL.Enable(EnableCap.Texture2D);
            GL.ActiveTexture(TextureUnit.Texture0);
            GL.BindTexture(TextureTarget.Texture2D, renderedTexture);
            checkGlError();

            GL.Begin(PrimitiveType.Quads);
            {
                GL.TexCoord2(0.0f, 0.0f); GL.Vertex2(LocalPoints[0].X, LocalPoints[0].Y);
                GL.TexCoord2(1.0f, 0.0f); GL.Vertex2(LocalPoints[1].X, LocalPoints[1].Y);
                GL.TexCoord2(1.0f, 1.0f); GL.Vertex2(LocalPoints[2].X, LocalPoints[2].Y);
                GL.TexCoord2(0.0f, 1.0f); GL.Vertex2(LocalPoints[3].X, LocalPoints[3].Y);
            }
            GL.End();
            GL.Disable(EnableCap.Texture2D);

            //TODO draw multipla images
            //loop over all images and calling OnRender2()

            if (createImage)
            {
                createImage = false;
                GPoint p = new GPoint(0, 0); //TODO get correct x,y
                using (Bitmap bitmap = new Bitmap(fbo_width, fbo_height))
                {
                    BitmapData bits = bitmap.LockBits(new Rectangle(0, 0, fbo_width, fbo_height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    checkGlError();
                    GL.ReadPixels((int)p.X, (int)p.Y, fbo_width, fbo_height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bits.Scan0);
                    checkGlError();
                    bitmap.UnlockBits(bits);
                    bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
                    bitmap.Save(@"c:\Downloads\aaa\ReadPixels_" + DateTime.Now.ToString("HHmmss_fff") + ".png", ImageFormat.Png);
                }
            }

            checkGlError();
            //does command in next line, draw framebuffer on the screen?
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); //would draw to the default framebuffer again, basically finishing the drawing to the other framebuffer(the backbuffer which will be brought to front by SwapBuffers)
            checkGlError();
            #endregion
        }

        private void calculateMasterBitmapSize()
        {
            //for test example
            fbo_width = 256;
            fbo_height = 256 * 2;

            DirectBitmap newBitmap = new DirectBitmap(fbo_width, fbo_height);
            newBitmap.Bitmap.MakeTransparent();
            setBitmap(newBitmap);
        }

        public void setBitmap(DirectBitmap bmp)
        {
            lock (masterBitmapSync)
            {
                if (masterBitmap != null)
                    masterBitmap.Dispose();
                masterBitmap = bmp;
                if (masterBitmap == null)
                    deleteTexture();
            }
        }

        void deleteTexture()
        {
            if (renderedTexture > 0)
            {
                GL.DeleteTexture(renderedTexture);
                renderedTexture = 0;
            }
        }

        public void createTexture()
        {
            if (masterBitmap == null)
                return;

            int t = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, t);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, masterBitmap.Width, masterBitmap.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);

            Rectangle rect = new Rectangle(0, 0, masterBitmap.Width, masterBitmap.Height);
            System.Drawing.Imaging.BitmapData data = masterBitmap.Bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            GL.BindTexture(TextureTarget.Texture2D, t);
            GL.TexSubImage2D(TextureTarget.Texture2D, 0, rect.X, rect.Y, rect.Width, rect.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);

            masterBitmap.Bitmap.UnlockBits(data);
            masterBitmap.Dispose();
            masterBitmap = null;

            if (renderedTexture > 0)
                GL.DeleteTexture(renderedTexture);
            renderedTexture = t;
        }

        private void checkGlError()
        {
            ErrorCode errorCode = GL.GetError();
            if (errorCode != ErrorCode.NoError)
            {
                Console.WriteLine("ERROR: " + errorCode);
            }
        }

        public void OnRender2()
        {
            /*
            //this is example only
            lock (bitmapSync)
            {
                if (bitmap != null)
                    createTexture();
            }

            GL.Enable(EnableCap.Texture2D);
            GL.BindTexture(TextureTarget.Texture2D, texture);

            GL.Begin(PrimitiveType.Quads);
            {
                GL.TexCoord4(texCoords[0]); GL.Vertex4(LocalPoints[0].X, LocalPoints[0].Y, 1, 1); //UL
                GL.TexCoord4(texCoords[1]); GL.Vertex4(LocalPoints[1].X, LocalPoints[1].Y, 1, 1); //UR
                GL.TexCoord4(texCoords[2]); GL.Vertex4(LocalPoints[2].X, LocalPoints[2].Y, 1, 1); //LR
                GL.TexCoord4(texCoords[3]); GL.Vertex4(LocalPoints[3].X, LocalPoints[3].Y, 1, 1); //LL
            }

            GL.End();
            GL.Disable(EnableCap.Texture2D);
            */
        }
    }
}
Martin86
  • 123
  • 1
  • 2
  • 19
  • What is it you are trying to draw? If you have simple 2D graphics it would be way easier to use GDI for the drawing. Search for `Graphics.FromImage`. – JonasH Dec 08 '20 at 13:55
  • documentation for OpenTK is on their website https://opentk.net/index.html – Christoph Rackwitz Dec 08 '20 at 15:53
  • I am trying to draw multiple 2D images (image over image) in to buffer, and save the result and also show it on the screen. But I am not able to make framebuffer to work. I don't know what I am making wrong. @ChristophRackwitz I can't fine any info about framebuffer on your link – Martin86 Dec 08 '20 at 18:13
  • @JonasH which command draws framebuffer on stage? – Martin86 Dec 09 '20 at 09:57
  • Does this answer your question? [Drawing with openTK, save to file, show on screen](https://stackoverflow.com/questions/65250801/drawing-with-opentk-save-to-file-show-on-screen) – Rabbid76 Dec 11 '20 at 11:49

0 Answers0