Ugh, I was thinking it should work with ARGB surfaces since that's what my app is using, but obviously there aren't any precission issues when doing the RGB -> LogLuv -> RGB test in an single pixelshader. I'll try messing around with the test app later today using an intermediate render target, hopefully I can find out something useful. I'm not sure it's a precission issue though, since I did a quick check with this:
float4 lle = LogLuvEncode(color.rgb); lle *= 256; lle = floor(lle); lle /= 256; float3 lld = LogLuvDecode(lle);
And that does work (only slightly less saturated, but no banding or other artifacts), so it would seem the approach is sound. That at least narrows down the problem to either the conversion/rendering to an RGB surface or some weird filtering issue. Have you tried using point filtering to test how that works out?
Anyway... [smile]
I had a quick go at it and there was indeed some banding popping up again when doing RGB -> LogLuv -> (backbuffer resolve) -> LogLuv -> RGB. I had been wondering if the 128 & 256 numbers were valid for the 0-255 color range, so I decided to change those to 127 & 255 and that actually removes the banding even when using the intermediate buffer. Guess it was more of a silly range issue than precission. This code seems to do the trick:
// M matrix, for encodingconst static float3x3 M = float3x3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969);// Inverse M matrix, for decodingconst static float3x3 InverseM = float3x3( 6.0013, -2.700, -1.7995, -1.332, 3.1029, -5.7720, .3007, -1.088, 5.6268); float4 LogLuvEncode(in float3 vRGB) { float4 vResult; float3 Xp_Y_XYZp = mul(vRGB, M); Xp_Y_XYZp = max(Xp_Y_XYZp, float3(1e-6, 1e-6, 1e-6)); vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z; float Le = 2 * log2(Xp_Y_XYZp.y) + 127; vResult.w = frac(Le); vResult.z = (Le - (floor(vResult.w*255.0f))/255.0f)/255.0f; return vResult;}float3 LogLuvDecode(in float4 vLogLuv){ float Le = vLogLuv.z * 255 + vLogLuv.w; float3 Xp_Y_XYZp; Xp_Y_XYZp.y = exp2((Le - 127) / 2); Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y; Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z; float3 vRGB = mul(Xp_Y_XYZp, InverseM); return max(vRGB, 0);}
Ps. for giggles I tried to see if Christers shortcut (Le / 255) worked this way, but that's still producing massive amounts of banding. I'm really curious to know if no one else ran into this or if it's simply no problem on PS3 hardware.
[Edited by - remigius on July 8, 2008 1:20:45 AM]