Original Post
For my recent project (targeting the 360 and PC through XNA) I've been doing some experimenting with alternative formats for HDR. If I were just doing PC I'd probably just use fp16 and be done with it, but for the 360 I've been trying out some alternatives since fp16 has some disadvantages on that platfrom (and since the special fp10 frame buffer format isn't available through XNA [flaming]) Anyway my most recent adventure has been with LogLuv encoding (Heavenly Sword's famous NAO32 technique). I've managed to come up with a working implmentation based on Christer Ericson's optimized Cg code, however I've been getting artifacts in the final result:
I'm guessing that I'm running into discontinuites somewhere...however I can't seem to figure out where. If anyone has any experience with this and could point in the right direction, I'd be eternally grateful. This is the shader code I'm using: I've tried playing around with clamping the values of Xp_Y_XYZp to 1e-6 and also the value of vLogLuv.y before division, but it hasn't gotten rid of the artifacts. I know Marco (Heavenly Sword dev) mentioned you had be careful to avoid carry problems if you're linearly filtering, however I'm getting these artifacts even when I just do a straight decode of the whole render target without bloom or tone-mapping. So yeah...I'm basically stumped. Any mathematical geniuses out there who can help out a poor soul? [smile] By the way, this is the original paper describing the LogLuv format.
I'm guessing that I'm running into discontinuites somewhere...however I can't seem to figure out where. If anyone has any experience with this and could point in the right direction, I'd be eternally grateful. This is the shader code I'm using:
// M matrix, for encoding
const 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 decoding
const 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) + 128;
vResult.z = Le / 256;
vResult.w = frac(Le);
return vResult;
}
float3 LogLuvDecode(in float4 vLogLuv)
{
float Le = vLogLuv.z * 256 + vLogLuv.w;
float3 Xp_Y_XYZp;
Xp_Y_XYZp.y = exp2((Le - 128) / 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);
}
