将使用 OpenGL 绘制的纹理保存到文件中

问题描述 投票:0回答:2

我用openGL绘制纹理(视频图像)。图像是在地图上绘制的,因此在大多数情况下绘制的图像是梯形的。仅当我使用“透视正确纹理”时,图像在地图上看起来才不错。 我的问题是如何捕获绘制的纹理并将其存储到文件中。 我只想保存绘制的纹理,而不是屏幕截图或此函数中未绘制的任何其他内容(公共覆盖 void OnRender())。 我还在地图上渲染其他东西,所以截图不是解决方案。那么如何绘制一些帧缓冲区以在屏幕上使用它并将其保存到文件中。

使用 openTK nuGet v1.1.1589.5942 (v4.0.6)

using GMap.NET.OpenGL;
using OpenTK;
using OpenTK.Graphics.OpenGL;        

public GMapControl() : base(new OpenTK.Graphics.GraphicsMode(32, 24, 8, 4))
{
    Paint += glControl_Paint;
}

void glControl_Paint(object sender, PaintEventArgs e)
{
    if (!loaded)
        return;

    if (makeControlContext)
    {
        //VideoForm
        controlContext = new GraphicsContext(GraphicsMode, WindowInfo);
        makeControlContext = false;
    }
    if (controlContext != null)
        controlContext.MakeCurrent(WindowInfo);
    else
        MakeCurrent();

    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    GL.MatrixMode(MatrixMode.Modelview);

    GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
    GL.Enable(EnableCap.Blend);

    GL.Enable(EnableCap.LineSmooth);
    GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);

    DrawMap();

    textureLoader();

    if (OnPaint != null)
    {
        GL.DepthMask(false);
        GL.LoadIdentity();
        OnPaint(sender, e);
        if (useViewPortFix)
            setupViewport();
    }
    GL.Disable(EnableCap.Blend);
    GL.Flush();
    SwapBuffers();
}

void DrawMap()
{
    try
    {
      
    }
    finally
    {
        if (themeFont != null)
            OnPaintOverlays();
        GL.PopMatrix();
    }
}

protected virtual void OnPaintOverlays()
{
    GL.LoadIdentity();
    GL.Translate(Core.renderOffset.X, Core.renderOffset.Y, 0);
    try
    {
        foreach (GMapOverlay o in Overlays)
        {
            if (o.IsVisibile)
            {
                o.OnRender();
            }
        }
    }
    catch { }
}

public override void OnRender()
{
    GL.Color4(backgroundColor.Value);

    lock (bitmapSync)
    {
        if (bitmap != null)
            createTexture();
    }

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

    //Do the magick for "Perspective correct texturing"
    // center point
    GPoint localTargetPosition = MainForm.instance.gMapControl.FromLatLngToLocalWithOffset(targetPosition);
    // determines distances to center for all vertexes
    double dUL = Common.distance(new double[] { LocalPoints[0].X, LocalPoints[0].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });
    double dUR = Common.distance(new double[] { LocalPoints[1].X, LocalPoints[1].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });
    double dLR = Common.distance(new double[] { LocalPoints[2].X, LocalPoints[2].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });
    double dLL = Common.distance(new double[] { LocalPoints[3].X, LocalPoints[3].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });

    var texCoords = new[]
    {
            new Vector4(0, 0, 1, 1),
            new Vector4(1, 0, 1, 1),
            new Vector4(1, 1, 1, 1),
            new Vector4(0, 1, 1, 1)
        };

    texCoords[0] *= (float)((dUL + dLR) / dLR);
    texCoords[1] *= (float)((dUR + dLL) / dLL);
    texCoords[2] *= (float)((dLR + dUL) / dUL);
    texCoords[3] *= (float)((dLL + dUR) / dUR);

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

    GL.End();
    GL.Disable(EnableCap.Texture2D);

    //TODO store drawn texture/image to file (only the drawn texture not screenshot or anything else which is not drawn here)
}

我尝试使用 Framebuffer 执行此操作,但没有成功。绘制到帧缓冲区,然后从中读取像素,输出是空图像。

int FramebufferName = -1;
int depthrenderbuffer;
int fbo_width = 1280;
int fbo_height = 720;

