Context: I'm working on a way to get
ugly GI for low end platform (target being Mali 400 mp GPU) through texture feedback and texture PVS. (work in progress).
Problem: In order to bypass the precision limit of 8bits textures (4 values with 256/64 rays), 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 (1024 values with 65536/64 rays). However the result don't work as intended, in my test case I have no colors (white light so far, colored comes later),
but the decoding back to RGB show red and green results instead of shades of gray. Y (luminance) seems to be decoded back just fine when used in isolation (before RGB), Co Cg are untouched when saved to texture.
(all textures data are R8G8B8A8)
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 caseHere is the raw Encoded data as RG as Y, and CoCg as BA
RG as 16bits luminance encoding split (yellow is r+g)

BA as Chroma CoCg encoding

Here is the code that encode to chromalum in fragment:
//chromaLum encoding
float3 chromalum = RgbToYCoCg(irradiance);
//divide luminance by number of rays to integrate over frame (accumulation)
chromalum.x /= numRays;
//turn the Y (luminance) into 16bits
float4 lum = Float32ToIntrgba(chromalum.x);
//encode split luminance (in RG) and chroma (BA)
float4 result = float4(lum.x,lum.y, chromalum.y,chromalum.z);
//return 16bit encoding to accumulation texture //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);
//decode GI (16bit Y -> chromalum -> RGB)
float luminance = IntrgbaToFloat32(float4(GI.xy,0,0)); //return luminance*16;
float3 lighting = YCoCgToRgb(float3(luminance,GI.z,GI.w));
GI = float4(lighting,1);
color += GI; //should mask albedo with light, not adding light, currently color is black, so add is a temp hack
return color;
Functions 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;
}