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);
*/
}
}
}