<p>First of all, you don't need position in the G-buffer at all. The position of a pixel can be <a href="https://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/" rel="noreferrer">reconstructed from the depth buffer</a>, knowing the camera setup and the pixel's screen-space xy position. So you can get rid of that whole buffer.</p>

<p>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.</p>

<p>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.</p>

<p>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.</p>

<p>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 <a href="http://jcgt.org/published/0003/02/01/" rel="noreferrer">octahedral mapping</a>.</p>

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

<ul>
<li>Material color + metallic: RGBA8</li>
<li>Octahedral world-space normal + roughness + AO: RGBA16</li>
</ul>

<p>and that's all! Only 12 bytes per pixel.</p>

<p>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.</p>

CC BY-SA 3.0
2017-04-07T15:33:34.287Z
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
CC BY-SA 3.0
2017-04-06T23:22:46.880Z
2017-04-06T23:22:46.880Z
Nathan Reed
<p>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. </p>

<p>What I have so far is 4 3-part vectors: </p>

<ul>
<li>Albedo/Diffuse</li>
<li>Normal</li>
<li>Tangent</li>
<li>Position</li>
</ul>

<p>And 4 single components</p>

<ul>
<li>Metallic</li>
<li>Roughness</li>
<li>Height</li>
<li>Ambient Occlusion</li>
</ul>

<p>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. </p>

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

<optimisation><deferred-rendering>
How much precision do I need in my G-Buffer?
Mark A. Ropper
# How much precision do I need in my G-Buffer?

(Asked by: username_0 on 2017-04-06)

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?

**Tags:** optimisation, deferred-rendering


---

## 1 answers

### Answer by username_1 on 2017-04-06. Score: 4 | Accepted answer

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](https://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/), 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](http://jcgt.org/published/0003/02/01/).

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.

> **Comments:**
> **username_2**: 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).  

---

