I don't see much much discussing about the uses of floating point textures in forums. I use them, for example, in shaders to do some computations (almost like they're a kernel) and then pass the result to a surface shader to obtain some specific vertex deformations, coloring and other vfx.
This time, though, I need to fetch those values from GPU side to CPU side so I can process a float[] array containing the flattened float (result) values held by the float texture (just after calling Graphics.Blit
that calls a shader that fills the floating point texture).
How can this be achieved?
Side note: the only dev that I saw using this method so far is Keijiro Takahashi, for example in his Kvant Wall; if you have other sources I'd be grateful if you let me know.
I do know there are compute shaders and OpenCL and CUDA, however this is the method I need now.
The question in short: how do I fetch the values contained in a floating point texture into a C# float[]
array?
So I came up with this solution.
float[] DecodeFloatTexture()
{
Texture2D decTex = new Texture2D(resultBuffer.width, resultBuffer.height, TextureFormat.RGBAFloat, false);
RenderTexture.active = resultBuffer;
decTex.ReadPixels(new Rect(0, 0, resultBuffer.width, resultBuffer.height), 0, 0);
decTex.Apply();
RenderTexture.active = null;
Color[] colors = decTex.GetPixels();
// HERE YOU CAN GET ALL 4 FLOATS OUT OR JUST THOSE YOU NEED.
// IN MY CASE ALL 4 VALUES HAVE A MEANING SO I'M GETTING THEM ALL.
float[] results = new float[colors.Length*4];
for(int i=0; i<colors.Length; i++)
{
results[i * 4] = colors[i].r;
results[i * 4 + 1] = colors[i].g;
results[i * 4 + 2] = colors[i].b;
results[i * 4 + 3] = colors[i].a;
}
return results;
}
Alternatively, if what we need is not a float, GetRawTextureData can be used to then convert the bytes to the new type with System.BitConverter which gives some flexibility on the data you are passing from the shader (for example if your fragment shader is outputting half4). If you need float though the first method is better.
float[] DecodeFloatTexture()
{
Texture2D decTex = new Texture2D(resultBuffer.width, resultBuffer.height, TextureFormat.RGBAFloat, false);
RenderTexture.active = resultBuffer;
decTex.ReadPixels(new Rect(0, 0, resultBuffer.width, resultBuffer.height), 0, 0);
decTex.Apply();
RenderTexture.active = null;
byte[] bytes = decTex.GetRawTextureData();
float[] results = new float[resultBuffer.width * resultBuffer.height];
for (int i = 0; i < results.Length; i++)
{
int byteIndex = i * 4;
byte[] localBytes = new byte[] { bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3] }; // converts 4 bytes to a float
results[i] = System.BitConverter.ToSingle(localBytes, 0);
}
return results;
}