opentk / GLWpfControl

A fast native control for OpenTK 4.x + 3.x on WPF.
MIT License
201 stars 49 forks source link

How to use offscreen render in GLWpfControl to export a image #119

Closed Spring-grow closed 6 months ago

Spring-grow commented 1 year ago

I wrote a piece of code in a GLWpfControl ,Like below, but it doesn't work:

      private int FBO=0,CBO=0,DBO = 0,TBO=0;
        private static int rendercount = 0;
        public unsafe  void OffScreenRender()
        {
            Loger.LogInfo("start");
            int defautFrame = 0;

            defautFrame= GL.GetInteger(GetPName.FramebufferBinding);
            Loger.LogInfo($"default framebuffer {defautFrame}");
            if(FBO==0)
            {
                FBO = GL.GenFramebuffer();
                Loger.LogInfo($"Create FBO {FBO}");
            }
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, FBO);

            if(CBO==0)
            {
                CBO = GL.GenRenderbuffer();

                Loger.LogInfo($"Create CBO {CBO}");
            }
                GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, CBO);
                GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Rgba8, 800, 600);
                GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, RenderbufferTarget.Renderbuffer, CBO);

            if(DBO==0)
            {
                DBO = GL.GenRenderbuffer();
                Loger.LogInfo($"Create DBO {DBO}");
            }
                GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, DBO);
                GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent, 800, 600);
                GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, RenderbufferTarget.Renderbuffer, DBO);

            if (TBO == 0)
            {
                TBO = GL.GenTexture();
                Loger.LogInfo($"Create TBO {TBO}");
            }
                GL.BindTexture(TextureTarget.Texture2D, TBO);
                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 800, 600, 0, PixelFormat.Rgba, PixelType.UnsignedInt, IntPtr.Zero);
                GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, TBO, 0);
            if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete)
            {
                Loger.LogError($"OffScreenRender Framebuffer status is not complete");
            }

            **if (!initialed) InitGL();
            ShaderProgram.ActiveProgram(_program);
            UpdateGL();

            GL.BindFramebuffer(FramebufferTarget.Framebuffer, FBO);
            GL.Viewport(0, 0, 800, 600);
            //GL.Enable(EnableCap.DepthTest); 
            //GL.ClearColor(0.2f, 0.5f, 0.6f, 0.5f);
            GL.BindVertexArray(VAO);

            var g =MyData.Graphics;

            if (MDataType == MyType.Points)
            {
                GL.DrawElements(BeginMode.Points, g.PointsSize, DrawElementsType.UnsignedInt, 0);
            }
            else if (MDataType == MType.Face)
            {

                GL.DrawElements(BeginMode.Triangles, g.TranglesSize, DrawElementsType.UnsignedInt, g.PointsSize * sizeof(uint));
            }
            else
            {
                GL.DrawElements(BeginMode.Lines, g.GridsSize, DrawElementsType.UnsignedInt, (g.PointsSize + g.TranglesSize) * sizeof(uint));
            }
            GL.BindVertexArray(0);**

            byte[] pixels=new byte[4*800*600];
            GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
            fixed(byte* buff=pixels){
                GL.ReadPixels(0, 0, 800, 600, PixelFormat.Rgba, PixelType.UnsignedInt,new IntPtr(buff));
            }
            //Bitmap bmp = new Bitmap(800, 600);

            //{mapType.ToString()}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss-ffff")}
            GL.BindFramebuffer(FramebufferTarget.Framebuffer,defautFrame);
            ShaderProgram.DeactiveProgram();

            using (var fs = new FileStream($"D:\\GLCapture\\{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss-ffff")}.pixels", FileMode.OpenOrCreate))
            {
                fs.Write(pixels, 0, pixels.Length);
                fs.Flush();
                fs.Close();
            }
            Loger.LogInfo("end");

        }

In this code, the InitGL and UpdateGL used to initialize program and VAO data, as well as the DrawElements section, are the same as what I wrote in GLWpfControl's OnRender(). Here, I want to achieve off screen rendering by binding my own framebuffer to OffScreenRender(), but this framebuffer does not work, when I read by ReadPixels from this buffer, all data in pixels is 0. I want to save the content rendered in GLWpfControl to an image through off screen rendering. What should I do?

NogginBops commented 6 months ago

There could be a lot of reasons why this code isn't working. Most likely it has nothing to do with GLWpfControl and more to do with correct OpenGL usage. Some common issues are: