Search Unity

How do I use a .cube LUT file with Color Lookup?

Discussion in 'Universal Render Pipeline' started by OfficialHermie, Jun 14, 2020.

  1. OfficialHermie

    OfficialHermie

    Joined:
    Oct 12, 2012
    Posts:
    585
    Is there any way to have convert a .cube LUT file to a PNG that I can put into the slot of Color Lookup->Lookup Texture with URP?

    I would like to use Unity for that if possible.
     
  2. OfficialHermie

    OfficialHermie

    Joined:
    Oct 12, 2012
    Posts:
    585
    I find this forum useless as nobody replies to most of my questions.
    Not even a comment or "Currently not planned" or something like this.
     
  3. aivo

    aivo

    Joined:
    Feb 13, 2017
    Posts:
    30
    As I realized that I can import *.LUT files in HDRP but not in URP, I found the following way:

    I created a project with Unity HDRP. In the HDRP package I found the following file CubeLutImporter.cs which I just copied over to my URP project. There I changed the namespace to UnityEditor.Rendering.Universal and just commented out the two lines that cause errors (they are hdrp specific and thus not important for my general purpose lut importer).Now I have a regular LUT importer for *.cube files :)

    In case anybody is interested, I created the following method for my utils, which allows me to load *.cube files from anywhere in my filesystem (using Texture3D.SetPixels(Color[]) and Texture3D.Apply()):

    Code (CSharp):
    1.   static string FilterLine(string line)
    2.     {
    3.         var filtered = new StringBuilder();
    4.         line = line.TrimStart().TrimEnd();
    5.         int len = line.Length;
    6.         int o = 0;
    7.  
    8.         while (o < len)
    9.         {
    10.             char c = line[o];
    11.             if (c == '#') break; // Comment filtering
    12.             filtered.Append(c);
    13.             o++;
    14.         }
    15.  
    16.         return filtered.ToString();
    17.     }
    18.     public static bool ParseCubeData(string ctxPath, out int lutSize, out Color[] pixels)
    19.     {
    20.         Texture3D texture3D = new Texture3D(1, 1, 1, TextureFormat.RGBA32, false);
    21.         // Quick & dirty error utility
    22.         bool Error(string msg)
    23.         {
    24.             //ctx.LogImportError(msg);
    25.             Debug.LogWarning(msg);
    26.             return false;
    27.         }
    28.  
    29.         var lines = File.ReadAllLines(ctxPath);
    30.         pixels = null;
    31.         lutSize = -1;
    32.  
    33.         // Start parsing
    34.         int sizeCube = -1;
    35.         var table = new List<Color>();
    36.  
    37.         for (int i = 0; true; i++)
    38.         {
    39.             // EOF
    40.             if (i >= lines.Length)
    41.             {
    42.                 if (table.Count != sizeCube)
    43.                     return Error("Premature end of file");
    44.  
    45.                 break;
    46.             }
    47.  
    48.             // Cleanup & comment removal
    49.             var line = FilterLine(lines[i]);
    50.  
    51.             if (string.IsNullOrEmpty(line))
    52.                 continue;
    53.  
    54.             // Header data
    55.             if (line.StartsWith("TITLE"))
    56.                 continue; // Skip the title tag, we don't need it
    57.  
    58.             if (line.StartsWith("LUT_3D_SIZE"))
    59.             {
    60.                 var sizeStr = line.Substring(11).TrimStart();
    61.  
    62.                 if (!int.TryParse(sizeStr, out var size))
    63.                     return Error($"Invalid data on line {i}");
    64.  
    65.                 //if (size < GlobalPostProcessSettings.k_MinLutSize || size > GlobalPostProcessSettings.k_MaxLutSize)
    66.                 //    return Error("LUT size out of range");
    67.  
    68.                 lutSize = size;
    69.                 sizeCube = size * size * size;
    70.  
    71.                 continue;
    72.             }
    73.  
    74.             if (line.StartsWith("DOMAIN_"))
    75.                 continue; // Skip domain boundaries, haven't run into a single cube file that used them
    76.  
    77.             // Table
    78.             var row = line.Split();
    79.  
    80.             if (row.Length != 3)
    81.                 return Error($"Invalid data on line {i}");
    82.  
    83.             var color = Color.black;
    84.  
    85.             for (int j = 0; j < 3; j++)
    86.             {
    87.                 if (!float.TryParse(row[j], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out var d))
    88.                     return Error($"Invalid data on line {i}");
    89.  
    90.                 color[j] = d;
    91.             }
    92.  
    93.             table.Add(color);
    94.         }
    95.  
    96.         if (sizeCube != table.Count)
    97.             return Error($"Wrong table size - Expected {sizeCube} elements, got {table.Count}");
    98.  
    99.         pixels = table.ToArray();
    100.         return true;
    101.     }
     
    GoGoGadget, GreatestBear and mbussidk like this.
  4. GreatestBear

    GreatestBear

    Joined:
    Mar 6, 2018
    Posts:
    24
    Thank you, this was driving me nuts! The documentation on color grading and post processing in general in URP is a huge mess.

    URP docs don't even mention Color Grading as an available post processing feature, but it's listed as "recommended" for mobile platforms! There's a tutorial on how to export LUTs from photoshop that skims over the import process and when you try to follow that with URP, you get a .cube file that simply doesn't import. What on earth?!

    This saved me a bunch of time, thanks.
     
    jakeSAIC likes this.
  5. GeorgeAdamon

    GeorgeAdamon

    Joined:
    May 31, 2017
    Posts:
    48
    Thanks a lot for this @aivo.
    I try to use the generated 3D Textures in URP 13.1.8, but it seems that URP needs 2D LUTs instead of 3D, because I get this message: upload_2022-6-19_16-59-40.png

    Any thoughts?

    The 3D Texture looks fine btw.
     
  6. GeorgeAdamon

    GeorgeAdamon

    Joined:
    May 31, 2017
    Posts:
    48
    After a little bit of reading, I solved it simply by creating a Texture2D where
    width = size * size
    and
    height = size
    , and re-arranging the pixels a little bit. Essentially the Z-coordinate frames are stacked next to each other along the X axis.
    Here's my simple Scripted Importer:
    Code (CSharp):
    1.  
    2. using UnityEditor.AssetImporters;
    3. using UnityEngine;
    4.  
    5. [ScriptedImporter(0,".cube", AllowCaching = true)]
    6. public class LUTImporter : ScriptedImporter
    7. {
    8.     public Texture2D t2d;
    9.    
    10.     public override void OnImportAsset(AssetImportContext ctx)
    11.     {
    12.         if (LUTConverter.ParseCubeData(ctx.assetPath, out var size, out var colors))
    13.         {
    14.             var width      = size * size;
    15.             var colorCount = colors.Length;
    16.            
    17.             t2d = new Texture2D(width, size, TextureFormat.RGBAFloat, false);
    18.  
    19.             var tempColors = new Color[colorCount];
    20.            
    21.             for (var i = 0; i < colorCount; i++)
    22.             {
    23.                 // 1D to 3D Coords
    24.                 var X = i % size;
    25.                 var Y = (i / size) % size;
    26.                 var Z = i / (size * size);
    27.  
    28.                 // 2D Coords
    29.                 // Shifting X by the width of a full tile, everytime we move to a new Z slice
    30.                 var targetX = X + Z * size;
    31.  
    32.                 // 2D to 1D Coords
    33.                 var j = targetX + Y * width;
    34.  
    35.                 // Put the color in a new place in the 2D texture
    36.                 tempColors[j] = colors[i];
    37.             }
    38.            
    39.             t2d.SetPixels(tempColors);
    40.             t2d.Apply();
    41.            
    42.             ctx.AddObjectToAsset("lut", t2d);
    43.             ctx.SetMainObject(t2d);
    44.         }
    45.     }
    46. }
    47.  
     
    Last edited: Jun 19, 2022
    GoGoGadget likes this.
  7. GoGoGadget

    GoGoGadget

    Joined:
    Sep 23, 2013
    Posts:
    864
    And in case your .cube files are like mine, and require flipping, can just call this:

    Code (CSharp):
    1.     public Texture2D FlipTexture(Texture2D original)
    2.     {
    3.         Texture2D flipped = new Texture2D(original.width, original.height);
    4.  
    5.         int xN = original.width;
    6.         int yN = original.height;
    7.  
    8.  
    9.         for (int i = 0; i < xN; i++)
    10.         {
    11.             for (int j = 0; j < yN; j++)
    12.             {
    13.                 flipped.SetPixel(i, yN - j - 1, original.GetPixel(i, j));
    14.             }
    15.         }
    16.         flipped.Apply(true);
    17.  
    18.         return flipped;
    19.     }
     
  8. DwikyAnderson

    DwikyAnderson

    Joined:
    Apr 5, 2020
    Posts:
    4
    Im still having this issue
    Im still having issue with this

    upload_2022-6-29_16-20-30.png


    upload_2022-6-29_16-21-29.png

    here one of my *.cube file that imported into texture 2D

    is there another solution with this problem?

    future regards
     

    Attached Files: