Search code examples
opengltexturesglslfborender-to-texture

How to render to a unsigned integer format


When rendering to an FBO that has an unsigned integer format presumably I can not:

gl_FragColour = uvec4(100,100,100,100);

as gl_FragColour is a vec4. So presumably I would have to define my own out?

out uvec4 colour

But will this be interpreted correctly without any other changes?

Also I wanted to use the same shader for both unsigned int formats and normal float textures with a if on a uniform (static branch). I am guessing that would be out of the question (unless I can have 2 outs and only set one?) and I might have to explore a function pointer approach?


Solution

  • So presumably I would have to define my own out?

    Yes, absolutely. You must declare your own fragment data output to write anything other than a floating-point color value.

    Mind you, "UNORM" (unsigned normalized) formats like GL_RGB8 take floating-point color as their input even though they are fixed-point data types. The real oddballs are actually the new INT/UINT formats introduced in OpenGL 3.0; they do not have floating-point to integer conversions defined and thus can only be written to using integer colors.

    But will this be interpreted correctly without any other changes?

    Traditionally what happens is that the value gets clamped to the range [0.0,1.0] and then since 99% of the time the output color format is unsigned normalized, each component is re-scaled to the fixed-point range (e.g. if GL_RGB8 => (float_color * 255)).

    If you have a floating-point or integer color buffer the clamping and scaling behavior no longer applies, but of course writing a floating-point color to an integer color buffer is undefined as discussed earlier. This is why when integral color formats (signed and unsigned integer) were introduced to core GL, gl_FragColor became deprecated (GL 3.0).

    Also I wanted to use the same shader for both unsigned int formats and normal float textures with a if on a uniform (static branch). I am guessing that would be out of the question (unless I can have 2 outs and only set one?) and I might have to explore a function pointer approach?

    Actually, this is doable. But it would require you to use two different FBO image attachment locations.

    Imagine the following shader:

    #version 330
    
    uniform bool is_float;
    
    layout(location = 0) out vec4  frag_color_float;
    layout(location = 1) out uvec4 frag_color_uint;
    
    void main (void) {
      if (is_float)
        frag_color_float = vec4 (0.1f, 0.2f, 0.3f, 1.0f);
      else
        frag_color_uint  = uvec4 (1,2,3,0xff);
    }
    

    In the above scenario, you would attach a traditional floating-point or fixed-point color buffer (GL_RGBA32F or GL_RGBA8) to GL_COLOR_ATTACHMENT0 and an unsigned integer color buffer (GL_RGBA32UI) to GL_COLOR_ATTACHMENT1.

    I do not know how practical this approach is in the long-run, but it is possible.