使用 openTK 绘图,保存到文件,显示在屏幕上
Posted
技术标签:
【中文标题】使用 openTK 绘图,保存到文件,显示在屏幕上【英文标题】:Drawing with openTK, save to file, show on screen 【发布时间】:2020-12-11 11:47:05 【问题描述】:我正在地球地图上绘制多张图像,我在每张图像上使用透视正确纹理link。
我想将渲染图像存储到文件中(源文件是 1280x760,渲染图像在大多数情况下旋转大约 160x90)。目前我正在使用 GL.ReadPixels 进行此操作
int width = 1920;
int height = 1080;
using (Bitmap bitmap = new Bitmap(width, height))
System.Drawing.Imaging.BitmapData bits = bitmap.LockBits(new Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.ReadPixels(0, (0, width, height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bits.Scan0);
bitmap.UnlockBits(bits);
bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
bitmap.Save("output.png", System.Drawing.Imaging.ImageFormat.Png);
当我移动地图时出现问题,渲染的图像在屏幕上不再可见,看起来在这种情况下 GL.ReadPixels 返回空像素。
即使当前未在屏幕上渲染,如何获取渲染图像?
额外的问题,如果我使用帧缓冲区,结果是相同的,但使用帧缓冲区我从未在屏幕上看到图像,看起来帧缓冲区没有在屏幕上绘制,但 GL.ReadPixels 可以获取图像。 还需要哪些代码行才能在屏幕上绘制帧缓冲区?
有什么想法吗?
我正在向帧缓冲区添加一些绘图代码,但结果是空图像。
int FBOHandle = 0;
int ColorTexture = 0;
int DepthTexture = 0;
public bool canRender = false;
public void onRender()
int zoom = (int)MainForm.mainMap.Zoom;
VideoMapOverlayBitmap pob = null;
lock (videoMapOverlayBitmapsSync)
videoMapOverlayBitmaps.TryGetValue(zoom, out pob);
if (pob == null)
return;
if (canRender)
canRender = false;
int fboWidth = 1920;
int fboHeight = 1080;
// Create Color Tex for framebuffer
GL.GenTextures(1, out ColorTexture);
GL.BindTexture(TextureTarget.Texture2D, ColorTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, fboWidth, fboHeight, 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.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
// GL.Ext.GenerateMipmap( GenerateMipmapTarget.Texture2D );
// Create Depth Tex for framebuffer
GL.GenTextures(1, out DepthTexture);
GL.BindTexture(TextureTarget.Texture2D, DepthTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, (PixelInternalFormat)All.DepthComponent32, fboWidth, fboHeight, 0, OpenTK.Graphics.OpenGL.PixelFormat.DepthComponent, PixelType.UnsignedInt, IntPtr.Zero);
// things go horribly wrong if DepthComponent's Bitcount does not match the main Framebuffer's Depth
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.ClampToBorder);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
// GL.Ext.GenerateMipmap( GenerateMipmapTarget.Texture2D );
// Create a FBO and attach the textures
GL.Ext.GenFramebuffers(1, out FBOHandle);
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, FBOHandle);
GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, ColorTexture, 0);
GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, TextureTarget.Texture2D, DepthTexture, 0);
//check for errors on framebuffer
FramebufferErrorCode errorCode = GL.Ext.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
if (errorCode != FramebufferErrorCode.FramebufferComplete)
if (errorCode == FramebufferErrorCode.FramebufferUnsupported)
Console.WriteLine("FramebufferUnsupported");
OnUnload();
return;
GL.ClearColor(0, 0, 0, 0);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
/*
//this corrupts my main screen
GL.ClearColor(Color.White);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(0, fboWidth, fboHeight, 0, -1, 1); // Up-left corner pixel has coordinate (0, 0)
GL.Viewport(0, 0, fboWidth, fboHeight); // Use all of the glControl painting area
*/
// Render all images
PureProjection proj = MainForm.mainMap.MapProvider.Projection;
List<VideoLogEntry> log = Log;
//go over all images in the loop
foreach (var videoEntry in log)
if (videoEntry == null)
continue;
if (videoEntry.projectedRectangleEmpty())
continue;
PointLatLng[] rect = videoEntry.getProjectedRectangle();
if (videoEntry.bmp != null)
videoEntry.createTexture();
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, videoEntry.texture);
//GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, videoEntry.texture, 0);
//GL.DrawBuffers(1, new int[] videoEntry.texture ); //compiler error
//Do the magick for "Perspective correct texturing"
// center point
GPoint localTargetPosition = MainForm.instance.gMapControl.FromLatLngToLocalWithOffset(videoEntry.target);
videoEntry.UpdatePolygonLocalPosition(videoEntry.projectedRectangle);
// determines distances to center for all vertexes
double dUL = Common.distance(new double[] videoEntry.LocalPoints[0].X, videoEntry.LocalPoints[0].Y , new double[] localTargetPosition.X, localTargetPosition.Y );
double dUR = Common.distance(new double[] videoEntry.LocalPoints[1].X, videoEntry.LocalPoints[1].Y , new double[] localTargetPosition.X, localTargetPosition.Y );
double dLR = Common.distance(new double[] videoEntry.LocalPoints[2].X, videoEntry.LocalPoints[2].Y , new double[] localTargetPosition.X, localTargetPosition.Y );
double dLL = Common.distance(new double[] videoEntry.LocalPoints[3].X, videoEntry.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(videoEntry.LocalPoints[0].X, videoEntry.LocalPoints[0].Y, 1, 1); //UL LocalPoints[0] gimbalUL
GL.TexCoord4(texCoords[1]); GL.Vertex4(videoEntry.LocalPoints[1].X, videoEntry.LocalPoints[1].Y, 1, 1); //UR LocalPoints[1] gimbalUR
GL.TexCoord4(texCoords[2]); GL.Vertex4(videoEntry.LocalPoints[2].X, videoEntry.LocalPoints[2].Y, 1, 1); //LR LocalPoints[2] gimbalLR
GL.TexCoord4(texCoords[3]); GL.Vertex4(videoEntry.LocalPoints[3].X, videoEntry.LocalPoints[3].Y, 1, 1); //LL LocalPoints[3] gimbalLL
GL.End();
GL.Disable(EnableCap.Texture2D);
// Grab your screenshot
// draw FBO in to file
lock (pob.masterBitmapSync)
using (Bitmap mybitmap = new Bitmap(fboWidth, fboHeight))
//fill bitmal so we will see what ReadPixels draw
using (Graphics gfx = Graphics.FromImage(mybitmap))
using (SolidBrush brush = new SolidBrush(Color.FromArgb(0, 0, 255)))
gfx.FillRectangle(brush, 0, 0, mybitmap.Width, mybitmap.Height);
GPoint p = new GPoint(0, 0);
int outputWidth = mybitmap.Width;
int outputHeight = mybitmap.Height;
System.Drawing.Imaging.BitmapData bits = mybitmap.LockBits(new Rectangle(0, 0, mybitmap.Width, mybitmap.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.ReadPixels((int)p.X, (int)p.Y, outputWidth, outputHeight, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bits.Scan0);
mybitmap.UnlockBits(bits);
//mybitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
mybitmap.Save(@"c:\Downloads\aaa\ReadPixels_" + DateTime.Now.ToString("HHmmss_fff") + ".png", System.Drawing.Imaging.ImageFormat.Png);
pob.masterBitmap.Dispose();
pob.masterBitmap = null;
pob.masterBitmap = (Bitmap)mybitmap.Clone();
// Unload and dispose the frame buffer
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
// Clean up what we allocated before exiting
if (ColorTexture != 0)
GL.DeleteTextures(1, ref ColorTexture);
ColorTexture = 0;
if (DepthTexture != 0)
GL.DeleteTextures(1, ref DepthTexture);
DepthTexture = 0;
if (FBOHandle != 0)
GL.Ext.DeleteFramebuffers(1, ref FBOHandle);
FBOHandle = 0;
【问题讨论】:
【参考方案1】:即使当前未在屏幕上渲染,如何获取渲染图像?
创建一个新的帧缓冲区,绑定该帧缓冲区,设置视口,设置正射投影矩阵,将您想要的地图部分渲染到其中,调用 GL.ReadPixels,保存屏幕截图,卸载和处置帧缓冲区。
这里有一些示例代码:
// Create framebuffer
int fboId = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, fboId);
// Set up a framebuffer attachment here
int width = ...
int height = ...
int textureId = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, textureId);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, width, height, 0, PixelFormat.Rgba, PixelType.Float, IntPtr.Zero);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, (FramebufferAttachment)fbAtt.AttachmentType, TextureTarget.Texture2D, textureId, 0);
GL.DrawBuffers(1, new int[] textureId );
GL.Viewport(0, 0, width, height);
// Set up ortho modo. probably also want to disable depth testing and any active blend modes
....
// Render your map now
....
// Grab your screenshot
....
// Unload and dispose the frame buffer
...
// Reset everything (viewport, ortho mode, etc.) if you don't your normal map to flicker for a frame
....
你的另一个问题我不太明白
【讨论】:
@Tryron 我按照你的建议用代码编辑我的问题。但我得到空图像。我需要调用 GL.Ext.BindFramebuffer 还是 GL.BindFramebuffer? 您可能仍需要正确设置正交模式和视口。完成修复主屏幕“损坏”后,将它们重置为原始值。话虽如此,很多事情都会导致您的图像为空。也可能与混合、部门测试或其他原因有关。以上是关于使用 openTK 绘图,保存到文件,显示在屏幕上的主要内容,如果未能解决你的问题,请参考以下文章
Gnuplot 输出设置为“在屏幕上”显示如何更改程序,以便将绘图输出定向到我在下面提供程序的文件夹
在previewLayer上绘图:AVCaptureVideoPreviewLayer