/// <summary> /// Concatenates multiple tables into one single texture 2D array /// </summary> /// <param name="_tablesFileNames"></param> /// <param name="_targetFileName"></param> /// <param name="_foramt"></param> static void ExportTexture(FileInfo[] _tablesFileNames, FileInfo _targetFileName, ImageUtility.PIXEL_FORMAT _format) { // Load tables LTC[][,] tables = new LTC[_tablesFileNames.Length][, ]; for (int i = 0; i < _tablesFileNames.Length; i++) { int validResultsCount; LTC[,] table = FitterForm.LoadTable(_tablesFileNames[i], out validResultsCount); if (validResultsCount != table.Length) { throw new Exception("Not all table results are valid!"); } tables[i] = table; if (i != 0 && (table.GetLength(0) != tables[0].GetLength(0) || table.GetLength(1) != tables[0].GetLength(1))) { throw new Exception("Table dimensions mismatch!"); } } // Create the Texture2DArray uint W = (uint)tables[0].GetLength(0); uint H = (uint)tables[0].GetLength(1); ImageUtility.ImagesMatrix M = new ImageUtility.ImagesMatrix(); M.InitTexture2DArray(W, H, (uint)tables.Length, 1); M.AllocateImageFiles(_format, new ImageUtility.ColorProfile(ImageUtility.ColorProfile.STANDARD_PROFILE.LINEAR)); for (int i = 0; i < tables.Length; i++) { LTC[,] table = tables[i]; // ImageUtility.ImageFile I = new ImageUtility.ImageFile( W, H, _format, profile ); // M[(uint) i][0][0] = I; double largest = 0; ImageUtility.ImageFile I = M[(uint)i][0][0]; I.WritePixels((uint _X, uint _Y, ref float4 _color) => { LTC ltc = table[_X, _Y]; const double tol = 1e-6; // if ( Mathf.Abs( ltc.invM[2,2] - 1 ) > tol ) // throw new Exception( "Not one!" ); if (Mathf.Abs(ltc.invM[0, 1]) > tol || Mathf.Abs(ltc.invM[1, 0]) > tol || Mathf.Abs(ltc.invM[1, 2]) > tol || Mathf.Abs(ltc.invM[2, 1]) > tol) { throw new Exception("Not zero!"); } largest = Math.Max(largest, Math.Abs(ltc.invM[2, 2] - 1)); double factor = 1.0 / ltc.invM[2, 2]; _color.x = (float)(factor * ltc.invM[0, 0]); _color.y = (float)(factor * ltc.invM[0, 2]); _color.z = (float)(factor * ltc.invM[1, 1]); _color.w = (float)(factor * ltc.invM[2, 0]); }); } M.DDSSaveFile(_targetFileName, ImageUtility.COMPONENT_FORMAT.AUTO); }
void Plot(int _roughnessIndex, int _row, int _column, ImageFile _image, PanelOutput _panel, Label _label) { _image.Clear(float4.One); // Determine range first int W = m_results.GetLength(0); float[] values = new float[W]; float min = float.MaxValue; float max = -float.MaxValue; for (int X = 0; X < W; X++) { LTC ltc = m_results[_roughnessIndex, X]; double term = ltc.invM[_row, _column]; term /= ltc.invM[1, 1]; // Normalize by central term values[X] = (float)term; min = Math.Min(min, values[X]); max = Math.Max(max, values[X]); } float2 rangeY = float2.UnitY; rangeY.x = Math.Min(rangeY.x, min); rangeY.y = Math.Max(rangeY.y, max); // Plot graph and update UI float2 rangeX = new float2(0, 1); _image.PlotGraph(new float4(0, 0, 0, 1), rangeX, rangeY, ( float _X ) => { int X = Math.Min(W - 1, (int)(W * _X)); return(values[X]); }); _image.PlotAxes(float4.UnitW, rangeX, rangeY, 0.1f, 0.1f * (rangeY.y - rangeY.x)); _label.Text = "Coefficient m" + _row + "" + _column + " - Range [" + rangeY.x + ", " + rangeY.y + "]"; _panel.m_bitmap = _image.AsBitmap; _panel.Refresh(); }
static void Export(FileInfo _tableFileName, FileInfo _targetFileName, string _BRDFName) { int validResultsCount; LTC[,] table = FitterForm.LoadTable(_tableFileName, out validResultsCount); string sourceCode = ""; // Export LTC matrices int tableSize = table.GetLength(0); LTC defaultLTC = new LTC(); defaultLTC.magnitude = 0.0; #if EXPORT_FOR_UNITY string tableName = "s_LtcMatrixData_" + _BRDFName; sourceCode += "using UnityEngine;\r\n" + "using System;\r\n" + "\r\n" + "namespace UnityEngine.Experimental.Rendering.HDPipeline\r\n" + "{\r\n" + " public partial class LTCAreaLight\r\n" + " {\r\n" + " // Table contains 3x3 matrix coefficients of M^-1 for the fitting of the " + _BRDFName + " BRDF using the LTC technique\r\n" + " // From \"Real-Time Polygonal-Light Shading with Linearly Transformed Cosines\" 2016 (https://eheitzresearch.wordpress.com/415-2/)\r\n" + " //\r\n" + " // The table is accessed via LTCAreaLight." + tableName + "[<roughnessIndex> + 64 * <thetaIndex>] // Theta values are along the Y axis, Roughness values are along the X axis\r\n" + " // • roughness = ( <roughnessIndex> / " + (tableSize - 1) + " )^2\r\n" + " // • cosTheta = 1 - ( <thetaIndex> / " + (tableSize - 1) + " )^2\r\n" + " //\r\n" + " public static double[,] "+ tableName + " = new double[k_LtcLUTResolution * k_LtcLUTResolution, k_LtcLUTMatrixDim * k_LtcLUTMatrixDim] {\r\n"; for (int thetaIndex = 0; thetaIndex < tableSize; thetaIndex++) { string matrixRowString = " "; for (int roughnessIndex = 0; roughnessIndex < tableSize; roughnessIndex++) { LTC ltc = table[roughnessIndex, thetaIndex]; if (ltc == null) { ltc = defaultLTC; } string matrixString = ltc.invM[0, 0] + ", " + ltc.invM[0, 1] + ", " + ltc.invM[0, 2] + ", "; matrixString += ltc.invM[1, 0] + ", " + ltc.invM[1, 1] + ", " + ltc.invM[1, 2] + ", "; matrixString += ltc.invM[2, 0] + ", " + ltc.invM[2, 1] + ", " + ltc.invM[2, 2]; // string matrixString = ltc.M[0,0] + ", " + ltc.M[0,1] + ", " + ltc.M[0,2] + ", "; // matrixString += ltc.M[1,0] + ", " + ltc.M[1,1] + ", " + ltc.M[1,2] + ", "; // matrixString += ltc.M[2,0] + ", " + ltc.M[2,1] + ", " + ltc.M[2,2]; matrixRowString += "{ " + matrixString + " }, "; } // Compute theta float y = (float)thetaIndex / (tableSize - 1); float cosTheta = 1 - y * y; matrixRowString += " // Cos(theta) = " + cosTheta + "\r\n"; sourceCode += matrixRowString; } #else string className = "LTCData_" + _BRDFName; sourceCode += "using System;\r\n" + "\r\n" + "namespace LTCAreaLight\r\n" + "{\r\n" + " public partial class " + className + "\r\n" + " {\r\n" + " // Table contains 3x3 matrix coefficients of M^-1 for the fitting of the " + _BRDFName + " BRDF using the LTC technique\r\n" + " // From \"Real-Time Polygonal-Light Shading with Linearly Transformed Cosines\" 2016 (https://eheitzresearch.wordpress.com/415-2/)\r\n" + " //\r\n" + " // The table is accessed via LTCAreaLight." + className + "[64 * <roughnessIndex> + <thetaIndex>] // Theta values are on X axis, Roughness values are on Y axis\r\n" + " // • roughness = ( <roughnessIndex> / " + (tableSize - 1) + " )^2\r\n" + " // • cosTheta = 1 - ( <thetaIndex> / " + (tableSize - 1) + " )^2\r\n" + " //\r\n" + " public static double[,] s_invM = new double["+ tableSize + " * " + tableSize + ", 3 * 3] {\r\n"; for (int roughnessIndex = 0; roughnessIndex < tableSize; roughnessIndex++) { // string matrixRowString = " { "; string matrixRowString = " "; for (int thetaIndex = 0; thetaIndex < tableSize; thetaIndex++) { LTC ltc = table[roughnessIndex, thetaIndex]; if (ltc == null) { ltc = defaultLTC; } string matrixString = ltc.invM[0, 0] + ", " + ltc.invM[0, 1] + ", " + ltc.invM[0, 2] + ", "; matrixString += ltc.invM[1, 0] + ", " + ltc.invM[1, 1] + ", " + ltc.invM[1, 2] + ", "; matrixString += ltc.invM[2, 0] + ", " + ltc.invM[2, 1] + ", " + ltc.invM[2, 2]; // matrixRowString += (thetaIndex == 0 ? "{ " : ", { ") + matrixString + " }"; matrixRowString += "{ " + matrixString + " }, "; } // // Compute roughness // float perceptualRoughness = (float) roughnessIndex / (tableSize-1); // float alpha = perceptualRoughness * perceptualRoughness; // // // matrixRowString += " },\r\n"; // matrixRowString += " // Roughness = " + alpha + "\r\n"; throw new Exception("Ta mère!"); sourceCode += matrixRowString; } #endif sourceCode += " };\r\n"; // Export LTC amplitude and fresnel // // public static float[] s_LtcGGXMagnitudeData = new float[k_LtcLUTResolution * k_LtcLUTResolution] // public static float[] s_LtcGGXFresnelData = new float[k_LtcLUTResolution * k_LtcLUTResolution] sourceCode += "\r\n"; sourceCode += " // NOTE: Formerly, we needed to also export and create a table for the BRDF's amplitude factor + fresnel coefficient\r\n"; sourceCode += " // but it turns out these 2 factors are actually already precomputed and available in the FGD table corresponding\r\n"; sourceCode += " // to the " + _BRDFName + " BRDF, therefore they are no longer exported...\r\n"; // Close class and namespace sourceCode += " }\r\n"; sourceCode += "}\r\n"; // Write content // FileInfo targetFileName = new FileInfo( Path.Combine( Path.GetDirectoryName( _tableFileName.FullName ), Path.GetFileNameWithoutExtension( _tableFileName.FullName ) + ".cs" ) ); using (StreamWriter W = _targetFileName.CreateText()) W.Write(sourceCode); }