PDA

View Full Version : FBO render to texture using VBO



Cyberdemon
11-25-2012, 02:37 PM
I'm using OpenTK and am trying to render to an FBO texture as per the tutorial here:

http://www.opentk.com/node/397
http://www.opentk.com/files/Program_0.cs

This example uses immediate mode to draw triangles to the texture but I'd like to use VBO. I'm very new to OpenGL and have my rendering working using VBO but I don't know how to replace the random triangle rendering in this tutorial with DrawElements using VBO.

Does anyone know of a tutorial which uses VBO to render to a FBO?

Cyberdemon
11-27-2012, 01:01 PM
Alternatively my code is below if someone is able to see the problem.

My working VBO render (renders my world as expected):


protected override void OnRenderFrame_VBO(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

// Camera.
Matrix4 lookat = Matrix4.LookAt(this.Camera, this.Target, this.LocalUp);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref lookat);
GL.Viewport(0, 0, this.Width, this.Height);
double aspect_ratio = this.Width / (double)this.Height;
OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView(fov, (float)aspect_ratio, 1, 25000);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref perspective);

// Colour Array.
GL.BindBuffer(BufferTarget.ArrayBuffer, buffers.ColoursBufferId);
GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero);
GL.EnableClientState(ArrayCap.ColorArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

// Vertex Data Array.
GL.BindBuffer(BufferTarget.ArrayBuffer, buffers.VertexDataArrayBufferId);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(world.Vertices.Length * Vector3.SizeInBytes), world.Vertices, BufferUsageHint.StreamDraw);
GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
GL.EnableClientState(ArrayCap.VertexArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

// Index Array.
GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffers.ElementIndicesBufferId);

GL.DrawElements(BeginMode.Triangles, world.Indices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);
this.SwapBuffers();
}

And my FBO code which is not working (renders all red):


public static void OnLoad(EventArgs e)
{

<<Working VBO creation logic here>>

// Create Color Tex
GL.GenTextures(1, out buffers.ColourTexture);
GL.BindTexture(TextureTarget.Texture2D, buffers.ColourTexture);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, this.OffscreenFBOWidth, this.OffscreenFBOHeight, 0, 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 a FBO and attach the textures
GL.Ext.GenFramebuffers(1, out buffers.FBOHandle);
GL.Ext.BindFramebuffer(FramebufferTarget.Framebuff erExt, buffers.FBOHandle);
GL.Ext.FramebufferTexture2D(FramebufferTarget.Fram ebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, buffers.ColourTexture, 0);

if (GL.Ext.CheckFramebufferStatus(FramebufferTarget.F ramebufferExt) != FramebufferErrorCode.FramebufferCompleteExt)
{
throw new Exception(GL.Ext.CheckFramebufferStatus(Framebuffe rTarget.FramebufferExt).ToString());
}

GL.Ext.BindFramebuffer(FramebufferTarget.Framebuff erExt, 0);
GL.BindTexture(TextureTarget.Texture2D, 0);
}

protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

// Camera.
Matrix4 lookat = Matrix4.LookAt(this.Camera, this.Target, this.LocalUp);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref lookat);
GL.Viewport(0, 0, this.OffscreenFBOWidth, this.OffscreenFBOHeight);
double aspect_ratio = Width / (double)Height;
OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView(fov, (float)aspect_ratio, 1, 25000);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref perspective);

// Bind FBO.
GL.Ext.BindFramebuffer(FramebufferTarget.Framebuff erExt, buffers.FBOHandle);

GL.ClearColor(0f, 0f, .7f, 0f);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

// Render VBO.
// Colour Array.
GL.BindBuffer(BufferTarget.ArrayBuffer, buffers.ColoursBufferId);
GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero);
GL.EnableClientState(ArrayCap.ColorArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

// Vertex Data Array.
GL.BindBuffer(BufferTarget.ArrayBuffer, buffers.VertexDataArrayBufferId);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(world.Vertices.Length * Vector3.SizeInBytes), world.Vertices, BufferUsageHint.StreamDraw);
GL.VertexPointer(3, VertexPointerType.Float, Vector3.SizeInBytes, IntPtr.Zero);
GL.EnableClientState(ArrayCap.VertexArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

// Index Array.
GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffers.ElementIndicesBufferId);
GL.DrawElements(BeginMode.Triangles, world.Indices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);