public override void OnRender()
{
    if (!targetPosition.IsEmpty)
    {
        if (FramebufferName == -1)
        {
            //Create new Framebuffer only once
            GL.GenFramebuffers(1, out FramebufferName);
            GL.BindFramebuffer(FramebufferTarget.Framebuffer, FramebufferName);

            //create texture from bitmap 1280x720
            lock (bitmapSync)
            {
                if (bitmap != null)
                {
                    fbo_width = bitmap.Width;
                    fbo_height = bitmap.Height;
                    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, bitmap.Width, bitmap.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);

                    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                    System.Drawing.Imaging.BitmapData data = 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);

                    bitmap.UnlockBits(data);
                    bitmap.Dispose();
                    bitmap = null;

                    if (renderedTexture > 0)
                        GL.DeleteTexture(renderedTexture);
                    renderedTexture = t;
                    GL.FramebufferTexture2D(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, renderedTexture, 0); //original texture 1280x720
                }
            }

            /* 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);
            //GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, RenderbufferTarget.Renderbuffer, renderedTexture);

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

            //GL.ReadBuffer(ReadBufferMode.ColorAttachment0);

            //DrawBuffersEnum[] drawBuffersEnum = new DrawBuffersEnum[] { DrawBuffersEnum.ColorAttachment0 };
            //GL.DrawBuffers(1, drawBuffersEnum);

            if (GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != FramebufferErrorCode.FramebufferComplete)
            {
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
                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)
                GL.DeleteFramebuffers(1, ref FramebufferName);
                GL.DeleteFramebuffers(1, ref depthrenderbuffer);
                return;
            }
            checkGlError();
        }

        //drawInFramebuffer
        //GL.BindTexture(TextureTarget.Texture2D, 0);
        //GL.Enable(EnableCap.Texture2D);
        GL.BindFramebuffer(FramebufferTarget.Framebuffer, FramebufferName);
        //GL.Viewport(0, 0, fbo_width, fbo_height);
        checkGlError();

        //clear all
        GL.ClearColor(1, 1, 1, 0);
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

        //GL.MatrixMode(MatrixMode.Projection);
        //GL.LoadIdentity();
        checkGlError();

        //bind texture to framebuffer
        GL.Enable(EnableCap.Texture2D);
        checkGlError();
        GL.ActiveTexture(TextureUnit.Texture0);
        checkGlError();
        GL.BindTexture(TextureTarget.Texture2D, renderedTexture);
        checkGlError();
        //GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, renderedTexture);
        //checkGlError();

        //draw in framebuffer
        //Do the magick for "Perspective correct texturing"
        // center point
        GPoint localTargetPosition = MainForm.instance.gMapControl.FromLatLngToLocalWithOffset(targetPosition);
        // determines distances to center for all vertexes
        double dUL = Common.distance(new double[] { LocalPoints[0].X, LocalPoints[0].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });
        double dUR = Common.distance(new double[] { LocalPoints[1].X, LocalPoints[1].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });
        double dLR = Common.distance(new double[] { LocalPoints[2].X, LocalPoints[2].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });
        double dLL = Common.distance(new double[] { LocalPoints[3].X, LocalPoints[3].Y }, new double[] { localTargetPosition.X, localTargetPosition.Y });

        var texCoords = new[]
        {
            new Vector4(0, 0, 1, 1),
            new Vector4(1, 0, 1, 1),
            new Vector4(1, 1, 1, 1),
            new Vector4(0, 1, 1, 1)
        };

        texCoords[0] *= (float)((dUL + dLR) / dLR);
        texCoords[1] *= (float)((dUR + dLL) / dLL);
        texCoords[2] *= (float)((dLR + dUL) / dUL);
        texCoords[3] *= (float)((dLL + dUR) / dUR);

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

        GL.End();
        GL.Disable(EnableCap.Texture2D);
        checkGlError();

        //TODO, get size an location where image is in framebuffer
        fbo_width = 1280;
        fbo_height = 720;
        long minX = 0;
        long maxY = 0;

        #endregion
        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);
            GL.ReadPixels((int)minX, (int)maxY, fbo_width, fbo_height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bits.Scan0);
            bitmap.UnlockBits(bits);
            bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
            bitmap.Save(@"c:\Downloads\aaa\ReadPixels_" + now.ToString("HHmmss_fff") + ".png", ImageFormat.Png); //getting empty image, alpha = 0
        }
        
        checkGlError();
        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)

        //TODO draw framebuffer on screen. HOW???
        /*
        GL.Enable(EnableCap.Texture2D);
        GL.BindTexture(TextureTarget.Texture2D, renderedTexture);
        GL.BlitFramebuffer(0, 0, fbo_width, fbo_height, 0, 0, fbo_width, fbo_height, ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Nearest);
        */
    }
    else
    {
        base.OnRender();
    }
}

private void checkGlError()
{
    ErrorCode errorCode = GL.GetError();
    if (errorCode != ErrorCode.NoError)
    {
        Console.WriteLine("ERROR: " + errorCode);
    }
}
c# opengl opentk gmap.net
2个回答
0
投票

如果你想从帧缓冲区读取矩形区域,那么你可以使用

GL.ReadPixels
。例如:

Bitmap bmp = new Bitmap(width, height);
System.Drawing.Imaging.BitmapData data =
    bmp.LockBits(this.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
                 System.Drawing.Imaging.PixelFormat.Format24bppRgb);
GL.ReadPixels(x, y, width, height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);

纹理对象的像素数据可以通过

GL.GetTexImage
读取。此功能仅在桌面 OpenGL 中提供,在 OpenGL ES 中不提供:

GL.BindTexture(TextureTarget.Texture2D, texture);
GL.GetTexImage(TextureTarget.Texture2D, 0, PixelFormat.Bgr, PixelType.UnsignedByte, target);

在 OpenGL ES 中,您需要将纹理附加到帧缓冲区。请参阅 opengl es 2.0 android c++ glGetTexImage 替代品


0
投票

问题解决了吗?

谢谢。

© www.soinside.com 2019 - 2024. All rights reserved.