ZTK's picture

primitive vertex colors look masked when rendering to a FBO

Hello,

I've found a wierd issue when rendering to an off screen FBO. I've set up two empty textures, then I render to them by calling GL.Clear in blue, and then render a triangle where each vertex is a color (red, green, and blue). Two wierd things happen.

First of all, although the GL.Clear function works fine, the vertex colors don't look right. They render as either black or a shade of blue! What's even stranger is that if I start from a texture loaded on disk, it works fine. It's when I start from a null texture that it does this.

Secondly, the second texture has a blue gradient applied to it. This I don't understand at all. If anything, the two textures should be identical. They each have their own FBO, their own texture, and are rendered to using the same function.

I've written a small program which reproduces the issue. Please see the image below to see what I mean.

I have two questions:
1) Why would vertex colors render as black? The code for rendering the triangle is here. One can see that vertices are set with red, green, and blue. Yet the triangle renders as black.

        private void RenderTriangleInFBO(int FBOHandle)
        {
            GL.Ext.BindFramebuffer(FramebufferTarget.DrawFramebuffer, FBOHandle);
            GL.PushAttrib(AttribMask.ViewportBit);
            {
                GL.Viewport(0, 0, size_w, size_h);
 
                // clear the screen in blue, to make it very obvious what the clear affected. only the FBO, not the real framebuffer
                GL.ClearColor(0.0f, 0.0f, 1.0f, 1.0f);
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit);
 
                // render a multicolored triangle
                GL.Begin(BeginMode.Triangles);
                {
                    GL.Color4(0.0f, 1.0f, 0.0f, 0.0f);
                    GL.Vertex3(-0.5, -0.5, 0.0);
 
                    GL.Color4(1.0f, 0.0f, 0.0f, 0.0f);
                    GL.Vertex3(0.5, -.5, 0.0);
 
                    GL.Color4(0.0f, 0.0f, 1.0f, 0.0f);
                    GL.Vertex3(0.0, 0.5, 0.0);
                }
                // reset to white
                GL.Color4(1.0f, 1.0f, 1.0f, 1.0f);
                GL.End();
            }
            GL.PopAttrib();
 
            GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); // disable rendering into the FBO
        }

2) Why would the two textures render differently? The second texture on the right has a gradient on it. If I set the background color to red, it will have a red gradient with the gradient starting in the red corner. So it looks like it's "trying" to render correctly, it's almost as if there is a color mask or something going on. But each FBO is rendered to independently. We have

            RenderTriangleInFBO(FBOHandle1);
            RenderTriangleInFBO(FBOHandle2);

If I switch the order of the two, then they BOTH have that "gradient". Does anyone have any idea what's going on? I'm really scratching my head!

I've attached the full source below.

// Released to the public domain. Use, modify and relicense at will.
 
using System;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using OpenTK.Input;
 
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;
 
namespace StarterKit
{
 
    class Game : GameWindow
    {
        private Matrix4 projectionMatrix;
 
        int texture1, texture2;
        int FBOHandle1, FBOHandle2, depthbuffername1, depthbuffername2;
 
        int size_w = 50;
        int size_h = 50;
 
        /// <summary>Creates a 800x600 window with the specified title.</summary>
        public Game()
            : base(800, 600, GraphicsMode.Default, "OpenTK Quick Start Sample")
        {
            VSync = VSyncMode.On;
        }
 
        /// <summary>Load resources here.</sudfmmary>
        /// <param name="e">Not used.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            GL.Enable(EnableCap.Texture2D);
 
            // load texture 
            GL.GenTextures(1, out texture1);
            GL.BindTexture(TextureTarget.Texture2D, texture1);
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, size_w, size_h, 0,
            OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
 
            // load texture 2
            GL.GenTextures(1, out texture2);
            GL.BindTexture(TextureTarget.Texture2D, texture2);
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, size_w, size_h, 0,
            OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
 
            CreateFBOandAssignTexture(out FBOHandle1, texture1, out depthbuffername1);
            CreateFBOandAssignTexture(out FBOHandle2, texture2, out depthbuffername2);
 
            // draw the triangle into the FBO that holds texture1 and texture2
            RenderTriangleInFBO(FBOHandle1);
            RenderTriangleInFBO(FBOHandle2);
        }
 
        private void CreateFBOandAssignTexture(out int fbo, int texture, out int depthbuffername)
        {
            // create and bind an FBO
            GL.Ext.GenFramebuffers(1, out fbo);
            GL.Ext.BindFramebuffer(FramebufferTarget.DrawFramebuffer, fbo);
 
            // create depth renderbuffer
            GL.Ext.GenRenderbuffers(1, out depthbuffername);
 
            // assign texture to FBO
            GL.Ext.FramebufferTexture2D(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, texture, 0);
        }
 
