Advertisement

G-Buffer 2 Channel Normal

Started by December 29, 2018 06:59 PM
5 comments, last by Vilem Otte 6 years, 1 month ago

As I understand it, compress 3 channel normal to 2 channel works as following. Instead of storing x, y and z, we store x and y. Z can then be calculated as sqrt(1 - x^2 - y^2).

My question is how can we know the sign of Z?

If the normal is geometry normal, than we can assume the Z is always facing towards camera. But what if the normal is the final normal, e.g. after-normal-mapping normal? They can point to anywhere.

I'm too lazy to go through unity shader files to find out the answer myself and it will do more damage if I come to an incorrect conclusion. I know I can get more insights here.

30 minutes ago, acerskyline said:

My question is how can we know the sign of Z?

If the normal is geometry normal, than we can assume the Z is always facing towards camera. But what if the normal is the final normal, e.g. after-normal-mapping normal? They can point to anywhere.

In my home project, I just shoved the sign bit into another channel somewhere where i had space left. For a time I used the roughness channel for it, it was a full float channel, but roughness goes only from 0 to 1, so the sign portion was entirely unused. Nowadays I put it in one of the free 2 bit alpha channels in one of the g-buffers. Another solution is to use a different kind of encoding that handles this kind of stuff, but I don't have experience with those.

Advertisement

https://aras-p.info/texts/CompactNormalStorage.html

 

2 hours ago, Makusik Fedakusik said:

I have actually stumbled on that before. But, it seems the link on that page doesn't work anymore. I googled the PDF directly and found out the presenter did not provide any fix but simply explained the cause. But thank you for helping me!

1.JPG

2.JPG

2 hours ago, LandonJerre said:

In my home project, I just shoved the sign bit into another channel somewhere where i had space left. For a time I used the roughness channel for it, it was a full float channel, but roughness goes only from 0 to 1, so the sign portion was entirely unused. Nowadays I put it in one of the free 2 bit alpha channels in one of the g-buffers. Another solution is to use a different kind of encoding that handles this kind of stuff, but I don't have experience with those.

That's what I'm looking for. Thanks! It seems yours way is the standard way to do it. (slip the sign in some tiny space or combine it with some always-positive value)

On 12/29/2018 at 2:57 PM, Makusik Fedakusik said:

Interesting page. It motivated me to try some of these methods.

The one that preserved the best quality in my project was Method #7: Stereographic Projection. Most of the others ended up having few noticeable artifacts in the negative values when zooming in.

I'm curious to hear about other people's result.

I'm currently using Lambert Azimuthal Equal-Area projection (it is also noted in linked article), which is a spheremap transform. I'm storing normals in 2x 16-bit floating point channels - which works quite good (using just 8-bit precision isn't enough - especially on reflective surfaces).

I haven't noticed any artifacts (I've tried multiple algorithms for 8-bit, but all were visible on smooth reflective surfaces). Screenshot attached (notice the visible 'blocky' pattern on first image in reflection):

normals_lp.thumb.png.090ef7d79b2640b14b29cc5a15bee076.png

Fig. 01 - Normals stored in 2x 8-bit channel with spherical transform encoding

normals_hp.thumb.png.27294344318985db733e0c85acc39ddb.png

Fig. 02 - Normals stored in 2x 16-bit channel with spherical transform encoding

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

This topic is closed to new replies.

Advertisement