6

I'm in the process of working out how to pack all the information I need for a Physically Based Deferred Renderer into a G-Buffer without using an obscene amount of render targets.

What I have so far is 4 3-part vectors:

  • Albedo/Diffuse
  • Normal
  • Tangent
  • Position

And 4 single components

  • Metallic
  • Roughness
  • Height
  • Ambient Occlusion

A naive approach is to bundle one of the single components into the alpha (fourth) channel with one of the 3-part vectors, which is my current line of investigation. However, given that four 4-channel full precision floating point render targets isn't small I understand it's common to use half precision and even smaller representations to be more memory conscious.

What I'm asking is: which components can I safely cut precision down on without losing quality, and by how much?

Mark A. Ropper
  • 287
  • 2
  • 6

1 Answers1

4

First of all, you don't need position in the G-buffer at all. The position of a pixel can be reconstructed from the depth buffer, knowing the camera setup and the pixel's screen-space xy position. So you can get rid of that whole buffer.

Also, you don't ordinarily need tangent vectors in the G-buffer either. They're only needed for converting normal maps from tangent space, and for parallax mapping; these would be done during the G-buffer fill pass (when you have tangents from the mesh you're rendering), and the G-buffer would only store normals in world or view space.

Material properties like colors, roughness, and metallic are usually just 8-bit values in the G-buffer, since they're sourced from 8-bit textures. Same for AO.

Height is also not needed in the G-buffer unless you're going to be doing some kind of multi-pass blending that depends on it, but if you do need it, 8 bits is probably enough for that too.

Normals can be benefit from being stored as 16-bit values rather than 8-bit. Half-float is okay, but 16-bit fixed-point is even better, as it gives you more uniform precision across all orientations (half-float is more precise near the axes and loses some precision away from them). Moreover, you can cut them from 3 components down to 2 using octahedral mapping.

So, at the end of the day, a minimal G-buffer might look like:

  • Material color + metallic: RGBA8
  • Octahedral world-space normal + roughness + AO: RGBA16

and that's all! Only 12 bytes per pixel.

Alternatively, you could use an RG16 buffer for the normals, and move roughness + AO into a separate 8-bit buffer. That would give you some room to grow should you eventually need more G-buffer components of either 8-bit or 16-bit sizes.

Nathan Reed
  • 24,302
  • 2
  • 63
  • 103
  • 1
    Also, metalness is often a binary value, and assuming that you will not use roughness gradients (so as to see the impact of reduced precision), you could store metalness and roughness in a single 8 bit channel (1 bit for metalness and 7 for roughness). – Kostas Anagnostou Apr 07 '17 at 15:33