Search Unity

Resolved Texture3D compression issue

Discussion in 'General Graphics' started by ignarmezh, Sep 8, 2020.

  1. ignarmezh

    ignarmezh

    Joined:
    Mar 23, 2017
    Posts:
    56
    Hi!

    I have an issue with Texture3D compression. Here's my code that is supposed to compress all of the individual slices as regular 2D textures and then to assemble them back into a 3D texture:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using Unity.Collections;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. public class CompressTexture : EditorWindow
    7. {
    8.     [MenuItem("Assets/CompressTexture")]
    9.     static void Compress()
    10.     {
    11.         if (!Selection.activeObject.name.Contains("Volume"))
    12.             return;
    13.         Texture3D activeContext = (Texture3D) Selection.activeObject;
    14.      
    15.         var dataTex = activeContext.GetPixels();
    16.      
    17.         int rx = activeContext.width;
    18.         int ry = activeContext.height;
    19.         int rz = activeContext.depth;
    20.      
    21.         List<Texture2D> textures2D = new List<Texture2D>();
    22.      
    23.         for (int z = 0; z < rz; z++)
    24.         {
    25.             Texture2D newTex = new Texture2D(rx, ry, TextureFormat.RGBAHalf, false);
    26.             var arrColors = new Color[rx * ry];
    27.             for (int y = 0; y < ry; y++)
    28.             {
    29.                 for (int x = 0; x < rx; x++)
    30.                 {
    31.                     int index = z * ry * rx + y * rx + x;
    32.                     arrColors[y * rx + x] = dataTex[index];
    33.                 }
    34.             }
    35.  
    36.             newTex.SetPixels(arrColors);
    37.             newTex.filterMode = FilterMode.Bilinear;
    38.             newTex.wrapMode = TextureWrapMode.Clamp;
    39.             newTex.Apply();
    40.             textures2D.Add(newTex);
    41.         }
    42.      
    43.         for (int i = 0; i < textures2D.Count; i++)
    44.         {
    45.             EditorUtility.CompressTexture(textures2D[i], TextureFormat.BC6H, TextureCompressionQuality.Normal);
    46.             textures2D[i].Apply();
    47.         }
    48.  
    49.         List<byte> dataResult = new List<byte>();
    50.         for (int z = 0; z < rz; z++)
    51.         {
    52.             var tex2DData = textures2D[z].GetRawTextureData<byte>();
    53.  
    54.             for (int i = 0; i < tex2DData.Length; i++)
    55.             {
    56.                 dataResult.Add(tex2DData[i]);
    57.             }
    58.         }
    59.      
    60.         var tex = new Texture3D(rx, ry, rz, TextureFormat.BC6H, false);
    61.         tex.SetPixelData(dataResult.ToArray(),0);
    62.         tex.filterMode = FilterMode.Bilinear;
    63.         tex.wrapMode = TextureWrapMode.Clamp;
    64.         tex.Apply(updateMipmaps:false);
    65.         AssetDatabase.CreateAsset(tex, string.Format(@"Assets/{0}.asset", Selection.activeObject.name));
    66.         Debug.Log("Succeeded");
    67.     }
    68. }
    If I don't use any compression, and just write into RGBAHalf, then I get a copy of my initial 3D texture, which kinda confirms that everything except for the compression works.
    If comment this:
    Code (CSharp):
    1. for (int i = 0; i < textures2D.Count; i++)
    2.         {
    3.             EditorUtility.CompressTexture(textures2D[i], TextureFormat.BC6H, TextureCompressionQuality.Normal);
    4.             textures2D[i].Apply();
    5.         }
    And change this:
    Code (CSharp):
    1. var tex = new Texture3D(rx, ry, rz, TextureFormat.RGBAHalf, false);
    The problem arises when I try to create a 3D texture from a set of compressed 2D textures. The error is "failed to create 3D texture". I get the same error even if I create a 3D texture from a single compressed 2D texture.

    Thanks!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    That error will be happening just by calling
    new Texture3D()
    with any compressed format, in part because OpenGL inexplicably doesn't support 3D textures using some compressed formats, and in part because Unity didn't update the code to know which formats OpenGL does support, as 3D textures were added prior to those newer compression formats. This was all kind of silly too since every other API (Direct3D, Vulkan, Metal, and any console specific APIs) all do support all compressed texture formats as 3D textures.

    As mentioned in the other thread on this, the Texture2DArray type is implemented on GPUs as a 3D texture. So on the hardware they're functionally the same, and any GPU that can support compressed texture arrays can and does support compressed 3D textures. It all comes down to an oversight in the OpenGL API spec itself not supporting it for a handful of formats, and at the time Unity implemented 3D textures it only supported those unsupported formats.

    AFAIK this is all fixed in 2020.1. If you're trying to use compressed 3D textures in any version of Unity 2019 or older, there isn't a way to get this to work.

    Also you might want to use
    CopyTexture()
    instead of
    SetPixelData()
    &
    GetRawTextureData()
    . Should create less garbage and be a little faster.
    https://docs.unity3d.com/ScriptReference/Graphics.CopyTexture.html
    The documentation for that function is a little confusing, but if both textures have read/write enabled, it does a CPU side copy as well as a GPU side copy.
     
  3. ignarmezh

    ignarmezh

    Joined:
    Mar 23, 2017
    Posts:
    56
    I'm working with Direct3D. Could you please explain how is it connected with OpenGL, because I'm a bit confused about that part?

    I'm testing this on 2020.1.3. Was it fixed in some later version?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Unity is at its core an OpenGL based engine. For a long time they didn’t support anything that OpenGL didn’t. Their Texture3D support dates back to this time period, so since OpenGL didn’t support compressed 3D textures, neither did Unity.

    That was nearly a decade ago now. It wasn’t fixed in large part because so few people tried (and fewer still reported it). 3D textures are now supposed to be a first class citizen though. If it’s still broken, file a bug report.

    Alternatively you can try setting up the texture as an atlas and importing it directly as a 3D texture.
     
    BrandyStarbrite likes this.
  5. ignarmezh

    ignarmezh

    Joined:
    Mar 23, 2017
    Posts:
    56
    Thanks!
    I have fixed the problem.
    It turned out that I used a texture with width/height/depth not being multiple of 4. Therefore it couldn't be compressed into BC6H format, it seems.
     
    bgolus likes this.
  6. topitsky

    topitsky

    Joined:
    Jan 12, 2016
    Posts:
    100
    Thanks! They should fix so that the error is given correctly, it just says that its an unsupported format
     
    ignarmezh likes this.