GL.Ext.BindFramebuffer(FramebufferTarget.Framebuff erExt, 0);

GL.ClearColor(.4f, 0f, 0f, 0f);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.Texture2D);

GL.BindTexture(TextureTarget.Texture2D, 0);

GL.PushMatrix();
{
GL.Translate(-1.1f, 0f, 0f);
GL.BindTexture(TextureTarget.Texture2D, buffers.ColourTexture);

GL.Begin(BeginMode.Quads);
{
GL.TexCoord2(0f, 1f);
GL.Vertex2(-1.0f, 1.0f);
GL.TexCoord2(0.0f, 0.0f);
GL.Vertex2(-1.0f, -1.0f);
GL.TexCoord2(1.0f, 0.0f);
GL.Vertex2(1.0f, -1.0f);
GL.TexCoord2(1.0f, 1.0f);
GL.Vertex2(1.0f, 1.0f);
}
GL.End();
}
GL.PopMatrix();

this.SwapBuffers();
}

I'll take any pointers...

tonyo_au
11-27-2012, 03:57 PM
I think your quad points are mixed up

try


GL.TextCoord2(0.0f,0.0f);
GL.Vertex2(-1.0f,-1.0f);
GL.TextCoord2(1.0f,0.0f);
GL.Vertex2(1.0f,-1.0f);
GL.TextCoord2(1.0f,1.0f);
GL.Vertex2(1.0f,1.0f);
GL.TextCoord2(0.0f,1.0f);
GL.Vertex2(-1.0f,1.0f);

Cyberdemon
11-28-2012, 01:35 PM
Just tried your code but still no joy.

Thanks for the suggestion I really appreciate it.

tonyo_au
11-28-2012, 04:35 PM
If you don't bind a texture for the quad draw but just set a colour does the quad draw in the colour?
eg render a blue quad over the background.




GL.Begin(BeginMode.Quads);
GL.Color3f(0.0f,1.0f,0.0f);
GL.Vertex2(-1.0f, 1.0f);
GL.Vertex2(-1.0f, -1.0f);
GL.Vertex2(1.0f, -1.0f);
GL.Vertex2(1.0f, 1.0f);
GL.End();


This will at least remove the quad draw as a problem

Cyberdemon
11-29-2012, 10:54 AM
Good idea.


GL.PushMatrix();
{
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.Begin(BeginMode.Quads);
{
GL.Color3(0f, .6f, 0f);
GL.Vertex2(-1.0f, -1.0f);
GL.Vertex2(1.0f, -1.0f);
GL.Vertex2(1.0f, 1.0f);
GL.Vertex2(-1.0f, 1.0f);
}
GL.End();

GL.Begin(BeginMode.Quads);
{
GL.Color3(0f, 0f, .6f);
GL.Vertex2(-1.0f, 1.0f);
GL.Vertex2(-1.0f, -1.0f);
GL.Vertex2(1.0f, -1.0f);
GL.Vertex2(1.0f, 1.0f);
}
GL.End();
}
GL.PopMatrix();

I've tried the above (drawing the quad as you suggested and how it was originally) but I don't see either quad.

That's great though. Now I know it's not a FBO issue. Thanks.

I guess I need to reset the view after my VBO rendering. I thought this was achieved with GL.LoadIdentity() but adding GL.LoadIdentity() makes no difference.

Cyberdemon
11-29-2012, 02:55 PM
Ok, finally got the quad rendering on screen.

Once the VBO has been drawn to the FBO I'm calling GL.LoadIdentity() before and after applying a Modelview Matrix.


// Draw VBO to FBO.
GL.DrawElements(BeginMode.Triangles, world.Indices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);
// Stop rendering to FBO.
GL.Ext.BindFramebuffer(FramebufferTarget.Framebuff erExt, 0);

