6
votes

How do I properly render an integer ID of an object to an integer texture buffer?

Say I have a texture2D with internal format GL_LUMINANCE16 and i attach it as color attachment to my FBO.

When rendering an object, i pass an integer ID to the shader and would like to render this id into my integer texture.

fragmentshader output is of type vec4 however. How do I properly transform my ID to four component float and avoid conversion inaccuracies such that in the end the integervalue in my integer texture target corresponds to the integer ID i wanted to render?

3

3 Answers

4
votes

There are several problems with your question.

First, GL_LUMINANCE16 is not an "integer texture." It is a texture that contains normalized unsigned integer values. It uses integers to represent floats on the range [0, 1]. If you want to store actual integers, you must use an actual integer image format.

Second, you cannot render to a luminance texture; they are not color-renderable formats. If you actually want to render to a single-channel texture, you must create a single-channel image format. So instead of GL_LUMINANCE16, you use GL_R16UI, which is a 16-bit single-channel unsigned integral image format.

Now that you have this set up correctly, it's pretty trivial. Define a uint fragment shader output and have your fragment shader write your uint value to it. This uint could come from the vertex shader or from a uniform; however you want to do it.

Obviously you'll also need to attach your texture or renderbuffer to an FBO, but I'm fairly sure you know that.

One final thing: don't use the phrase "texture buffer" unless you mean one of these. Otherwise, it gets confusing.

5
votes

I still don't think that there is a clear answer here. So here is how I made it work through a 2D texture:

// First, create a frame buffer:
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

// Then generate your texture and define an unsigned int array:
glGenTextures(1, &textureid);
glBindTexture(GL_TEXTURE_2D, textureid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, w, h, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

// Attach it to the frame buffer object:
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, textureid, 0);

// Before rendering    
glBindFramebuffer(GL_FRAMEBUFFER, fbo);    
GLuint buffers[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; // this assumes that there is another     texture that is for the render buffer. Color attachment1 is preserved for the element ids.
glDrawBuffers(2, buffers);

// Clear and render here
glFlush(); // Flush after just in case
glBindFramebuffer(GL_FRAMEBUFFER, 0);

On the GLSL side the fragment shader should have (4.3 core profile code here):

layout(location = 0) out vec4 colorOut; // The first element in 'buffers' so location 0    
layout(location = 1) out uvec4 elementID; // The second element in 'buffers' so location 1. unsigned int vector as color

// ...
void main()
{
//...

elementID = uvec4( elementid, 0, 0, 0 ); // Write the element id as integer to the red channel.

}

You can read the values on the host side:

unsigned int* ids = new unsigned int[ w*h ];
glBindTexture(GL_TEXTURE_2D, textureid);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, ids);
0
votes

I suppose that your integer ID is an identifier for a set of primitives; indeed it can be defined as an shader uniform:

uniform int vertedObjectId;

Once you render, you want to store the fragment processing into an integer texture. Note that integer textures shall be sampled with integer samplers (isampler2D), which returns integer vectors (i.e. ivec3).

This texture can be attached to a framebuffer object (be aware of framebuffer completeness). The framebuffer attachment can be bound to an integer output variable:

out int fragmentObjectId;

void main() {
    fragmentObjectId = vertedObjectId;
}

You need a few extension supports, or an advanced OpenGL version (3.2?).