Problem using FrameBuffer for Picking

I want to use a FrameBuffer to hold an ObjectID and a VertexIndex so I can read the pixel data and determine the object the mouse is over.

I have managed to do this OK for an 32bit Unsigned Integer FrameBuffer. However not all hardware is supporting the Integer frame buffer (Intel HD 4000 does not supply a 32bit buffer and even a 16bit buffer does not seem to work correctly). 16bit with a max of 65k may be too small.

I want to do the same thing using a RGB32F FrameBuffer. However, the FrameBuffer ends up with no data (all pixels are (0,0,0)). The following code snippets show my basic coding (in Pascal)

I am using NVIDEA with OpenGL4.4 and latest drivers for development.

FrameBuffer setup


// Create the FBO
      glGenFramebuffers(1, @fFBOHandle);
      glBindFramebuffer(GL_FRAMEBUFFER, fFBOHandle);

  // Create the texture object for the primitive information buffer
      glGenTextures(fColBufSize, @fColorTexture[0]);

      for I := 0 to fColBufSize-1 do
        glBindTexture(GL_TEXTURE_2D, fColorTexture[i]);
        case aMode of
           GL_RGB32F: glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB32F,  SizeX, SizeY, 0, GL_RGB, GL_FLOAT,  nil  );
           GL_RGB16F: glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB16F,  SizeX, SizeY, 0, GL_RGB, GL_FLOAT,  nil  );
        glTexImage2D( GL_TEXTURE_2D, 0, aMode,  SizeX, SizeY, 0, GL_RGB, GL_FLOAT,  nil  );
        glFramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + I, GL_TEXTURE_2D, fColorTexture[i], 0 );
        fBuffer[i] :=  GL_COLOR_ATTACHMENT0 + I;

      glDrawBuffers(fColBufSize, @fBuffer[0]);
      glBindTexture(GL_TEXTURE_2D, 0);

  // depth
      if fIncDepthBuffer then
        glGenTextures(1, @fDepthBuff);
        glBindTexture(GL_TEXTURE_2D, fDepthBuff);
        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fDepthBuff, 0);
        glBindTexture(GL_TEXTURE_2D, 0);

    // Verify that the FBO is correct
      aStatus := glCheckFramebufferStatus(GL_FRAMEBUFFER);

    // Restore the default framebuffer
      glReadBuffer(GL_NONE);    //for older hardware

      glBindRenderbuffer(GL_RENDERBUFFER, 0);
      glBindFramebuffer(GL_FRAMEBUFFER, 0);

FrameBuffer Enable Writing (beginning of frame buffer render loop) and will end with glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

  if fFBOHandle  = 0 then exit;

  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fFBOHandle);

  if ClearBuffers then
     for I := 0 to self.fColBufSize-1 do
       glClearBufferfv(GL_COLOR,I,@BufClearColF);  //all zero

     if fIncDepthBuffer then
       glClearBufferfv(GL_DEPTH,0,@BufClearDepthF)  //1

  fIsLinked  := True;
  Result     := True;

FrameBuffer Read Pixels

  Var PixelID : array [0..2] of GLFLOAT; //3 x 4 Byte
      PixelPos: array [0..2] of GLFLOAT; //3 x 4 Byte

    ObjID := 0;
    if fFBOHandle  = 0 then exit;


    glBindFramebuffer(GL_READ_FRAMEBUFFER, fFBOHandle);

    if fColBufSize>=1 then
      glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, @PixelID[0]);

    if fColBufSize>=2 then
      glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, @PixelPos[0]);


  //release frame buffer
    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);

    ObjID    := Trunc(PixelID[0]);
    GripIndex:= Trunc(PixelID[1]);

Vertex Shader

     layout(location=0) in vec3 VertexPosition ;   
     layout(std140) uniform WorldUniform   
      vec3 GlobalOffset; 
    } World; 
     uniform mat4 ModelViewProjection; 
    out float InstanceID;
  void main(void) 
    InstanceID  = float(gl_InstanceID); 
    gl_Position =  ModelViewProjection * vec4((VertexPosition - World.GlobalOffset), 1.0) ;   

Fragment Shader

   uniform float ObjectID;
 //used for selection render
  in float InstanceID;
  layout (location = 0) out vec3 FragColor ;

  void main(void) 
      FragColor   = vec3(float(ObjectID), float(InstanceID) ,0);


If you only need to store 2 values, GL_RGBA16UI (4x 16-bit unsigned integers per pixel) would suffice; split each ID into high-word and low word and store them separately, e.g.

FragColor = uvec4(ObjectID & 0xFFFFU, ObjectID >> 16, InstanceID & 0xFFFFU, InstanceID >> 16);

Or if the GLSL version doesn’t support bitwise operations:

FragColor = uvec4(ObjectID % 0x10000U, ObjectID / 0x10000U, InstanceID % 0x10000U, InstanceID / 0x10000U);

Also, you say:

You are using 16-bit unsigned integer (e.g. RG_16UI), not signed integer (RG_16I) or fixed-point (GL_RG16), right?

And if you need to use 32-bit floats, you still only need 2 channels (i.e. RG_32F rather than RGB_32F).

Thanks for the reply.

Yes a good idea using the 4 channel 16bit. I may only need three as the InstanceID will be less than 65k.
Yes I did use RGB_16UI and it worked OK on my NVIDEA and AMD but failed on the Intel HD 4000 (with recent drivers).

My main concern is that using the float approach fails to write data to the buffer on all my hardware and I want to understand why.

Once working with float option I will make a choice of which approach (UI or F) to take and optimize the channel count appropriately. I need something that will work on a good range of hardware, including the Intel HD.

My problem was that I needed to disable Blending. lesson learned, watch the current GL state. Blending is not an issue with a UI buffer??

No. Blending is only performed for fixed-point or floating-point colour buffers.

Odd. According to Wikipedia, HD 4000 should support OpenGL 3.3 on Linux, 4.0 on Windows, 4.1 on Mac. All of those versions require RG32UI and RGBA16UI to be supported for both textures and renderbuffers. 3-component (RGB) formats (including RGB_16UI), _SNORM formats and compressed formats must be accepted for textures but not renderbuffers. I’m not sure whether the renderbuffer restrictions are also supposed to apply to textures which are bound to framebuffers, but it might be worth checking whether 2- or 4-component formats such as RG_16UI, RG_32UI or RGBA_16UI work.

I believe you need to use RGBA16UI on Intel. I had a similar issue with RGB32I on the HD4600, and using RGBA32I fixed the problem.

Edit: Yes, on Intel you must disable blending, or it’ll fail to write to integer framebuffers. Ran into that as well.