// Reset Matrix.
GL.LoadIdentity();
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();

// Draw FBO as texture.
GL.ClearColor(0f, Convert.ToSingle(this.rand.NextDouble()), 1f, 0f); // Greenish.
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

GL.PushMatrix();
{
GL.BindTexture(TextureTarget.Texture2D, Rendering.Controller.buffers.ColourTexture);
GL.Begin(BeginMode.Quads);
{
GL.TexCoord2(0.0f, 0.0f);
GL.Vertex2(-1.0f, -1.0f);
GL.TexCoord2(1.0f, 0.0f);
GL.Vertex2(1.0f, -1.0f);
GL.TexCoord2(1.0f, 1.0f);
GL.Vertex2(1.0f, 1.0f);
GL.TexCoord2(0.0f, 1.0f);
GL.Vertex2(-1f, 1.0f);
}
GL.End();
}
GL.PopMatrix();

The FBO isn't rendering full-screen and the colours are all a shade of green, but I think that's progress.

tonyo_au
11-29-2012, 10:19 PM
Yes that sounds like progress.

The GL.LoadIdentity should not be done prior to the GL.MatrixMode(MatrixMode.Modelview) as it changes the matix of the last

GL.MatrixMode before it, which is not what you want.

The next step in debugging is to use the code that renders to the texture buffer to just render straight to the screen
so you can see what is being rendered to the texture.

(The alernate is to save the texture to a file as an image that you can look at but that might actually be more work
than is necessary).

Cyberdemon
11-30-2012, 02:24 PM
If I remove the GL.LoadIdentity() prior to the GL.MatrixMode(MatrixMode.Modelview) then nothing is rendered. I guess this means I probably have some bigger issue?

This is how my rendering looks without using FBO (clear colour is pink):
http://i45.tinypic.com/1rsmmu.png

This is the same frame with my FBO code (FBO clear red, quad rendering clear blue):
http://i47.tinypic.com/29z5rmv.png

The image in the bottom left is the FBO but it should be full screen and with the colours as per the non FBO render.

I'm fairly happy now that the FBO rendering is working because the first call to render draws the FBO with the right colours, just not full screen.

tonyo_au
11-30-2012, 05:50 PM
It looks like you FBO render is only rendering the red channel but I don't know why. You can confirm this by clearing the backgroundto with say white and
see what clear colour appears when you render the FBO texture.

Just some other notes

Usually your

GL.PushMatrix(); GL.PopMatrix();
are around any changes to the matrices not after you change them. The idea is to also know what state the matrices are in.

Check you calls to glViewport since this effect where things are drawn on the window. Also check calls GL.MatrixMode to see what matrix is being set to identity matrix - I assume it is the projection matrix since
I think it need to be set to identity as well to render a whole screenquad (I am a bit vague because I have been using shaders to do this for so long I forget exactly how the old fixed pipeline works).

Cyberdemon
12-02-2012, 05:18 AM
I'm over the moon. I was missing a call to GL.Viewport() to set the full screen bounds exactly as you said. And it was rendering all red because I wasn't clearing the texture GL.BindTexture(TextureTarget.Texture2D, 0) before re-drawing it - I'm a fool.

But it's all working now:

Standard:
http://i49.tinypic.com/33fc9jc.png

Stretched FBO:
http://i49.tinypic.com/34insly.png

Thanks for the other notes and my incorrect GL.PushMatrix() GL.PopMatrix() usage.

You mention shaders and not using the fixed pipeline. If you have any links to resources which show how I could achieve this with shaders I'd like to have a look. I take it this fixed pipeline approach is non-standard?

Thanks for all your guidance.

tonyo_au
12-02-2012, 03:06 PM
You mention shaders and not using the fixed pipeline. If you have any links to resources which show how I could achieve this with shaders I'd like to have a look. I take it this fixed pipeline approach is non-standard?
Not non-standard just old style and much less flexible than shaders.
If you can afford it, I would recommend an e-book called "OpenGL 4.0 Shading Language Cookbook". It is a quick way in to shaders. Others might like to offer advice on good shader language web sites.