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



我用openGL绘制了一个纹理(视频图像(。图像是在地图上绘制的,因此在大多数情况下绘制的图像是梯形的。只有当我使用";透视正确纹理";。我的问题是如何捕捉绘制的纹理并将其存储到文件中我只想保存绘制的纹理,而不是屏幕截图或其他在这个函数中没有绘制的东西(public override void OnRender(((我还渲染了地图上的其他东西,所以截图不是一个解决方案。因此,如何绘制一些帧缓冲区,以便在屏幕上使用它并将其保存到文件中。

使用openTK-nuGet v1.11589.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:DownloadsaaaReadPixels_" + 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);
}
}

如果要从帧缓冲区读取矩形区域,则可以使用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替代方案。

最新更新