前言 :
解 3D LUT Cube 檔案
執行結果 :
偷吃步作法 :
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class Run : MonoBehaviour
{
public Transform par;
public Transform obj;
void Start()
{
Cube cube = new Cube();
float[,,,] cube_color64 = cube.GetCube("C:/A/My.cube");
print("cube.size : " + cube.size);
float[,,,] cube_color256 = new float[256, 256, 256, 3];
int oSize = cube.size - 1;
int targetSize = 256;
// 預設是 += 8, 所以有空格,如果你用 ++,小心爆炸 !
for (int z = 0; z < targetSize; z += 8)
{
for (int y = 0; y < targetSize; y += 8)
{
for (int x = 0; x < targetSize; x += 8)
{
float targetX = x * (oSize / (float)targetSize);
float targetY = y * (oSize / (float)targetSize);
float targetZ = z * (oSize / (float)targetSize);
int x0 = (int)targetX;
int y0 = (int)targetY;
int z0 = (int)targetZ;
int x1 = x0 + 1;
int y1 = y0 + 1;
int z1 = z0 + 1;
float X = targetX - x1;
float Y = targetY - y1;
float Z = targetZ - z1;
float w1 = (1 - X) * (1 - Y) * (1 - Z);
float w2 = X * (1 - Y) * (1 - Z);
float w3 = (1 - X) * Y * (1 - Z);
float w4 = (1 - X) * (1 - Y) * Z;
float w5 = X * (1 - Y) * Z;
float w6 = (1 - X) * Y * Z;
float w7 = X * Y * (1 - Z);
float w8 = X * Y * Z;
Color c000 = new Color(cube_color64[x0, y0, z0, 0], cube_color64[x0, y0, z0, 1], cube_color64[x0, y0, z0, 2]);
Color c100 = new Color(cube_color64[x1, y0, z0, 0], cube_color64[x1, y0, z0, 1], cube_color64[x1, y0, z0, 2]);
Color c010 = new Color(cube_color64[x0, y1, z0, 0], cube_color64[x0, y1, z0, 1], cube_color64[x0, y1, z0, 2]);
Color c001 = new Color(cube_color64[x0, y0, z1, 0], cube_color64[x0, y0, z1, 1], cube_color64[x0, y0, z1, 2]);
Color c101 = new Color(cube_color64[x1, y0, z1, 0], cube_color64[x1, y0, z1, 1], cube_color64[x1, y0, z1, 2]);
Color c011 = new Color(cube_color64[x0, y1, z1, 0], cube_color64[x0, y1, z1, 1], cube_color64[x0, y1, z1, 2]);
Color c110 = new Color(cube_color64[x1, y1, z0, 0], cube_color64[x1, y1, z0, 1], cube_color64[x1, y1, z0, 2]);
Color c111 = new Color(cube_color64[x1, y1, z1, 0], cube_color64[x1, y1, z1, 1], cube_color64[x1, y1, z1, 2]);
Transform t = Instantiate<Transform>(obj);
t.position = new Vector3(x, y, z);
t.GetComponent<Renderer>().material.color = new Color(
c000.r * w1 + c100.r * w2 + c010.r * w3 + c001.r * w4 + c101.r * w5 + c011.r * w6 + c110.r * w7 + c111.r * w8,
c000.g * w1 + c100.g * w2 + c010.g * w3 + c001.g * w4 + c101.g * w5 + c011.g * w6 + c110.g * w7 + c111.g * w8,
c000.b * w1 + c100.b * w2 + c010.b * w3 + c001.b * w4 + c101.b * w5 + c011.b * w6 + c110.b * w7 + c111.b * w8
);
}
}
}
}
bool isShowCube = false;
void ShowCube(float[,,,] cube_color)
{
isShowCube = true;
for (int z = 0; z < cube_color.GetLength(2); z += 2)
{
for (int y = 0; y < cube_color.GetLength(1); y += 2)
{
for (int x = 0; x < cube_color.GetLength(0); x += 2)
{
float R = cube_color[x, y, z, 0];
float G = cube_color[x, y, z, 1];
float B = cube_color[x, y, z, 2];
Transform t = Instantiate<Transform>(obj);
t.parent = par;
t.localPosition = new Vector3(R, G, B) * 48;
t.GetComponent<Renderer>().material.color = new Color(R, G, B);
}
}
}
}
}
好一點的作法 :
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class LUT3D : MonoBehaviour
{
public Transform par;
public Transform obj;
public Texture2D inpurtTexture;
public RawImage outputImage;
void Start()
{
//float[,,,] table = LoadTable("C:/A/My.cube");
float[,,,] table = LoadTable("C:/A/Fuji.cube");
//float[,,,] table = LoadTable("C:/A/FLog_FGamut_to_FLog_BT.709_33grid_V.1.00.cube");
//ShowCube(LoadTable("C:/A/Fuji.cube"), 8);
//ShowCube(new Cube().GetCube("C:/A/Fuji.cube"), 1);
//ShowCube(LoadTable("C:/A/My.cube"), 8);
//ShowCube(new Cube().GetCube("C:/A/My.cube"), 2);
//ShowCube(LoadTable("C:/A/FLog_FGamut_to_FLog_BT.709_33grid_V.1.00.cube"), 8);
//ShowCube(new Cube().GetCube("C:/A/FLog_FGamut_to_FLog_BT.709_33grid_V.1.00.cube"), 1);
Texture2D t = new Texture2D(inpurtTexture.width, inpurtTexture.height, TextureFormat.RGBAFloat, false);
for (int y = 0; y < t.height; y++)
{
for (int x = 0; x < t.width; x++)
{
Color c = inpurtTexture.GetPixel(x, y);
int r = Mathf.CeilToInt(c.r * 255);
int g = Mathf.CeilToInt(c.g * 255);
int b = Mathf.CeilToInt(c.b * 255);
float R = table[r, g, b, 0];
float G = table[r, g, b, 1];
float B = table[r, g, b, 2];
t.SetPixel(x, y, new Color(R, G, B));
}
}
t.Apply();
outputImage.texture = t;
}
float[,,,] LoadTable(string path)
{
Cube cube = new Cube();
float[,,,] cube_color64 = cube.GetCube(path);
print("cube.size : " + cube.size);
float[,,,] cube_color256 = new float[256, 256, 256, 3];
int oSize = cube.size;
int targetSize = 256;
int scale = targetSize / oSize;
print("scale : " + scale);
for (int z = 0; z < targetSize - scale; z++)
{
for (int y = 0; y < targetSize - scale; y++)
{
for (int x = 0; x < targetSize - scale; x++)
{
float targetX = x * (oSize / (float)targetSize);
float targetY = y * (oSize / (float)targetSize);
float targetZ = z * (oSize / (float)targetSize);
int x0 = (int)targetX; // 0~63
int y0 = (int)targetY;
int z0 = (int)targetZ;
int x1 = x0 + 1; // 1~64
int y1 = y0 + 1;
int z1 = z0 + 1;
float X = targetX - x0;
float Y = targetY - y0;
float Z = targetZ - z0;
Color c000 = new Color(cube_color64[x0, y0, z0, 0], cube_color64[x0, y0, z0, 1], cube_color64[x0, y0, z0, 2]);
Color c100 = new Color(cube_color64[x1, y0, z0, 0], cube_color64[x1, y0, z0, 1], cube_color64[x1, y0, z0, 2]);
Color c010 = new Color(cube_color64[x0, y0, z1, 0], cube_color64[x0, y0, z1, 1], cube_color64[x0, y0, z1, 2]);
Color c001 = new Color(cube_color64[x0, y1, z0, 0], cube_color64[x0, y1, z0, 1], cube_color64[x0, y1, z0, 2]);
Color c101 = new Color(cube_color64[x1, y1, z0, 0], cube_color64[x1, y1, z0, 1], cube_color64[x1, y1, z0, 2]);
Color c011 = new Color(cube_color64[x0, y1, z1, 0], cube_color64[x0, y1, z1, 1], cube_color64[x0, y1, z1, 2]);
Color c110 = new Color(cube_color64[x1, y0, z1, 0], cube_color64[x1, y0, z1, 1], cube_color64[x1, y0, z1, 2]);
Color c111 = new Color(cube_color64[x1, y1, z1, 0], cube_color64[x1, y1, z1, 1], cube_color64[x1, y1, z1, 2]);
Color c00 = new Color(Mathf.Lerp(c000.r, c100.r, X), Mathf.Lerp(c000.g, c100.g, X), Mathf.Lerp(c000.b, c100.b, X));
Color c01 = new Color(Mathf.Lerp(c001.r, c101.r, X), Mathf.Lerp(c001.g, c101.g, X), Mathf.Lerp(c001.b, c101.b, X));
Color c10 = new Color(Mathf.Lerp(c010.r, c110.r, X), Mathf.Lerp(c010.g, c110.g, X), Mathf.Lerp(c010.b, c110.b, X));
Color c11 = new Color(Mathf.Lerp(c011.r, c111.r, X), Mathf.Lerp(c011.g, c111.g, X), Mathf.Lerp(c011.b, c111.b, X));
Color c0 = new Color(Mathf.Lerp(c00.r, c10.r, Z), Mathf.Lerp(c00.g, c10.g, Z), Mathf.Lerp(c00.b, c10.b, Z));
Color c1 = new Color(Mathf.Lerp(c01.r, c11.r, Z), Mathf.Lerp(c01.g, c11.g, Z), Mathf.Lerp(c01.b, c11.b, Z));
Color c = new Color(Mathf.Lerp(c0.r, c1.r, Y), Mathf.Lerp(c0.g, c1.g, Y), Mathf.Lerp(c0.b, c1.b, Y));
cube_color256[x, y, z, 0] = c.r;
cube_color256[x, y, z, 1] = c.g;
cube_color256[x, y, z, 2] = c.b;
//print(Mathf.CeilToInt(c.r * 255));
// 最後一個
int end = targetSize - scale - 1;
if (x == end || y == end || z == end)
{
for (int k = 1; k < scale + 1; k++)
{
// 這邊也要做插值,目前沒做
float r = cube_color64[x1, y1, z1, 0];
float g = cube_color64[x1, y1, z1, 1];
float b = cube_color64[x1, y1, z1, 2];
cube_color256[x + k, y + k, z + k, 0] = r;
cube_color256[x + k, y + k, z + k, 1] = g;
cube_color256[x + k, y + k, z + k, 2] = b;
}
}
}
}
}
return cube_color256;
}
void ShowCube(float[,,,] cube_color, int step)
{
for (int z = 0; z < cube_color.GetLength(2); z += step)
{
for (int y = 0; y < cube_color.GetLength(1); y += step)
{
for (int x = 0; x < cube_color.GetLength(0); x += step)
{
float R = cube_color[x, y, z, 0];
float G = cube_color[x, y, z, 1];
float B = cube_color[x, y, z, 2];
Transform t = Instantiate<Transform>(obj);
t.parent = par;
t.localPosition = new Vector3(R, G, B) * 48;
t.GetComponent<Renderer>().material.color = new Color(R, G, B);
//print((int)(R * 255));
}
}
}
}
}
Cube.cs :
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class Cube
{
public string title;
public int size;
public float[,,,] GetCube(string path)
{
string[] lines = File.ReadAllLines(path);
int index = 0;
//index = GetTitle(lines, index, out title);
//print("[ " + index + " ] Title : " + title);
index = GetLut3DSize(lines, index, out size);
//print("[ " + index + " ] LUT 3D Size : " + lut3DSize);
float[,,,] cube = new float[size, size, size, 3]; // X, Y, Z, RGB
int allSize = size * size * size;
int x = 0, y = 0, z = 0;
for (int i = index, k = 0; k < allSize; i++)
{
string line = lines[i];
if (CanRead(line) == false) continue;
string[] s = line.Split(' ');
float R = float.Parse(s[0]);
float G = float.Parse(s[1]);
float B = float.Parse(s[2]);
x = k % size;
cube[x, y, z, 0] = R;
cube[x, y, z, 1] = G;
cube[x, y, z, 2] = B;
if (x == size-1)
{
if (y == size-1)
{
y = 0;
z++;
}
else
{
y++;
}
}
k++;
}
return cube;
}
bool CanRead(string line)
{
if (line.Length == 0) return false;
if (line[0] == '#') return false;
return true;
}
// 讀完後,指針往後一格
int GetTitle(string[] lines, int index, out string title)
{
title = null;
for (int i = index; i < lines.Length; i++)
{
string s = lines[i];
if (s.IndexOf("TITLE") != -1)
{
int start = s.IndexOf("\"") + 1;
int end = s.LastIndexOf("\"");
int length = end - start;
title = s.Substring(start, length);
index = i + 1;
break;
}
}
return index;
}
// 讀完後,指針往後一格
int GetLut3DSize(string[] lines, int index, out int size)
{
size = -1;
for (int k = index; k < lines.Length; k++)
{
string s = lines[k];
if (s.IndexOf("LUT_3D_SIZE") != -1)
{
string[] ss = s.Split(' ');
for (int i = 1; i < ss.Length; i++)
{
if (int.TryParse(ss[i], out size))
{
index = k + 1;
break;
}
}
break;
}
}
return index;
}
}
參考 :
https://www.lightillusion.com/luts.html
http://www.fallenempiredigital.com/blog/2012/12/04/luts-part-1-what-is-a-lut/
https://en.wikipedia.org/wiki/3D_lookup_table