        private void RenderTriangleInFBO(int FBOHandle)
        {
            GL.Ext.BindFramebuffer(FramebufferTarget.DrawFramebuffer, FBOHandle);
            GL.PushAttrib(AttribMask.ViewportBit);
            {
                GL.Viewport(0, 0, size_w, size_h);
 
                // clear the screen in blue, to make it very obvious what the clear affected. only the FBO, not the real framebuffer
                GL.ClearColor(1.0f, 0.0f, 0.0f, 1.0f);
                GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit);
 
                // render a multicolored triangle
                GL.Begin(BeginMode.Triangles);
                {
                    GL.Color4(0.0f, 1.0f, 0.0f, 0.0f);
                    GL.Vertex3(-0.5, -0.5, 0.0);
 
                    GL.Color4(1.0f, 0.0f, 0.0f, 0.0f);
                    GL.Vertex3(0.5, -.5, 0.0);
 
                    GL.Color4(0.0f, 0.0f, 1.0f, 0.0f);
                    GL.Vertex3(0.0, 0.5, 0.0);
                }
                // reset to white
                GL.Color4(1.0f, 1.0f, 1.0f, 1.0f);
                GL.End();
            }
            GL.PopAttrib();
 
            GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); // disable rendering into the FBO
        }
 
        private void CreateTexture(out int texture, Bitmap bitmap)
        {
            // load texture 
            GL.GenTextures(1, out texture);
 
            //Still required else TexImage2D will be applyed on the last bound texture
            GL.BindTexture(TextureTarget.Texture2D, texture);
 
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, size_w, size_h, 0,
            OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
 
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
        }
 
        /// <summary>
        /// Called when your window is resized. Set your viewport here. It is also
        /// a good place to set up your projection matrix (which probably changes
        /// along when the aspect ratio of your window).
        /// </summary>
        /// <param name="e">Not used.</param>
 
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
            projectionMatrix = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Width / (float)Height, 1.0f, 64.0f);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref projectionMatrix);
        }
 
        /// <summary>
        /// Called when it is time to setup the next frame. Add you game logic here.
        /// </summary>
        /// <param name="e">Contains timing information for framerate independent logic.</param>
 
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);
            if (Keyboard[Key.Escape])
                Exit();
        }
 
        /// <summary>
        /// Called when it is time to render the next frame. Add your rendering code here.
        /// </summary>
        /// <param name="e">Contains timing information.</param>
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);
            GL.ClearColor(0.9f, 0.7f, 0.7f, 0f);
 
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
 
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(-1, 1, -1, 1, -1, 1.1);
 
            float third = 2.0f/3.0f;
 
            // now render texture 1 in a square (without shader)
            GL.UseProgram(0);
            GL.BindTexture(TextureTarget.Texture2D, texture1);
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(-1.0f, -1.0f + third , 0.0f);
            GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(-1.0f, -1.0f + third + third, 0.0f);
            GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(-1.0f + third, -1.0f + third + third, 0.0f);
            GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(-1.0f + third, -1.0f + third, 0.0f);
            GL.End();
 
            // now render texture 2 in a square (without shader)
            GL.UseProgram(0);
            GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
            GL.BindTexture(TextureTarget.Texture2D, texture2);
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(-1.0f + 2 * third, -1.0f + third, 0.0f);
            GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(-1.0f + 2 * third, -1.0f + third + third, 0.0f);
            GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(-1.0f + third + 2 * third, -1.0f + third + third, 0.0f);
            GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(-1.0f + third + 2 * third, -1.0f + third, 0.0f);
            GL.End();
 
            SwapBuffers();
        }
 
 
 
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
 
        [STAThread]
        static void Main()
        {
            // The 'using' idiom guarantees proper resource cleanup.
            // We request 30 UpdateFrame events per second, and unlimited
            // RenderFrame events (as fast as the computer can handle).
            using (Game game = new Game())
            {
                game.Run(30.0);
            }
        }
    }
}
Inline Images

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
KryThorne's picture

I think it's because you don't unbind the texture before drawing the triangles and texturing is still enabled.

Just before you start the Begin call for the drawing the triangles add:
GL.BindTexture(TextureTarget.Texture2D, 0);

ZTK's picture

That was it! Thank you so much KryThorne!

Danny's picture

Got to say thank you KryThorne on this one. I spent the better part of 2 days trying to solve a similar problem. Your suggestion of unbinding did the trick.

If find it interesting that so many of the sample codes out there on the web didn't make use of unbinding. I did not realize the importance of this fact.

Again thanks for sharing...it is much appreciated.