Chooosing precision/representation in auxiliary buffers

This is a general question about getting better than 8 bit precision in framebuffer outputs.

For example:

  manager = FilterManager(base.win, base.cam)
  self.colourTex = Texture()
  self.depthTex = Texture()
  self.normalTex = Texture()
  quad = manager.renderSceneInto(colortex = self.colourTex, 
                               depthtex = self.depthTex, 
                               auxtex = self.normalTex)

When you run this code the default outcome is that self.normalTex is rgba8 or 8 bits of precision per channel. I would like the auxiliary buffers (e.g. normals) to be higher precision (16 bit, 32 bits – anything). I have discovered that you can get nasty visual artifacts from normals that are only 8 bit precision. Is there a way I can render to a framebuffer texture and get better than 8 bit precision in the auxiliary data? Any help would be appreciated.

The question is related to this one about precision in textures: Texture Precision for which the answer was “not yet” in 2012. I have a specific use case which is textures being used as framebuffers. Similar to that prior question, I tried changing the format using

  self.normalTex.setup2dTexture

with changes like setting the components to Texture.TFloat and/or making it format 35 (Frgba32) However, after normalTex gets filled by the renderSceneInto and I look at the format it is just rgba8 again.

I saw elsewhere that some people are trying packing the data into multiple 8 bit floats in the shader that first creates the normals (which starts with full float precision). However such packed data can’t be properly interpolated by texture(). I guess you could transfer it to a texture with 16 bit or higher components by hand (using a compute shader or something) and hopefully they would then be correctly interpolated by texture(). However, this seems like a clunky workaround to something that hopefully has a more direct solution now.

This question is related to the same use case for this one: Controlling blend modes -- in general and for auxiliary buffers Specifically, at low precision I can easily fit several variables into auxiliary output buffers but I may need closer control over how aux is represented and blended to make use of higher precision output.

This is supported, but FilterManager is unfortunately not exposing this functionality. With the lower-level API it is possible to use setAuxFloat on FrameBufferProperties instead of setAuxRgba in order to request a number of 32-bit-per-channel floating-point auxiliary buffers. (There is also setAuxHrgba for 16-bit per channel.)

Take a look at the FilterManager source code; it should be fairly simple to either modify it to use 32-bit buffers, or you could choose to use the lower-level API directly.

We plan on eventually introducing a system that makes it easier to select image formats for individual framebuffer attachments. If the existing system is not sufficient for your use cases, please file an issue report and we’ll look at implementing a solution sooner than that.

1 Like

A fairly dumb followup question: I made a modified renderSceneInto with 16 bit (or 32 bit) buffers. I can verify after the render that the texture has the correct type. However, when I look at it in the next shader it doesn’t contain the correct values of the normals. They all seem to be (0.5,0.5,1). This is coincidentally the clear value for the texture set in FilterManager.py. So it implies that it didn’t write to it after all I guess?

Perhaps the changes I made were not enough? A quick summary is below…

def New_createBuffer(FM, name, xsize, ysize, texgroup, depthbits=1):
   ...
    if (auxtex0 != None):
#        props.setAuxRgba(1)   #  8 bit
        props.setAuxHrgba(1)  # 16 bit
    ...
    if (auxtex0):
        buffer.addRenderTexture(auxtex0, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxHrgba0)


def New_renderSceneInto(FM, depthtex=None, colortex=None, auxtex=None, auxbits=0, textures=None):
    ...
    buffer = New_createBuffer(FM,"filter-base", winx, winy, texgroup)
    ...
    if (auxtex0):
#        buffer.setClearActive(GraphicsOutput.RTPAuxRgba0, 1)  # 8 bit
#        buffer.setClearValue(GraphicsOutput.RTPAuxRgba0, (0.5, 0.5, 1.0, 0.0))
        buffer.setClearActive(GraphicsOutput.RTPAuxHrgba0, 1)  #16 bit
        buffer.setClearValue(GraphicsOutput.RTPAuxHrgba0, (0.5, 0.5, 1.0, 0.0))