Storing depth in color channels of an FBO

Sage
Posts: 1,199
Joined: 2004.10
Post: #1
This is relevant to my post here ( http://idevgames.com/forum/showthread.php?t=18498 ) but I figure it deserves a thread of its own if anybody searches for this problem.

I want to store depth values in the color channel of an FBO. Why? Because I don't have the ability to create a depth cubemap...

Googling gets me a lot of unanswered questions. Perhaps my googling is poor.

Anyway, the article I'm working against is a GPUGem - http://http.developer.nvidia.com/GPUGems..._ch12.html - and it describes the following method for encoding depth to color and back:

Code:
// encode
Out.r = SquaredDistance * 2^0
Out.g = SquaredDistance * 2^8
Out.b = SquaredDistance * 2^16
Out.a = SquaredDistance * 2^24

//decode
DepthValue =
ShadowSample.r / 1 +
ShadowSample.g / 256 +
ShadowSample.b / 65536 +
ShadowSample.a / 16777216

The other method described looks to me like its for float textures, I'm not certain. The article is not very well written.

Now, since I've got my cubemap code working and my pipeline ready to start rendering depthmaps, I went and implemented the encode/decode in C++ just for giggles to see what kind of precision loss there is, and I nearly died. It's absurd.

Here's my c++
Code:
#include <iostream>
#include <cmath>

struct vec4 {

    int r,g,b,a;
    
    vec4( void ):
        r(0), g(0), b(0), a(0)
    {}
    
    vec4( int R, int G, int B, int A ):
        r(R), g(G), b(B), a(A)
    {}
    
};


vec4 EncodeDepth( float depth )
{
    return vec4(
        1 * depth,
        256 * depth,
        65536 * depth,
        16777216 * depth
    );
}

float DecodeDepth( const vec4 &shadowSample )
{
    return shadowSample.r / 1.0f +
           shadowSample.g / 256.0f +
           shadowSample.b / 65536.0f +
           shadowSample.a / 16777216.0f;
}

int main (int argc, char * const argv[])
{
    vec4 v;
    
    for ( int i = 0; i <= +100; i++ )
    {
        float f = float(i) / 100.0f;
        vec4 v = EncodeDepth( f );
        float f2 = DecodeDepth( v );
        
        std::cout << i << "\t" << f << " -> " << f2 << " error: " << std::abs( f2 - f ) << std::endl;
    }


    return 0;
}

And here's the output:
Code:
0    0 -> 0 error: 0
1    0.01 -> 0.027807 error: 0.017807
2    0.02 -> 0.0595202 error: 0.0395202
3    0.03 -> 0.0873425 error: 0.0573425
4    0.04 -> 0.119056 error: 0.0790557
5    0.05 -> 0.146863 error: 0.0968627
6    0.06 -> 0.178591 error: 0.118591
7    0.07 -> 0.206398 error: 0.136398
8    0.08 -> 0.238112 error: 0.158112
9    0.09 -> 0.26984 error: 0.17984
10    0.1 -> 0.297647 error: 0.197647
11    0.11 -> 0.32936 error: 0.21936
12    0.12 -> 0.357183 error: 0.237183
13    0.13 -> 0.388896 error: 0.258896
14    0.14 -> 0.416718 error: 0.276718
15    0.15 -> 0.448431 error: 0.298431
16    0.16 -> 0.476238 error: 0.316238
17    0.17 -> 0.507967 error: 0.337967
18    0.18 -> 0.53968 error: 0.35968
19    0.19 -> 0.567487 error: 0.377487
20    0.2 -> 0.599216 error: 0.399216
21    0.21 -> 0.627023 error: 0.417023
22    0.22 -> 0.658736 error: 0.438736
23    0.23 -> 0.686558 error: 0.456558
24    0.24 -> 0.718271 error: 0.478271
25    0.25 -> 0.75 error: 0.5
26    0.26 -> 0.777807 error: 0.517807
27    0.27 -> 0.80952 error: 0.53952
28    0.28 -> 0.837343 error: 0.557343
29    0.29 -> 0.869056 error: 0.579056
30    0.3 -> 0.896863 error: 0.596863
31    0.31 -> 0.928591 error: 0.618591
32    0.32 -> 0.956398 error: 0.636398
33    0.33 -> 0.988112 error: 0.658112
34    0.34 -> 1.01984 error: 0.67984
35    0.35 -> 1.04765 error: 0.697647
36    0.36 -> 1.07936 error: 0.71936
37    0.37 -> 1.10718 error: 0.737183
38    0.38 -> 1.1389 error: 0.758896
39    0.39 -> 1.16672 error: 0.776718
40    0.4 -> 1.19843 error: 0.798431
41    0.41 -> 1.22624 error: 0.816238
42    0.42 -> 1.25797 error: 0.837967
43    0.43 -> 1.28968 error: 0.85968
44    0.44 -> 1.31749 error: 0.877487
45    0.45 -> 1.34922 error: 0.899216
46    0.46 -> 1.37702 error: 0.917023
47    0.47 -> 1.40874 error: 0.938736
48    0.48 -> 1.43656 error: 0.956558
49    0.49 -> 1.46827 error: 0.978271
50    0.5 -> 1.5 error: 1
51    0.51 -> 1.52781 error: 1.01781
52    0.52 -> 1.55952 error: 1.03952
53    0.53 -> 1.58734 error: 1.05734
54    0.54 -> 1.61906 error: 1.07906
55    0.55 -> 1.64686 error: 1.09686
56    0.56 -> 1.67859 error: 1.11859
57    0.57 -> 1.7064 error: 1.1364
58    0.58 -> 1.73811 error: 1.15811
59    0.59 -> 1.76984 error: 1.17984
60    0.6 -> 1.79765 error: 1.19765
61    0.61 -> 1.82936 error: 1.21936
62    0.62 -> 1.85718 error: 1.23718
63    0.63 -> 1.8889 error: 1.2589
64    0.64 -> 1.91672 error: 1.27672
65    0.65 -> 1.94843 error: 1.29843
66    0.66 -> 1.97624 error: 1.31624
67    0.67 -> 2.00797 error: 1.33797
68    0.68 -> 2.03968 error: 1.35968
69    0.69 -> 2.06749 error: 1.37749
70    0.7 -> 2.09922 error: 1.39922
71    0.71 -> 2.12702 error: 1.41702
72    0.72 -> 2.15874 error: 1.43874
73    0.73 -> 2.18656 error: 1.45656
74    0.74 -> 2.21827 error: 1.47827
75    0.75 -> 2.25 error: 1.5
76    0.76 -> 2.27781 error: 1.51781
77    0.77 -> 2.30952 error: 1.53952
78    0.78 -> 2.33734 error: 1.55734
79    0.79 -> 2.36906 error: 1.57906
80    0.8 -> 2.39686 error: 1.59686
81    0.81 -> 2.42859 error: 1.61859
82    0.82 -> 2.4564 error: 1.6364
83    0.83 -> 2.48811 error: 1.65811
84    0.84 -> 2.51984 error: 1.67984
85    0.85 -> 2.54765 error: 1.69765
86    0.86 -> 2.57936 error: 1.71936
87    0.87 -> 2.60718 error: 1.73718
88    0.88 -> 2.6389 error: 1.7589
89    0.89 -> 2.66672 error: 1.77672
90    0.9 -> 2.69843 error: 1.79843
91    0.91 -> 2.72624 error: 1.81624
92    0.92 -> 2.75797 error: 1.83797
93    0.93 -> 2.78968 error: 1.85968
94    0.94 -> 2.81749 error: 1.87749
95    0.95 -> 2.84922 error: 1.89922
96    0.96 -> 2.87702 error: 1.91702
97    0.97 -> 2.90874 error: 1.93874
98    0.98 -> 2.93656 error: 1.95656
99    0.99 -> 2.96827 error: 1.97827
100    1 -> 4 error: 3

COMICAL error values. I don't see how I could get shadow mapping to work at all with this kind of error.

What am I missing? Did I cimpletely brainfart on the encode/decode?
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #2
It looks to me like you have your multiplications and your divisions reversed. I'd have expected to see Out.a = SquaredDistance / 2^24, ShadowSample.a * 16777216, etc. I can't make any sense of doing it the other way around.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #3
Well, in case somebody stumbles on this thread, in my poking and searching I found:

http://www.gamedev.net/community/forums/..._id=486847

It works very accurately for normalized depth values!
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #4
So, as a followup, the code I found doesn't actually seem to work. Or maybe it does but I'm not using it correctly.

Here's my implementation, in GLSL.
Code:
#define DEBUG_PACKING 0

vec4 FloatToFixed( in float depth )
{
    #if DEBUG_PACKING
        return vec4( depth, depth,depth,1 );
    #else

        const float toFixed = 255.0/256.0;
    
        return vec4(
            fract(depth*toFixed*1.0),
            fract(depth*toFixed*255.0),
            fract(depth*toFixed*255.0*255.0),
            fract(depth*toFixed*255.0*255.0*255.0)
        );
    #endif

}

float FixedToFloat( in vec4 shadowSample )
{
    #if DEBUG_PACKING
        return shadowSample.r;
    #else

        const float fromFixed = 256.0/255.0;
        return shadowSample.r*fromFixed/(1.0) +
               shadowSample.g*fromFixed/(255.0) +
               shadowSample.b*fromFixed/(255.0*255.0) +
               shadowSample.a*fromFixed/(255.0*255.0*255.0);    
    #endif

}

here's what gets rendered:
[Image: PointLightin-2009-12-06-04.png]

And a closeup:
[Image: PointLightin-2009-12-06-03.png]


So, it's all "holy" ( haha hah the cuebmap looks like a cross too)

Note, when I change DEBUG_PACKING to 1, and drop to only 8-bits of precision, it works great. It's not accurate, but I don't get the specking artifacts.

Any thoughts?
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #5
Interestingly, if I only use 24 bits of precision ( ignoring alpha ) the technique works correctly. I certainly can live with 24 bit depth precision, so this is fine.

Though I am perplexed that the alpha channel would result in this noise.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #6
So, it was a stupid oversight. You can't expect meaningful results doing this when you've got blending and alpha testing enabled. Rasp
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #7
It's also not too useful to expand a 23 bit mantissa to 32 bits...


As a future exercise, I'll point out another path you can play around with:

On a renderer that exports ARB_texture_float and ARB_texture_rg, you ought to be able to create an R32F cubemap, and render directly to it. That way you can just write out floats directly and skip the pack/unpack.

However as of 10.6.2, the Radeon X1600 supports sampling from this format but not rendering to it. Hopefully that will be rectified in a future update.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #8
arekkusu Wrote:However as of 10.6.2, the Radeon X1600 supports sampling from this format but not rendering to it. Hopefully that will be rectified in a future update.

So, it's possible a driver update could enable this feature? That would be amazing! Is it in general available on more modern GPUs? Say, the 9400 or 9600?

( I'm planning on buying a new MBP in the next six months or so )
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #9
Nvidia currently doesn't export ARB_texture_rg in Mac OS X, so no it isn't generally available.

But the hardware supports it and it works in Windows. So yes, a driver update could enable this feature.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  ? Find color value of 'pixel' in color buffer? Elphaba 1 4,278 Jul 22, 2009 01:23 PM
Last Post: Bachus
  Storing Map Data seven 1 2,741 Feb 11, 2005 11:57 AM
Last Post: CarbonX