Context: I invented a way to get ugly GI for low end platform (target being Mali 400 mp GPU) through texture feedback and texture PVS.
Problem: In order to bypass the precision limit of 8bits textures, when I accumulate light in multiple pass, I tried to encode the result as YCoCg with 16bits float packing of the Y components in the RG channel. However the result don't work as intended, in the test case I have no colors, but the decoding back to RGB show red and green results instead of shades of gray.
Note: the two planes on the right scene panels are test objects with shaders that validate the algorithm, the left quad show the encoding and decoding, the right one is the control objects with a regular unlit shader, as the two are similar, it shows the result are correctly encoded and decoded in this case
Here is the raw Encoded data as RG as Y, and CoCg as BA (all textures data are R8G8B8A8)
RG as 16bits luminance encoding split
Here is the code that encode to chromalum in fragment:
//chromaLum encoding
float3 chromalum = RgbToYCoCg(irradiance);
//divide lum by num ray
chromalum.x /= numRays;
//turn the Y (lum) into 16bits
float4 lum = Float32ToIntrgba(chromalum.x);
//encode split lum and chroma
float4 result = float4(lum.x,lum.y, chromalum.y,chromalum.z);
//return 16bit encoding to accumulation //display material must reconstruct RGB from chroma lum
return result;
And here is the part that decode it back
fixed4 color = tex2D(_MainTex, input.uv);
fixed4 GI = tex2D(_GI, input.uv); //return GI;
//decode GI (16bit -> chromalum -> RGB)
float luminance = IntrgbaToFloat32(float4(GI.xy,0,0)); //return luminance*16;
float3 lighting = YCoCgToRgb(float3(luminance,GI.z,GI.w));
// return float4(lighting,1);
GI = float4(lighting,1);
color += GI; //should mask albedo with light, not adding light, currently color is black, so add is a hack
return color;
Function details are here:
float3 RgbToYCoCg(float3 c){
float Y = 0.25 * c.r + 0.5 * c.g + 0.25 * c.b;
float Cb = 0.5 * c.r - 0.0 * c.g - 0.50 * c.b;
float Cr = -0.25 * c.r + 0.5 * c.g - 0.25 * c.b;
return float3(Y, Cb, Cr);
}
float3 YCoCgToRgb(float3 c){
float R = c.x + c.y - c.z;
float G = c.x + c.z;
float B = c.x - c.y - c.z;
return float3(R, G, B);
}
float4 Float32ToIntrgba(float floatValue){
const float toFixed = 255.0/256;
float4 output;
output.r = frac(floatValue*toFixed*1);
output.g = frac(floatValue*toFixed*255);
output.b = frac(floatValue*toFixed*255*255);
output.a = frac(floatValue*toFixed*255*255*255);
return output;
}
float IntrgbaToFloat32(float4 Value){
const float fromFixed = 256.0/255;
float input;
input =
Value.r*fromFixed/(1)
+Value.g*fromFixed/(255)
+Value.b*fromFixed/(255*255)
+Value.a*fromFixed/(255*255*255);
return input;
}