RenderTexture 3D to Texture3D

Is it possible to Convert a Rendertexture with depth to a Texture3D? or Store the Rendertexture as a Texture3D.

It should be possible, but it’ll take a bit of extra work.

Right now there’s no way to read back a specific layer of a 3D render texture to the CPU. I’ve not tried, but I suspect if you tried to use ReadPixels on a render texture with volume depth, you’ll always get the top slice. So instead you’ll need to create a 2D render texture with the same width, height and format as the 3D one and use Graphics.CopyTexture() to copy a single layer at a time out. You can then use tex.ReadPixels() to read that 2D render texture into a Texture2D. You can then use GetPixels/SetPixels or CopyTexture again to copy each slice from a Texture2D into a Texture3D.

Also as an aside, you said “a render texture with depth”. A render texture with depth isn’t a 3D render texture, it’s a render texture with a separate 2D depth buffer. To make a 3D render texture you need to create a render texture using a RenderTextureDescriptor with a TextureDimension.tex3D dimension and volumeDepth.

2 Likes

This worked for me.

1 Like

Fastest way for even large (multiple GB) textures:

void SaveRT3DToTexture3DAsset(RenderTexture rt3D, string pathWithoutAssetsAndExtension)
{
    int width = rt3D.width, height = rt3D.height, depth = rt3D.volumeDepth;
    var a = new NativeArray<byte>(width * height * depth, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); //change if format is not 8 bits (i was using R8_UNorm) (create a struct with 4 bytes etc)
    AsyncGPUReadback.RequestIntoNativeArray(ref a, rt3D, 0, (_) =>
    {
        Texture3D output = new Texture3D(width, height, depth, rt3D.graphicsFormat, TextureCreationFlags.None);
        output.SetPixelData(a, 0);
        output.Apply(updateMipmaps: false, makeNoLongerReadable: true);
        AssetDatabase.CreateAsset(output, $"Assets/{pathWithoutAssetsAndExtension}.asset");
        AssetDatabase.SaveAssetIfDirty(output);
        a.Dispose();
        rt3D.Release();
    });
}
2 Likes

Thanks for sharing!
I tried your code and got the following errors (occuring on line 5 in you previous message):

Size of source texture data (524288 bytes) is larger than the destination nativeArray (262144 bytes).
UnityEngine.Rendering.AsyncGPUReadback:RequestIntoNativeArray<byte> (Unity.Collections.NativeArray`1<byte>&,UnityEngine.Texture,int,System.Action`1<UnityEngine.Rendering.AsyncGPUReadbackRequest>)
ArgumentException: Texture3D.SetPixelData: size of data to be filled was larger than the size of data available in the source array. (Texture '')
UnityEngine.Texture3D.SetPixelData[T] (Unity.Collections.NativeArray`1[T] data, System.Int32 mipLevel, System.Int32 sourceDataStartIndex) (at <30adf90198bc4c4b83910c6fb1877998>:0)

Do you know how to fix this?

I guess this is case dependent, maybe it has something to do with the 8 bits format you are mentionning? I must admit this is too advanced code for me. Thanks for the help anyways!