├── GaussianTex ├── Editor.meta ├── Editor │ ├── Scripts.meta │ ├── Scripts │ │ ├── ColorspaceObj.cs │ │ ├── ColorspaceObj.cs.meta │ │ ├── EigenDecomposition.cs │ │ ├── EigenDecomposition.cs.meta │ │ ├── GaussTexUI.cs │ │ ├── GaussTexUI.cs.meta │ │ ├── Matrix3x3.cs │ │ ├── Matrix3x3.cs.meta │ │ ├── TexToGaussian.cs │ │ └── TexToGaussian.cs.meta │ ├── Shaders.meta │ └── Shaders │ │ ├── BitonicMergeSort.compute │ │ ├── BitonicMergeSort.compute.meta │ │ ├── SortedTextureToGaussian.compute │ │ ├── SortedTextureToGaussian.compute.meta │ │ ├── TexturePreprocessor.compute │ │ └── TexturePreprocessor.compute.meta ├── demo.meta ├── demo │ ├── materials.meta │ ├── materials │ │ ├── Blending Comparison Demo.mat │ │ └── Blending Comparison Demo.mat.meta │ ├── textures.meta │ └── textures │ │ ├── mossdemo.jpg │ │ ├── mossdemo.jpg.meta │ │ ├── mossdemo_colorspace.asset │ │ ├── mossdemo_colorspace.asset.meta │ │ ├── mossdemo_gauss.jpg │ │ ├── mossdemo_gauss.jpg.meta │ │ ├── mossdemo_lut.asset │ │ └── mossdemo_lut.asset.meta ├── shaders.meta └── shaders │ ├── Demo.meta │ ├── Demo │ ├── BlendDemo.shader │ └── BlendDemo.shader.meta │ ├── GaussianBlend.cginc │ ├── GaussianBlend.cginc.meta │ ├── PBRExample.meta │ └── PBRExample │ ├── RTStandardCG.cginc │ ├── RTStandardCG.cginc.meta │ ├── RTStandardCommon.cginc │ ├── RTStandardCommon.cginc.meta │ ├── RTStandardMeta.cginc │ ├── RTStandardMeta.cginc.meta │ ├── RTStandardOpaque.shader │ └── RTStandardOpaque.shader.meta ├── LICENSE └── README.md /GaussianTex/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 89ced1465bce3c74e91c49b22ce3a910 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dce5e50916232d94e8dad5cc1961e352 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/ColorspaceObj.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace GaussianTexture 6 | { 7 | public class ColorspaceObj : ScriptableObject 8 | { 9 | public Vector4 Axis0; 10 | public Vector4 Axis1; 11 | public Vector4 Axis2; 12 | public Vector4 Center; 13 | } 14 | } -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/ColorspaceObj.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e273ecb3e5512c84898715e24802c7c6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/EigenDecomposition.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Error-mdl 3 | * Created: 2021-10-25 4 | * 5 | * An implementation of an iterative eigen solver for symmetric real 3x3 matricies 6 | * taken from "A Robust Eigensolver for 3x3 Symmetric Matrices" by David Eberly 7 | * https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf 8 | * 9 | * Made for computing the Eigenvectors of the covarience matrix of the RGB channels 10 | * of an image, in order to generate gaussian-transformed textures that compress well 11 | * with DXT compression as described in "Procedural Stochastic Textures by Tiling and 12 | * Blending" by Thomas Deliot and Eric Heitz 13 | * 14 | * (c) 2021 Error-mdl 15 | * This code is licensed under the BSD-3 Clause license, see LICENSE.md for more details 16 | */ 17 | 18 | using System.Collections; 19 | 20 | using System.Collections.Generic; 21 | using UnityEngine; 22 | using MatrixPlus; 23 | /// 24 | /// Calculates the Eigen-values and -vectors of real, symmetric 3x3 matricies. 25 | /// Based on the iterative method laid out in 26 | /// 27 | public class EigenDecomposition 28 | { 29 | 30 | /// 31 | /// Calculates a vector (sin X, cos X) such that the dot product of a given vector (-v, u) with (sin X, cos X) is 0. 32 | /// See equation 3 in 33 | /// 34 | /// First component of the input vector 35 | /// Second component of the input vector 36 | /// Variable in which to output the calculated value of sin X 37 | /// Variable in which to output the calculated value of cos X 38 | private static void ParallelSinCos(float u, float v, ref float cos0, ref float sin0) 39 | { 40 | float inputLen = Mathf.Sqrt((u * u) + (v * v)); 41 | if (inputLen > 0) 42 | { 43 | cos0 = u / inputLen; 44 | sin0 = v / inputLen; 45 | if (cos0 > 0) 46 | { 47 | cos0 = -cos0; 48 | sin0 = -sin0; 49 | } 50 | } 51 | else 52 | { 53 | sin0 = 0; 54 | cos0 = -1; 55 | } 56 | } 57 | 58 | /// 59 | /// Calculates the exponent portion of a float, similar to C's frexp. THIS DOESN"T SEEM TO QUITE WORK RIGHT 60 | /// Mostly taken from a stackoverflow post: 61 | /// 62 | /// Float from which the exponent is extracted from 63 | /// Exponent of input float 64 | private static int float_exp(float input) 65 | { 66 | if (System.Single.IsNaN(input) || System.Single.IsInfinity(input)) 67 | { 68 | return 0; 69 | } 70 | 71 | byte[] inputBytes = System.BitConverter.GetBytes(input); 72 | int inputInt = System.BitConverter.ToInt32(inputBytes, 0); 73 | int exponent = (inputInt >> 23) & 0x000000ff; 74 | int mantissa = inputInt & 0x007fffff; 75 | 76 | if (exponent == 0) 77 | { 78 | exponent = 1; 79 | } 80 | else 81 | { 82 | mantissa = mantissa | (1 << 23); 83 | } 84 | exponent -= 127 + 23; 85 | 86 | if (mantissa == 0) 87 | { 88 | return exponent; 89 | } 90 | 91 | while ((mantissa & 1) == 0) 92 | { 93 | mantissa >>= 1; 94 | exponent++; 95 | } 96 | 97 | return exponent; 98 | } 99 | 100 | /// 101 | /// Checks if a given element in a matrix is approximately 0 compared with the two closest diagonal elements of the matrix. 102 | /// Copied from listing 1 in 103 | /// 104 | /// First closest diagonal element 105 | /// Second closest diagonal element 106 | /// Non-diagonal element of a matrix 107 | /// 108 | private static bool isRelativeZero(float diagonal0, float diagonal1, float value) 109 | { 110 | float magnitude = Mathf.Abs(diagonal0) + Mathf.Abs(diagonal1); 111 | return (magnitude + value) == magnitude; 112 | } 113 | 114 | /// 115 | /// Gets the Eigenvectors and values of a given symmetric 3x3 matrix. 116 | /// 117 | /// Input 3x3 matrix, assumed to be symmetric. 118 | /// Vector3 into which the eigenvalues are output 119 | /// A 3x3 matrix composed of the 3 eigenvectors as its columns 120 | public static Matrix3x3f Get3x3SymmetricEigen(Matrix3x3f mat, ref Vector3 eigValues) 121 | { 122 | float c0 = 0, s0 = 0; 123 | ParallelSinCos(mat.m12, -mat.m02, ref c0, ref s0); 124 | //Debug.Log(string.Format("b12 first cos-sin product:({0}, {1}) * ({2}, {3}) = {4}",c0, s0, mat.m02, mat.m12, c0 * mat.m02 + s0 * mat.m12)); 125 | float ca00sa01 = c0 * mat.m00 + s0 * mat.m01; 126 | float ca01sa11 = c0 * mat.m01 + s0 * mat.m11; 127 | float sa00ca01 = s0 * mat.m00 - c0 * mat.m01; 128 | float sa01ca11 = s0 * mat.m01 - c0 * mat.m11; 129 | float b00 = c0 * ca00sa01 + s0 * ca01sa11; 130 | float b11 = s0 * sa00ca01 - c0 * sa01ca11; 131 | float b22 = mat.m22; 132 | float b01 = s0 * ca00sa01 - c0 * ca01sa11; 133 | float b12 = s0 * mat.m02 - c0 * mat.m12; 134 | //float b02 = 0f; 135 | //Debug.Log(string.Format("b00: {0}\tb11: {1}\tb22: {2}\nb01: {3}\tb12: {4}", b00, b11, b22, b01, b12)); 136 | Matrix3x3f B = new Matrix3x3f( 137 | b00, b01, 0f, 138 | b01, b11, b12, 139 | 0f, b12, b22 140 | ); 141 | 142 | Matrix3x3f Q = new Matrix3x3f( 143 | c0, s0, 0, 144 | s0, -c0, 0, 145 | 0, 0, 1 146 | ); 147 | 148 | int alpha = 24 + 125; //24 digits in float32 mantissa, minus the minimum exponent of -125 149 | 150 | if (Mathf.Abs(b12) < Mathf.Abs(b01)) 151 | { 152 | int power = 127;// since float_exp(b12) doesn't seem to be outputting the correct values, just assume the max exponent; 153 | //Debug.Log("Power: " + power.ToString()); 154 | int maxIterations = 2 * (power + alpha + 1); 155 | for (int i = 0; i < maxIterations; i++) 156 | { 157 | // Calculate sin(theta), cos(theta) such that (sin(2theta), cos(2theta)) dot (b01, (b11 - b00)/2) = 0 158 | // See eq.6 https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf 159 | 160 | float c1_2theta = 0.0f, s1_2theta = 0.0f; 161 | ParallelSinCos(0.5f * (B.m11 - B.m00), -B.m01, ref c1_2theta, ref s1_2theta); 162 | float s1 = Mathf.Sqrt(0.5f * (1f - c1_2theta)); 163 | float c1 = s1_2theta / (2 * s1); 164 | 165 | // Calculate Gt * B * G, but rather than actually multiply the matricies only calculate the upper triangular elements 166 | // and use pre-simplified formulae for each element 167 | 168 | float p00 = c1 * (c1 * B.m00 + s1 * B.m01) + s1 * (c1 * B.m01 + s1 * B.m11); 169 | float p11 = B.m22; 170 | float p22 = s1 * (s1 * B.m00 - c1 * B.m01) - c1 * (s1 * B.m01 - c1 * B.m11); 171 | float p01 = s1 * B.m12; 172 | float p12 = c1 * B.m12; 173 | 174 | B = new Matrix3x3f( 175 | p00, p01, 0f, 176 | p01, p11, p12, 177 | 0f, p12, p22 178 | ); 179 | //Debug.Log("Iteration " + i.ToString()); 180 | //Debug.Log(string.Format("b00: {0}\tb11: {1}\tb22: {2}\nb01: {3}\tb12: {4}", B.m00, B.m11, B.m22, B.m01, B.m12)); 181 | /* Calculate Q = Q * G 182 | */ 183 | Q = new Matrix3x3f( 184 | Q.m00 * c1 + Q.m01 * s1, Q.m02, Q.m00 * (-s1) + Q.m01 * c1, 185 | Q.m10 * c1 + Q.m11 * s1, Q.m12, Q.m10 * (-s1) + Q.m11 * c1, 186 | Q.m20 * c1 + Q.m21 * s1, Q.m22, Q.m20 * (-s1) + Q.m21 * c1 187 | ); 188 | 189 | /* 190 | string qout = ""; 191 | for (int t = 0; t < 3; t++) 192 | { 193 | qout += string.Format("{0}\t{1}\t{2}\n", Q.mValues[t][0], Q.mValues[t][1], Q.mValues[t][2]); 194 | } 195 | Debug.Log("Q:"); 196 | Debug.Log(qout); 197 | */ 198 | 199 | if (isRelativeZero(p11, p22, p12)) 200 | { 201 | //Debug.Log(string.Format("Successfully Zero'd out p12 after {0} iterations", i)); 202 | float c2_2theta = 0.0f, s2_2theta = 0.0f; 203 | ParallelSinCos((p00 - p11) * 0.5f, p01, ref c2_2theta, ref s2_2theta); 204 | float s2 = Mathf.Sqrt(0.5f * (1f - c2_2theta)); 205 | float c2 = s2_2theta / (2 * s2); 206 | eigValues.x = c2 * (c2 * p00 + s2 * p01) + s2 * (c2 * p01 + s2 * p11); 207 | eigValues.y = s2 * (s2 * p00 - c2 * p01) - c2 * (s2 * p01 - c2 * p11); 208 | eigValues.z = p22; 209 | 210 | Matrix3x3f H2 = new Matrix3x3f( 211 | c2, s2, 0f, 212 | s2, -c2, 0f, 213 | 0f, 0f, 1f 214 | ); 215 | Q = new Matrix3x3f( 216 | Q.m00 * c2 + Q.m01 * s2, Q.m00 * s2 - Q.m01 * c2, Q.m02, 217 | Q.m10 * c2 + Q.m11 * s2, Q.m10 * s2 - Q.m11 * c2, Q.m12, 218 | Q.m20 * c2 + Q.m21 * s2, Q.m20 * s2 - Q.m21 * c2, Q.m22 219 | ); 220 | break; 221 | } 222 | } 223 | //B = 224 | } 225 | else 226 | { 227 | int power = 127;// since float_exp(b01) doesn't seem to be outputting the correct values, assume the max exponent; 228 | //Debug.Log("Power: " + power.ToString()); 229 | int maxIterations = 2 * (power + alpha + 1); 230 | for (int i = 0; i < maxIterations; i++) 231 | { 232 | // Calculate sin(theta), cos(theta) such that (sin(2theta), cos(2theta)) dot (b12, (b22 - b11)/2) = 0 233 | // See eq.20 https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf 234 | 235 | float c1_2theta = 0.0f, s1_2theta = 0.0f; 236 | ParallelSinCos(0.5f * (B.m22 - B.m11), - B.m12, ref c1_2theta, ref s1_2theta); 237 | float s1 = Mathf.Sqrt(0.5f * (1f - c1_2theta)); 238 | float c1 = s1_2theta / (2 * s1); 239 | 240 | // Calculate Gt * B * G, but rather than actually multiply the matricies only calculate the upper triangular elements 241 | // and use pre-simplified formulae for each element 242 | 243 | float p00 = c1 * (c1 * B.m11 + s1 * B.m12) + s1 * (c1 * B.m12 + s1 * B.m22); 244 | float p11 = B.m00; 245 | float p22 = s1 * (s1 * B.m11 - c1 * B.m12) - c1 * (s1 * B.m12 - c1 * B.m22); 246 | float p01 = c1 * B.m01; 247 | float p12 = -s1 * B.m01; 248 | 249 | B = new Matrix3x3f( 250 | p00, p01, 0f, 251 | p01, p11, p12, 252 | 0f, p12, p22 253 | ); 254 | 255 | //Debug.Log("Iteration " + i.ToString()); 256 | //Debug.Log(string.Format("b00: {0}\tb11: {1}\tb22: {2}\nb01: {3}\tb12: {4}", B.m00, B.m11, B.m22, B.m01, B.m12)); 257 | 258 | /* Calculate Q = Q * G 259 | */ 260 | Q = new Matrix3x3f( 261 | Q.m01 * c1 + Q.m02 * s1, Q.m00, Q.m01 * (-s1) + Q.m02 * c1, 262 | Q.m11 * c1 + Q.m12 * s1, Q.m10, Q.m11 * (-s1) + Q.m12 * c1, 263 | Q.m21 * c1 + Q.m22 * s1, Q.m20, Q.m21 * (-s1) + Q.m22 * c1 264 | ); 265 | 266 | /* 267 | string qout = ""; 268 | for (int t = 0; t < 3; t++) 269 | { 270 | qout += string.Format("{0}\t{1}\t{2}\n",Q.mValues[t][0], Q.mValues[t][1], Q.mValues[t][2]); 271 | } 272 | Debug.Log("Q:"); 273 | Debug.Log(qout); 274 | */ 275 | 276 | if (isRelativeZero(B.m00, B.m11, B.m01)) 277 | { 278 | //Debug.Log(string.Format("Successfully Zero'd out p01 after {0} iterations", i)); 279 | float c2_2theta = 0.0f, s2_2theta = 0.0f; 280 | ParallelSinCos((B.m11 - B.m22) * 0.5f, B.m12, ref c2_2theta, ref s2_2theta); 281 | float s2 = Mathf.Sqrt(0.5f * (1f - c2_2theta)); 282 | float c2 = s2_2theta / (2 * s2); 283 | eigValues.x = B.m00; 284 | eigValues.y = c2 * (c2 * B.m11 + s2 * B.m12) + s2 * (c2 * B.m12 + s2 * B.m22); 285 | eigValues.z = s2 * (s2 * B.m11 - c2 * B.m12) - c2 * (s2 * B.m12 - c2 * B.m22); 286 | 287 | Matrix3x3f H2 = new Matrix3x3f( 288 | 1, 0, 0f, 289 | 0, c2, s2, 290 | 0f, s2, -c2 291 | ); 292 | Q = new Matrix3x3f( 293 | Q.m00, Q.m01 * c2 + Q.m02 * s2, Q.m01 * s2 - Q.m02 * c2, 294 | Q.m10, Q.m11 * c2 + Q.m12 * s2, Q.m11 * s2 - Q.m12 * c2, 295 | Q.m20, Q.m21 * c2 + Q.m22 * s2, Q.m21 * s2 - Q.m22 * c2 296 | ); 297 | break; 298 | } 299 | } 300 | } 301 | return Q; 302 | } 303 | } 304 | 305 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/EigenDecomposition.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e8610e294f41a3d419a6a4ed9f233513 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/GaussTexUI.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using GaussianTexture; 4 | using System.IO; 5 | public class GaussTexUI : EditorWindow 6 | { 7 | 8 | public ComputeShader TexturePreprocessor; 9 | public ComputeShader BitonicSort; 10 | public ComputeShader CumulativeDistribution; 11 | 12 | public Texture2D InputTex; 13 | public Material TestMaterial; 14 | public ColorspaceObj colorSpace; 15 | public bool EigenColors = true; 16 | public bool CompressionCorrection = false; 17 | public FileType fileType = FileType.png; 18 | 19 | 20 | public string axis0Property = "_CX"; 21 | public string axis1Property = "_CY"; 22 | public string axis2Property = "_CZ"; 23 | public string centerProperty = "_CsCenter"; 24 | 25 | private int LUTWidth = 16; 26 | private int LUTWidthPow2 = 4; 27 | private int LUTHeight = 16; 28 | private int LUTHeightPow2 = 4; 29 | 30 | private const int WINDOW_BORDER = 4; 31 | private const int WINDOW_MIN_W = 300; 32 | private const int WINDOW_MIN_H = 128; 33 | private const int SCROLL_WIDTH = 16; 34 | 35 | private bool GaussFoldout = true; 36 | private bool CSFoldout = false; 37 | private bool MatAssignFoldout = true; 38 | private bool MaterialPropertyFoldout = false; 39 | 40 | private Vector2 ScrollPos; 41 | private GUIStyle titleStyle; 42 | private GUIContent GaussLabel; 43 | private GUIContent ColorspaceLabel; 44 | 45 | 46 | private TexToGaussian ImageConverter; 47 | 48 | [MenuItem("Window/Convert Texture To Gaussian")] 49 | public static void ShowWindow() 50 | { 51 | GaussTexUI GaussUI = GetWindow(false, " Tex2Gaussian", true); 52 | GaussUI.minSize = new Vector2(WINDOW_MIN_W, WINDOW_MIN_H); 53 | } 54 | 55 | 56 | public void Awake() 57 | { 58 | //titleStyle = new GUIStyle(EditorStyles.foldout); 59 | GaussLabel = new GUIContent(); 60 | GaussLabel.text = " Convert Texture to Gaussian"; 61 | GaussLabel.image = EditorGUIUtility.ObjectContent(null, typeof(RenderTexture)).image; 62 | ColorspaceLabel = new GUIContent(); 63 | ColorspaceLabel.text = " Copy Colorspace Settings To Material"; 64 | ColorspaceLabel.image = EditorGUIUtility.ObjectContent(null, typeof(Transform)).image; 65 | 66 | ImageConverter = ScriptableObject.CreateInstance(); 67 | int foundComputeShaders = ImageConverter.AssignComputeShaders(); 68 | if (foundComputeShaders == 0) 69 | { 70 | TexturePreprocessor = ImageConverter.TexturePreprocessor; 71 | BitonicSort = ImageConverter.BitonicSort; 72 | CumulativeDistribution = ImageConverter.CumulativeDistribution; 73 | } 74 | else 75 | { 76 | Debug.LogWarning("Could not find compute shader dependencies"); 77 | } 78 | DestroyImmediate(ImageConverter); 79 | } 80 | 81 | void OnGUI() 82 | { 83 | titleStyle = GUI.skin.GetStyle("IN Title"); 84 | ScrollPos = GUILayout.BeginScrollView(ScrollPos, false, true, GUIStyle.none, GUI.skin.verticalScrollbar); 85 | 86 | GaussFoldout = DrawTitle(GaussLabel, GaussFoldout); 87 | 88 | if (GaussFoldout) 89 | { 90 | EditorGUI.indentLevel++; 91 | InputTex = EditorGUILayout.ObjectField("Texture to Convert", InputTex, typeof(Texture2D), false, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)) as Texture2D; 92 | 93 | fileType = (FileType)EditorGUILayout.EnumPopup("Save as: ", fileType, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 94 | if (fileType == FileType.jpg) 95 | { 96 | TextureImporter texImp = (TextureImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(InputTex)); 97 | if (texImp?.textureType == TextureImporterType.NormalMap) 98 | { 99 | EditorGUILayout.HelpBox("Jpg format cannot store normal maps as they use an alpha channel", MessageType.Error, true); 100 | } 101 | else 102 | { 103 | EditorGUILayout.HelpBox("Jpg format cannot store an alpha channel, only use this for textures with no alpha", MessageType.Warning, true); 104 | } 105 | } 106 | 107 | EditorGUILayout.BeginHorizontal(GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 108 | EditorGUILayout.LabelField(new GUIContent("Decorrolate Colorspace (Albedo, Specular only)","Prevents colors from appearing that aren't in the input image, but may slightly" + 109 | " reduce color accuracy. Only use with images that contain color information, not for images which contain independent information in each channel like metallic-smoothness maps"), 110 | GUILayout.Width(300)); 111 | GUILayout.FlexibleSpace(); 112 | EigenColors = EditorGUILayout.Toggle(EigenColors, GUILayout.Width(64 + SCROLL_WIDTH)); 113 | EditorGUILayout.EndHorizontal(); 114 | 115 | using (new EditorGUI.DisabledScope(EigenColors == false)) 116 | { 117 | EditorGUILayout.BeginHorizontal(GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 118 | EditorGUILayout.LabelField(new GUIContent("Compression Correction", "If Decorrolate Colorspace is enabled, attempt to correct for DXT compression." + 119 | "Seems to cause bad artifacts in many cases"), 120 | GUILayout.Width(300)); 121 | GUILayout.FlexibleSpace(); 122 | CompressionCorrection = EditorGUILayout.Toggle(CompressionCorrection, GUILayout.Width(64 + SCROLL_WIDTH)); 123 | EditorGUILayout.EndHorizontal(); 124 | } 125 | 126 | EditorGUILayout.BeginHorizontal(GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 127 | EditorGUILayout.LabelField("Lookup Table Dimensions:"); 128 | GUILayout.FlexibleSpace(); 129 | GUIContent LUTLabel = new GUIContent(); 130 | LUTLabel.text = string.Format("{0}x{1}, {2} Values", LUTWidth, LUTHeight, LUTWidth * LUTHeight); 131 | EditorGUILayout.LabelField(LUTLabel, EditorStyles.boldLabel, GUILayout.Width(EditorStyles.label.CalcSize(LUTLabel).x + 2*SCROLL_WIDTH)); 132 | EditorGUILayout.EndHorizontal(); 133 | 134 | EditorGUI.indentLevel++; 135 | LUTWidthPow2 = DrawSliderSteps("Width Power of 2", LUTWidthPow2, 1, 5, ref LUTWidth); 136 | LUTHeightPow2 = DrawSliderSteps("Height Power of 2", LUTHeightPow2, 1, 5, ref LUTHeight); 137 | EditorGUI.indentLevel--; 138 | 139 | CSFoldout = EditorGUILayout.Foldout(CSFoldout, "Compute Shaders"); 140 | if (CSFoldout) 141 | { 142 | EditorGUI.indentLevel++; 143 | TexturePreprocessor = EditorGUILayout.ObjectField("Texture Preprocessor", TexturePreprocessor, typeof(ComputeShader), false, 144 | GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)) as ComputeShader; 145 | BitonicSort = EditorGUILayout.ObjectField("Bitonic Merge Sort", BitonicSort, typeof(ComputeShader), false, 146 | GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)) as ComputeShader; 147 | CumulativeDistribution = EditorGUILayout.ObjectField("Gaussian Conversion", CumulativeDistribution, typeof(ComputeShader), false, 148 | GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)) as ComputeShader; 149 | EditorGUI.indentLevel--; 150 | } 151 | 152 | if (GUILayout.Button("Create Gaussian Texture and Lookup Table", GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH), GUILayout.Height(30))) 153 | { 154 | ImageConverter = ScriptableObject.CreateInstance(); 155 | if (TexturePreprocessor != null && BitonicSort != null && CumulativeDistribution != null) 156 | { 157 | ImageConverter.TexturePreprocessor = TexturePreprocessor; 158 | ImageConverter.BitonicSort = BitonicSort; 159 | ImageConverter.CumulativeDistribution = CumulativeDistribution; 160 | ImageConverter.CreateGaussianTexture(InputTex, LUTWidthPow2, LUTHeightPow2, CompressionCorrection, EigenColors, fileType); 161 | 162 | string inputNameAndPath = AssetDatabase.GetAssetPath(InputTex); 163 | string inputName = Path.GetFileNameWithoutExtension(inputNameAndPath); 164 | string inputPath = Path.GetDirectoryName(inputNameAndPath); 165 | 166 | string outputImagePath = Path.Combine(inputPath, inputName + "_gauss" + TexToGaussian.FileTypeToString(fileType)); 167 | string outputLUTPath = Path.Combine(inputPath, inputName + "_lut.asset"); 168 | string outputColorspacePath = Path.Combine(inputPath, inputName + "_Colorspace.asset"); 169 | EditorGUIUtility.PingObject(AssetDatabase.LoadMainAssetAtPath(outputImagePath)); 170 | EditorUtility.DisplayDialog("Textures Successfully Created", 171 | string.Format("Created:\n{0}\n{1}\n{2}", outputImagePath, outputLUTPath, outputColorspacePath), "Ok"); 172 | } 173 | else 174 | { 175 | EditorUtility.DisplayDialog("Compute Shaders Not Found", "One or more compute shader dependencies are missing. Make sure all 3 shaders are assigned before" + 176 | "attempting to run this script!", "Ok"); 177 | } 178 | DestroyImmediate(ImageConverter); 179 | } 180 | EditorGUILayout.Space(10); 181 | EditorGUI.indentLevel--; 182 | } 183 | 184 | MatAssignFoldout = DrawTitle(ColorspaceLabel, MatAssignFoldout); 185 | 186 | if (MatAssignFoldout) 187 | { 188 | EditorGUI.indentLevel++; 189 | TestMaterial = EditorGUILayout.ObjectField(TestMaterial, typeof(Material), false, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)) as Material; 190 | colorSpace = EditorGUILayout.ObjectField(colorSpace, typeof(ColorspaceObj), false, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)) as ColorspaceObj; 191 | MaterialPropertyFoldout = EditorGUILayout.Foldout(MaterialPropertyFoldout, "Material Property Names"); 192 | if (MaterialPropertyFoldout) 193 | { 194 | EditorGUI.indentLevel++; 195 | axis0Property = EditorGUILayout.TextField("Axis 0", axis0Property, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 196 | axis1Property = EditorGUILayout.TextField("Axis 1", axis1Property, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 197 | axis2Property = EditorGUILayout.TextField("Axis 2", axis2Property, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 198 | centerProperty = EditorGUILayout.TextField("Center", centerProperty, GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 199 | EditorGUI.indentLevel--; 200 | } 201 | 202 | if (GUILayout.Button("Assign Colorspace values", GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH), GUILayout.Height(30))) 203 | { 204 | TexToGaussian.CopyColorspaceToMat(TestMaterial, colorSpace, axis0Property, axis1Property, axis2Property, centerProperty); 205 | } 206 | EditorGUI.indentLevel--; 207 | } 208 | EditorGUILayout.EndScrollView(); 209 | 210 | } 211 | 212 | private void OnDestroy() 213 | { 214 | DestroyImmediate(ImageConverter); 215 | } 216 | 217 | private bool DrawTitle(GUIContent content, bool toggleName) 218 | { 219 | EditorGUILayout.BeginVertical(); 220 | EditorGUILayout.LabelField(content, titleStyle, GUILayout.Width(EditorGUIUtility.currentViewWidth), GUILayout.Height(titleStyle.fixedHeight)); 221 | EditorGUILayout.Space(-6); 222 | GUILayout.Box("", GUILayout.Width(EditorGUIUtility.currentViewWidth), GUILayout.Height(1)); 223 | EditorGUILayout.Space(-28); 224 | EditorGUILayout.BeginHorizontal(); 225 | EditorGUILayout.Space(6); 226 | toggleName = EditorGUILayout.Foldout(toggleName, ""); 227 | GUILayout.FlexibleSpace(); 228 | EditorGUILayout.EndHorizontal(); 229 | EditorGUILayout.Space(6); 230 | EditorGUILayout.EndVertical(); 231 | return (toggleName); 232 | } 233 | private int DrawSliderSteps(string title, int value, int min, int max, ref int displayValue) 234 | { 235 | EditorGUILayout.BeginHorizontal(GUILayout.Width(EditorGUIUtility.currentViewWidth - SCROLL_WIDTH)); 236 | EditorGUILayout.LabelField(title); 237 | GUILayout.FlexibleSpace(); 238 | value = EditorGUILayout.IntSlider(value, min, max, GUILayout.Width(EditorGUIUtility.currentViewWidth / 2)); 239 | 240 | EditorGUILayout.EndHorizontal(); 241 | 242 | displayValue = 1 << value; 243 | 244 | return value; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/GaussTexUI.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7d5254361e01522468d1b2f535544545 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/Matrix3x3.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Error-mdl 3 | * Created: 2021-10-26 4 | * 5 | * An extremely incomplete class for 3x3 matricies made for doing eigen decomposition of 3x3 covarience matricies. 6 | * Only a few simple methods like matrix by matrix multiplication are implemented. 7 | * 8 | * (c) 2021 Error.mdl 9 | * This code is licensed under the BSD-3 Clause license, see LICENSE.md for more details 10 | */ 11 | 12 | using System; 13 | using System.Collections; 14 | using System.Collections.Generic; 15 | using System.Runtime.InteropServices; 16 | using UnityEngine; 17 | 18 | 19 | 20 | /// 21 | /// A mostly incomplete set of classes implementing matricies of other sizes than unity's Matrix4x4 22 | /// 23 | namespace MatrixPlus 24 | { 25 | /// 26 | /// Partially implemented 3x3 32 bit floating point matrix class, has a few basic constructors and operations 27 | /// 28 | public class Matrix3x3f 29 | { 30 | public float[][] mValues; 31 | public float m00 32 | { 33 | get { return mValues[0][0]; } 34 | set { mValues[0][0] = value; } 35 | } 36 | public float m01 37 | { 38 | get { return mValues[0][1]; } 39 | set { mValues[0][1] = value; } 40 | } 41 | public float m02 42 | { 43 | get { return mValues[0][2]; } 44 | set { mValues[0][2] = value; } 45 | } 46 | public float m10 47 | { 48 | get { return mValues[1][0]; } 49 | set { mValues[1][0] = value; } 50 | } 51 | public float m11 52 | { 53 | get { return mValues[1][1]; } 54 | set { mValues[1][1] = value; } 55 | } 56 | public float m12 57 | { 58 | get { return mValues[1][2]; } 59 | set { mValues[1][2] = value; } 60 | } 61 | public float m20 62 | { 63 | get { return mValues[2][0]; } 64 | set { mValues[2][0] = value; } 65 | } 66 | public float m21 67 | { 68 | get { return mValues[2][1]; } 69 | set { mValues[2][1] = value; } 70 | } 71 | public float m22 72 | { 73 | get { return mValues[2][2]; } 74 | set { mValues[2][2] = value; } 75 | } 76 | 77 | public static Matrix3x3f operator +(Matrix3x3f a, Matrix3x3f b) => 78 | new Matrix3x3f(new float[3, 3] { 79 | { a.m00 + b.m00, a.m01 + b.m01, a.m02 + b.m02 }, 80 | { a.m10 + b.m10, a.m11 + b.m11, a.m12 + b.m12 }, 81 | { a.m20 + b.m20, a.m21 + b.m21, a.m22 + b.m22 }, 82 | }); 83 | 84 | public static Matrix3x3f operator -(Matrix3x3f a, Matrix3x3f b) => 85 | new Matrix3x3f(new float[3, 3] { 86 | { a.m00 - b.m00, a.m01 - b.m01, a.m02 - b.m02 }, 87 | { a.m10 - b.m10, a.m11 - b.m11, a.m12 - b.m12 }, 88 | { a.m20 - b.m20, a.m21 - b.m21, a.m22 - b.m22 }, 89 | }); 90 | 91 | public static Matrix3x3f operator *(float a, Matrix3x3f b) => 92 | new Matrix3x3f(new float[3, 3] { 93 | { a * b.m00, a * b.m01, a * b.m02 }, 94 | { a * b.m10, a * b.m11, a * b.m12 }, 95 | { a * b.m20, a * b.m21, a * b.m22 }, 96 | }); 97 | public static Matrix3x3f operator *(Matrix3x3f b, float a) => 98 | new Matrix3x3f(new float[3, 3] { 99 | { a * b.m00, a * b.m01, a * b.m02 }, 100 | { a * b.m10, a * b.m11, a * b.m12 }, 101 | { a * b.m20, a * b.m21, a * b.m22 }, 102 | }); 103 | 104 | 105 | public Matrix3x3f() 106 | { 107 | mValues = new float[3][] {new float[3]{ 0.0f, 0.0f, 0.0f }, 108 | new float[3]{ 0.0f, 0.0f, 0.0f }, 109 | new float[3]{ 0.0f, 0.0f, 0.0f }}; 110 | } 111 | 112 | public Matrix3x3f(float i00, float i01, float i02, float i10, float i11, float i12, float i20, float i21, float i22) 113 | { 114 | mValues = new float[3][] {new float[3]{ i00, i01, i02 }, 115 | new float[3]{ i10, i11, i12 }, 116 | new float[3]{ i20, i21, i22 }}; 117 | } 118 | 119 | public Matrix3x3f(float[,] arrayInput) 120 | { 121 | if (arrayInput == null) 122 | { 123 | throw new ArgumentException(string.Format("Input array null", nameof(arrayInput))); 124 | } 125 | 126 | if (arrayInput.GetLength(0) != 3 || arrayInput.GetLength(1) != 3) 127 | { 128 | throw new ArgumentException(string.Format("Cannot initialize Matrix3x3f from array with dimensions {0}x{1}, input must be 3x3!", 129 | arrayInput.GetLength(0), arrayInput.GetLength(1)), nameof(arrayInput)); 130 | } 131 | 132 | mValues = new float[3][] {new float[3]{ arrayInput[0, 0], arrayInput[0, 1], arrayInput[0, 2] }, 133 | new float[3]{ arrayInput[1, 0], arrayInput[1, 1], arrayInput[1, 2] }, 134 | new float[3]{ arrayInput[2, 0], arrayInput[2, 1], arrayInput[2, 2] }}; 135 | /* 136 | for (int r = 0; r < 3; r++) 137 | { 138 | for (int c = 0; c < 3; c++) 139 | { 140 | mValues[r][c] = arrayInput[r, c]; 141 | } 142 | } 143 | */ 144 | } 145 | 146 | /// 147 | /// Fills the matrix with 3 Vector3's, assigning the vectors as columns of the matrix 148 | /// 149 | /// Vector3 that will become the first column of the matrix 150 | /// Vector3 that will become the second column of the matrix 151 | /// Vector3 that will become the third column of the matrix 152 | public void VectorsToColumns(Vector3 column0, Vector3 column1, Vector3 column2) 153 | { 154 | mValues[0][0] = column0.x; 155 | mValues[1][0] = column0.y; 156 | mValues[2][0] = column0.z; 157 | mValues[0][1] = column1.x; 158 | mValues[1][1] = column1.y; 159 | mValues[2][1] = column1.z; 160 | mValues[0][2] = column2.x; 161 | mValues[1][2] = column2.y; 162 | mValues[2][2] = column2.z; 163 | } 164 | 165 | /// 166 | /// Fills the matrix with 3 Vector3's, assigning the vectors as rows of the matrix 167 | /// 168 | /// Vector3 that will become the first row of the matrix 169 | /// Vector3 that will become the second row of the matrix 170 | /// Vector3 that will become the third row of the matrix 171 | public void VectorsToRows(Vector3 row0, Vector3 row1, Vector3 row2) 172 | { 173 | mValues[0][0] = row0.x; 174 | mValues[0][1] = row0.y; 175 | mValues[0][2] = row0.z; 176 | mValues[1][0] = row1.x; 177 | mValues[1][1] = row1.y; 178 | mValues[1][2] = row1.z; 179 | mValues[2][0] = row2.x; 180 | mValues[2][1] = row2.y; 181 | mValues[2][2] = row2.z; 182 | } 183 | 184 | /// 185 | /// Gets a column of the matrix, and returns it as a Vector3 186 | /// 187 | /// Index of the column to retrieve, starting at 0 188 | /// 189 | public Vector3 GetColumnVector(int index) 190 | { 191 | if (index >= 3 || index < 0) 192 | { 193 | throw new IndexOutOfRangeException(String.Format("Tried to retrieve column at index {0} of 3x3 matrix, index must be between 0 and 2", index)); 194 | } 195 | return new Vector3((float)mValues[0][index], (float)mValues[1][index], (float)mValues[2][index]); 196 | } 197 | 198 | /// 199 | /// 200 | /// 201 | /// 202 | /// 203 | public Vector3 GetRowVector(int index) 204 | { 205 | if (index >= 3 || index < 0) 206 | { 207 | throw new IndexOutOfRangeException(String.Format("Tried to retrieve row at index {0} of 3x3 matrix, index must be between 0 and 2", index)); 208 | } 209 | return new Vector3((float)mValues[index][0], (float)mValues[index][1], (float)mValues[index][2]); 210 | } 211 | 212 | public static Matrix3x3f Mul(Matrix3x3f mat1, Matrix3x3f mat2) 213 | { 214 | Matrix3x3f output = new Matrix3x3f(); 215 | for (int r = 0; r < 3; r++) 216 | { 217 | for (int c = 0; c < 3; c++) 218 | { 219 | float element = 0.0f; 220 | 221 | for (int i = 0; i < 3; i++) 222 | { 223 | element += mat1.mValues[r][i] * mat2.mValues[i][c]; 224 | } 225 | 226 | output.mValues[r][c] = element; 227 | } 228 | } 229 | return output; 230 | } 231 | 232 | public Matrix3x3f Transpose() 233 | { 234 | Matrix3x3f output = new Matrix3x3f(m00, m10, m20, 235 | m01, m11, m21, 236 | m02, m12, m22); 237 | return output; 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/Matrix3x3.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7cd67a9b4fd935c46b230af519579286 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Scripts/TexToGaussian.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0792ac0513edfb045b237b42824f3f03 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f08135839dd44574db500f3374bbe21f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Shaders/BitonicMergeSort.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel LocalFullSortKernel 3 | #pragma kernel LocalShearKernel 4 | #pragma kernel GlobalMirrorKernel 5 | #pragma kernel GlobalShearKernel 6 | 7 | 8 | #define NUM_THREADS 1024 9 | // Create a RenderTexture with enableRandomWrite flag and set it 10 | // with cs.SetTexture 11 | RWTexture2D _TexOut; 12 | 13 | RWStructuredBuffer _Color; 14 | RWStructuredBuffer _Index; 15 | groupshared float Local_Color[2 * NUM_THREADS]; 16 | groupshared uint Local_Index[2 * NUM_THREADS]; 17 | 18 | uniform uint _TexWidth; 19 | uniform uint _TexHeight; 20 | 21 | uniform uint _HeightPower; 22 | 23 | /* 24 | BITONIC MERGE SORT 25 | See https://en.wikipedia.org/wiki/Bitonic_sorter#Alternative_representation and https://poniesandlight.co.uk/reflect/bitonic_merge_sort/ 26 | for a more detailed explanation 27 | 28 | 0--*---*-----*---*---------*-----*---*-----------------*---------*-----*---- 29 | | | | | | | | | | | 30 | 1--*---|-*---*---|-*-------|-*---*---|-*---------------|-*-------|-*---*---- 31 | | | | | | | | | | | | | 32 | 2--*---|-*---*---|-|-*-----*-|---*---|-|-*-------------|-|-*-----*-|---*---- 33 | | | | | | | | | | | | | | | | | 34 | 3--*---*-----*---|-|-|-*-----*---*---|-|-|-*-----------|-|-|-*-----*---*---- 35 | | | | | | | | | | | | | 36 | 4--*---*-----*---|-|-|-*---*-----*---|-|-|-|-*---------*-|-|-|---*-----*---- 37 | | | | | | | | | | | | | | | | | | | 38 | 5--*---|-*---*---|-|-*-----|-*---*---|-|-|-|-|-*---------*-|-|---|-*---*---- 39 | | | | | | | | | | | | | | | | | 40 | 6--*---|-*---*---|-*-------*-|---*---|-|-|-|-|-|-*---------*-|---*-|---*---- 41 | | | | | | | | | | | | | | | | | 42 | 7--*---*-----*---*-----------*---*---|-|-|-|-|-|-|-*---------*-----*---*---- 43 | | | | | | | | | 44 | 8--*---*-----*---*---------*-----*---|-|-|-|-|-|-|-*---*---------*-----*---- 45 | | | | | | | | | | | | | | | | | 46 | 9--*---|-*---*---|-*-------|-*---*---|-|-|-|-|-|-*-----|-*-------|-*---*---- 47 | | | | | | | | | | | | | | | | | 48 | 10-*---|-*---*---|-|-*-----*-|---*---|-|-|-|-|-*-------|-|-*-----*-|---*---- 49 | | | | | | | | | | | | | | | | | | | 50 | 11-*---*-----*---|-|-|-*-----*---*---|-|-|-|-*---------|-|-|-*-----*---*---- 51 | | | | | | | | | | | | | 52 | 12-*---*-----*---|-|-|-*---*-----*---|-|-|-*-----------*-|-|-|---*-----*---- 53 | | | | | | | | | | | | | | | | | 54 | 13-*---|-*---*---|-|-*-----|-*---*---|-|-*---------------*-|-|---|-*---*---- 55 | | | | | | | | | | | | | 56 | 14-*---|-*---*---|-*-------*-|---*---|-*-------------------*-|---*-|---*---- 57 | | | | | | | | | | | 58 | 15-*---*-----*---*-----------*---*---*-----------------------*-----*---*---- 59 | h 2 4 8 16 60 | |----Mirror----|-------Shear--------| 61 | 62 | This sorting method works by repeatedly sorting groups of elements, starting with groups of 2 and doubling the group size after each step 63 | until the group size is equal to the entire data set. The group size of each step will be referred to as its height h. Each step is composed 64 | of several distinct sub-steps: a single "mirror" step followed by a number of "shear" steps. The "mirror" step compares and swaps each element 65 | in the first half of the group with the element whose index within the group is equal to h - 1 minus the index of the element. For example 66 | with a group size of 4, element 0 is compared to element 3, and element 1 is compared to element 2. The mirror is then followed by 0 or more 67 | shear steps. Each shear step operates on groups of half the previous shear's group size (or half of h if it is the first step), and the shears 68 | repeat until their group size is 2. Each shear step compares and swaps each element in the first half of its group with the element whose index 69 | within the group is equal to half the group size plus the index of the element. For example, the first shear step of h = 16 compares 70 | element 0 to element 4, 1 to 5, 2 to 6, and 3 to 7. The next shear compares 0 to 2, and 1 to 3. The last shear compares 0 to 1. 71 | 72 | */ 73 | 74 | // Common factor used to calculate the indicies of the pairs of numbers to compare in both the mirror and shear steps 75 | inline uint q_calc(const uint t, const uint h_pow) 76 | { 77 | return ((t << 1) >> h_pow) << h_pow; 78 | } 79 | 80 | // Common factor used to calculate the indicies of the pairs of numbers to compare in both the mirror and shear steps 81 | inline uint m_calc(const uint t, const uint h) 82 | { 83 | uint h2 = max(1, (h >> 1)); 84 | return t % h2; 85 | } 86 | 87 | // Given a thread number, calculate the pair of indicies of the numbers to compare in a mirror step 88 | inline void calc_mirror_index(inout uint index1, inout uint index2, const uint h, const uint q, const uint m) 89 | { 90 | index1 = q + m; 91 | index2 = q + h - m - 1; 92 | } 93 | 94 | // Given a thread number, calculate the pair of indicies of the numbers to compare in a shear step 95 | inline void calc_shear_index(inout uint index1, inout uint index2, const uint h, const uint q, const uint m) 96 | { 97 | index1 = q + m; 98 | index2 = q + m + (h >> 1); 99 | } 100 | 101 | // Sort pairs of colors and pixel cordinates by the colors in the local groupshared array 102 | void local_sort_pair(uint i1, uint i2) 103 | { 104 | if (Local_Color[i1] < Local_Color[i2]) 105 | { 106 | float tmpColor = Local_Color[i1]; 107 | int tmpIndex = Local_Index[i1]; 108 | Local_Color[i1] = Local_Color[i2]; 109 | Local_Index[i1] = Local_Index[i2]; 110 | Local_Color[i2] = tmpColor; 111 | Local_Index[i2] = tmpIndex; 112 | } 113 | } 114 | 115 | // Sort pairs of colors and pixel cordinates by the colors in the global compute buffers 116 | void global_sort_pair(uint i1, uint i2) 117 | { 118 | if (_Color[i1] < _Color[i2]) 119 | { 120 | float tmpColor = _Color[i1]; 121 | int tmpIndex = _Index[i1]; 122 | _Color[i1] = _Color[i2]; 123 | _Index[i1] = _Index[i2]; 124 | _Color[i2] = tmpColor; 125 | _Index[i2] = tmpIndex; 126 | } 127 | } 128 | 129 | // Mirror a single pair of colors in the groupshared array 130 | void local_sort_mirror(const uint t, const uint h, const uint h_pow) 131 | { 132 | uint q, m; 133 | uint i1, i2; 134 | q = q_calc(t, h_pow); 135 | m = m_calc(t, h); 136 | calc_mirror_index(i1, i2, h, q, m); 137 | local_sort_pair(i1, i2); 138 | } 139 | 140 | // Mirror a single pair of colors in the compute buffer 141 | void global_sort_mirror(const uint t, const uint h, const uint h_pow) 142 | { 143 | uint q, m; 144 | uint i1, i2; 145 | q = q_calc(t, h_pow); 146 | m = m_calc(t, h); 147 | calc_mirror_index(i1, i2, h, q, m); 148 | global_sort_pair(i1, i2); 149 | } 150 | 151 | 152 | // Shear a single pair of colors in the groupshared array 153 | void local_sort_shear(const uint t, const uint h, const uint h_pow) 154 | { 155 | uint q, m; 156 | uint i1, i2; 157 | q = q_calc(t, h_pow); 158 | m = m_calc(t, h); 159 | calc_shear_index(i1, i2, h, q, m); 160 | local_sort_pair(i1, i2); 161 | } 162 | 163 | // Shear a single pair of colors in the compute buffer 164 | void global_sort_shear(const uint t, const uint h, const uint h_pow) 165 | { 166 | uint q, m; 167 | uint i1, i2; 168 | q = q_calc(t, h_pow); 169 | m = m_calc(t, h); 170 | calc_shear_index(i1, i2, h, q, m); 171 | global_sort_pair(i1, i2); 172 | } 173 | 174 | //Perform shears in local memory until h = 2 175 | void local_full_shear(const uint t, uint h, uint h_pow) 176 | { 177 | uint h_temp_pow = h_pow; 178 | for (uint h_temp = h; h_temp >= 2; h_temp = h_temp >> 1) 179 | { 180 | local_sort_shear(t, h_temp, h_temp_pow); 181 | GroupMemoryBarrierWithGroupSync(); 182 | h_temp_pow -= 1; 183 | } 184 | } 185 | 186 | //Do a full bitonic sort up to a groupsize of 2 * number of threads, which is the length of each of the groupshared arrays, 187 | //or until we sort all the elements in the texture if it is small 188 | void local_full_sort(const uint t) 189 | { 190 | uint h_pow = 1; 191 | uint h; 192 | uint maxIter = max(2 * NUM_THREADS, _TexWidth * _TexHeight); 193 | for (h = 2; h <= maxIter; h = h << 1) 194 | { 195 | local_sort_mirror(t, h, h_pow); 196 | GroupMemoryBarrierWithGroupSync(); 197 | uint h2_pow = h_pow - 1; 198 | uint h2 = h >> 1; 199 | local_full_shear(t, h2, h2_pow); 200 | h_pow += 1; 201 | } 202 | } 203 | 204 | [numthreads(NUM_THREADS, 1, 1)] 205 | void LocalFullSortKernel(uint3 id : SV_DispatchThreadID, uint3 group : SV_GroupID, uint thread : SV_GroupIndex) 206 | { 207 | //if (group.x*NUM_THREADS < ((_TexWidth * _TexHeight) >> 1)) // if the thread's id is bigger than half of the number of elements to sort, then skip all calculations. 208 | //{ 209 | //copy two elements per thread into local group-shared memory 210 | uint even = thread << 1; 211 | uint odd = even + 1; 212 | uint prevGroupThreads = group.x * NUM_THREADS; 213 | uint globalEvenIndex = 2 * prevGroupThreads + even; 214 | uint globalOddIndex = 2 * prevGroupThreads + odd; 215 | 216 | Local_Color[even] = _Color[globalEvenIndex]; 217 | Local_Color[odd] = _Color[globalOddIndex]; 218 | Local_Index[even] = _Index[globalEvenIndex]; 219 | Local_Index[odd] = _Index[globalOddIndex]; 220 | 221 | //Sync the group to make sure all elements have been copied to the shared memory 222 | GroupMemoryBarrierWithGroupSync(); 223 | 224 | //Sort all elements in the local cache doing bitonic merge sorting, doing group heights from 2 to 2 * NUM_THREADS 225 | local_full_sort(thread); 226 | 227 | GroupMemoryBarrierWithGroupSync(); 228 | 229 | //Copy the sorted elments in local memory back into the global buffers 230 | _Color[globalEvenIndex] = Local_Color[even]; 231 | _Index[globalEvenIndex] = Local_Index[even]; 232 | _Color[globalOddIndex] = Local_Color[odd]; 233 | _Index[globalOddIndex] = Local_Index[odd]; 234 | //} 235 | } 236 | 237 | 238 | [numthreads(NUM_THREADS, 1, 1)] 239 | void LocalShearKernel(uint3 id : SV_DispatchThreadID, uint3 group : SV_GroupID, uint thread : SV_GroupIndex) 240 | { 241 | uint even = thread << 1; 242 | uint odd = even + 1; 243 | uint prevGroupThreads = group.x * NUM_THREADS; 244 | uint globalEvenIndex = 2 * prevGroupThreads + even; 245 | uint globalOddIndex = 2 * prevGroupThreads + odd; 246 | 247 | Local_Color[even] = _Color[globalEvenIndex]; 248 | Local_Color[odd] = _Color[globalOddIndex]; 249 | Local_Index[even] = _Index[globalEvenIndex]; 250 | Local_Index[odd] = _Index[globalOddIndex]; 251 | 252 | GroupMemoryBarrierWithGroupSync(); 253 | 254 | uint h = 1 << _HeightPower; 255 | local_full_shear(thread, h, _HeightPower); 256 | 257 | GroupMemoryBarrierWithGroupSync(); 258 | 259 | _Color[globalEvenIndex] = Local_Color[even]; 260 | _Index[globalEvenIndex] = Local_Index[even]; 261 | _Color[globalOddIndex] = Local_Color[odd]; 262 | _Index[globalOddIndex] = Local_Index[odd]; 263 | } 264 | 265 | [numthreads(NUM_THREADS, 1, 1)] 266 | void GlobalMirrorKernel(uint3 id : SV_DispatchThreadID, uint3 group : SV_GroupID, uint thread : SV_GroupIndex) 267 | { 268 | 269 | uint h = 1 << _HeightPower; 270 | global_sort_mirror(id.x, h, _HeightPower); 271 | AllMemoryBarrierWithGroupSync(); 272 | } 273 | 274 | [numthreads(NUM_THREADS, 1, 1)] 275 | void GlobalShearKernel(uint3 id : SV_DispatchThreadID, uint3 group : SV_GroupID, uint thread : SV_GroupIndex) 276 | { 277 | uint h = 1 << _HeightPower; 278 | global_sort_shear(id.x, h, _HeightPower); 279 | AllMemoryBarrierWithGroupSync(); 280 | } 281 | 282 | /* 283 | [numthreads(NUM_THREADS, 1, 1)] 284 | void GlobalCopyBufferToTexture(uint3 id : SV_DispatchThreadID, uint3 group : SV_GroupID, uint thread : SV_GroupIndex) 285 | { 286 | uint even = thread << 1; 287 | uint odd = even + 1; 288 | uint prevGroupThreads = group.x * NUM_THREADS; 289 | 290 | uint globalIndex = 2 * prevGroupThreads + even; 291 | uint2 uv = uint2(globalIndex % _TexWidth, globalIndex / _TexWidth); 292 | _TexOut[uv] = float4(_Color[globalIndex], 0, 0, 1); 293 | //_TexOut[uv] = float4(group.x, 0, 0, 1); 294 | 295 | globalIndex = 2 * prevGroupThreads + odd; 296 | uv = uint2(globalIndex % _TexWidth, globalIndex / _TexWidth); 297 | _TexOut[uv] = float4(_Color[globalIndex], 0, 0, 1); 298 | } 299 | */ 300 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Shaders/BitonicMergeSort.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 67105f801c9eea7438afd827ce060278 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Shaders/SortedTextureToGaussian.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CreateInvCDFTexture 3 | #pragma kernel CreateLookupTable 4 | #pragma kernel PopulateLUTMipFilter 5 | #pragma kernel AverageLUTMipFilter 6 | #pragma kernel CompressionCorrection 7 | #pragma kernel WriteBlackToLUT 8 | #pragma kernel CopyLUTSliceTo2D 9 | 10 | 11 | #define NUM_THREADS 1024 12 | #define NUM_THREADS_2D 32 13 | #define MAX_DIM_LUT 32 14 | 15 | #define alpha 1.128379167095512573896 // 2 / sqrt(pi) 16 | #define beta 0.102772603301939973205 // (8 - 2 * pi)/(3 * pi^1.5) 17 | #define gamma 3.826116760542201589481 // 2*sqrt(alpha/3*beta) 18 | #define epsilon 0.694877062764156364381 // 1 / ( (alpha/3*beta)^1.5 * 2*beta) 19 | #define sqrt2 1.414213562373095048802 20 | 21 | #define gaussian_mu 0.5 22 | #define gaussian_sigma 0.1666666667 23 | #define gaussian_correction 1.0026 24 | #define gaussian_inv_correction 0.997665462817 25 | 26 | Texture2D _TexIn; 27 | RWTexture2D _TexOut; 28 | RWTexture2DArray _LUT; 29 | RWStructuredBuffer _Index; 30 | 31 | 32 | RWStructuredBuffer _Red; 33 | RWStructuredBuffer _Green; 34 | RWStructuredBuffer _Blue; 35 | RWStructuredBuffer _Alpha; 36 | 37 | uniform uint _TexWidth; 38 | uniform uint _TexHeight; 39 | uniform float4 _ColorMask; 40 | uniform float4 _InvAxisLengths; 41 | 42 | uniform uint _LUTWidth; 43 | uniform uint _LUTHeight; 44 | uniform uint _MipLevel; 45 | uniform float4 _MipStd; 46 | uniform uint _KernelWidth; 47 | 48 | // hlsl doesn't have asinh despite having sinh, so we need to make our own 49 | float arcsinh(const float x) 50 | { 51 | return log(x + sqrt(x * x + 1)); 52 | } 53 | 54 | // hlsl doesn't have atanh despite having tanh, so we need to make our own 55 | float arctanh(const float x) 56 | { 57 | return 0.5 * log((1.0 + x) / (1.0 - x)); 58 | } 59 | 60 | 61 | /* The inverse error function is properly calculated by an infinite talyor series that converges EXTREMELY slowly 62 | * for inputs close to 1, so instead we're going to use an approximation based on hyperbolic functions that is close 63 | * enough for the level of precision we require. 64 | * See John D. Vedder, "Simple approximations for the error function and its inverse", American Journal of Physics 55, 762 - 763 (1987) 65 | */ 66 | float InvErf(const float x) 67 | { 68 | float part1 = epsilon * arctanh(x); 69 | float part2 = 0.333333333333333 * arcsinh(part1); 70 | return gamma * sinh(part2); 71 | } 72 | 73 | float Quantile(const float index, const float numElements) 74 | { 75 | return (index + 0.5) / numElements; 76 | } 77 | 78 | float InvCDF(const float U, const float mu, const float sigma) 79 | { 80 | float x = 2.0 * U - 1.0; 81 | float invErfx = InvErf(gaussian_inv_correction * x); 82 | return mu + sigma * sqrt2 * invErfx; 83 | } 84 | 85 | float Erf(const float x) 86 | { 87 | float e = exp(-x * x); 88 | return alpha * sign(x) * sqrt(1.0 - e) * ((1.0/alpha) + 0.155 * e - 0.042625 * e * e); 89 | } 90 | 91 | float CDF(const float x, const float mu, const float sigma) 92 | { 93 | float erf = Erf((x-mu)/(sigma*sqrt2)); 94 | return 0.5 * (1 + gaussian_correction * erf); 95 | } 96 | 97 | [numthreads(NUM_THREADS,1,1)] 98 | void CreateInvCDFTexture (uint3 id : SV_DispatchThreadID) 99 | { 100 | int index = _Index[id.x]; 101 | float U = Quantile((float)id.x, (float)(_TexWidth * _TexHeight)); 102 | float invCDF = InvCDF(U, gaussian_mu, gaussian_sigma); 103 | int2 uv = int2(index % _TexWidth, index / _TexWidth); 104 | float4 color = _TexOut[uv] * (1.0 - _ColorMask) + invCDF * _ColorMask; 105 | _TexOut[uv] = color; 106 | } 107 | 108 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 109 | void CreateLookupTable(uint3 id : SV_DispatchThreadID) 110 | { 111 | if (id.x < _LUTWidth && id.y < _LUTHeight) 112 | { 113 | float x = Quantile(id.x + _LUTWidth * id.y, _LUTWidth * _LUTHeight); 114 | float U = CDF(x, gaussian_mu, gaussian_sigma); 115 | int index = (int)floor((double)U * (double)(_TexWidth * _TexHeight)); 116 | float R = _Red[index]; 117 | float G = _Green[index]; 118 | float B = _Blue[index]; 119 | float A = _Alpha[index]; 120 | float4 color = float4(R, G, B, A); 121 | _LUT[int3(id.xy, 0)] = color; 122 | } 123 | } 124 | 125 | 126 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 127 | void PopulateLUTMipFilter(uint3 id : SV_DispatchThreadID) 128 | { 129 | uint lutDim = _LUTWidth * _LUTHeight; 130 | if (id.x < lutDim * 2 && id.y < lutDim) 131 | { 132 | float U = Quantile(id.x, lutDim * 2); 133 | float yQ = Quantile(id.y, lutDim); 134 | float4 LUTx = float4(InvCDF(U, yQ, _MipStd.x), InvCDF(U, yQ, _MipStd.y), InvCDF(U, yQ, _MipStd.z), InvCDF(U, yQ, _MipStd.w)); 135 | LUTx *= lutDim; 136 | int4 LUTdim = clamp(LUTx, 0, lutDim - 1); 137 | int3 LUTuv0 = int3(LUTdim.x % _LUTWidth, LUTdim.x / _LUTWidth, 0); 138 | int3 LUTuv1 = int3(LUTdim.y % _LUTWidth, LUTdim.y / _LUTWidth, 0); 139 | int3 LUTuv2 = int3(LUTdim.z % _LUTWidth, LUTdim.z / _LUTWidth, 0); 140 | int3 LUTuv3 = int3(LUTdim.w % _LUTWidth, LUTdim.w / _LUTWidth, 0); 141 | _TexOut[id.xy] = float4(_LUT[LUTuv0].r, _LUT[LUTuv1].g, _LUT[LUTuv2].b, _LUT[LUTuv3].a); 142 | } 143 | } 144 | 145 | 146 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 147 | void AverageLUTMipFilter(uint3 id : SV_DispatchThreadID) 148 | { 149 | uint KernelCoords = _KernelWidth * id.x; 150 | if (KernelCoords < _TexWidth && id.y < _TexHeight) 151 | { 152 | float4 result = 0.0f; 153 | for (uint i = KernelCoords; i < KernelCoords + _KernelWidth; i++) 154 | { 155 | result += _TexIn.Load(int3(i, id.y, 0)); 156 | } 157 | result /= _KernelWidth; 158 | if (_KernelWidth == _TexWidth) 159 | { 160 | int3 uv = int3(id.y % _LUTWidth, id.y / _LUTWidth, _MipLevel); 161 | _LUT[uv] = result; 162 | } 163 | else 164 | { 165 | _TexOut[id.xy] = result; 166 | } 167 | } 168 | } 169 | 170 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 171 | void CompressionCorrection(uint3 id : SV_DispatchThreadID) 172 | { 173 | if (id.x < _TexWidth && id.y < _TexHeight) 174 | { 175 | float4 inColor = _TexOut.Load(id.xy); 176 | float4 outColor = (inColor - 0.5) * _InvAxisLengths + 0.5; 177 | _TexOut[id.xy] = outColor; 178 | } 179 | } 180 | 181 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 182 | void WriteBlackToLUT(uint3 id : SV_DispatchThreadID) 183 | { 184 | if (id.x < _LUTWidth && id.y < _LUTHeight) 185 | { 186 | _LUT[uint3(id.xy, 1)] = float4(0,1,0,0); 187 | } 188 | } 189 | 190 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 191 | void CopyLUTSliceTo2D(uint3 id : SV_DispatchThreadID) 192 | { 193 | if (id.x < _LUTWidth && id.y < _LUTHeight) 194 | { 195 | _TexOut[id.xy] = _LUT[uint3(id.xy, _MipLevel)]; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Shaders/SortedTextureToGaussian.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1b3786afca303f4e96df7b06faeffe5 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Shaders/TexturePreprocessor.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CopyTexture 3 | #pragma kernel SplitTextureAndIndex 4 | #pragma kernel AverageImage 5 | #pragma kernel ImageCovDiagonal 6 | #pragma kernel ImageCovUpper 7 | #pragma kernel MaxImage 8 | #pragma kernel MinImage 9 | #pragma kernel TransformColorsToEigenSpace 10 | #pragma kernel TransformColorsToBoundingBox 11 | #pragma kernel SwizzleColors 12 | #pragma kernel PopulateRGVariance 13 | #pragma kernel PopulateBAVariance 14 | #pragma kernel CalculateVariance 15 | 16 | #define NUM_THREADS_2D 32 17 | 18 | Texture2D _TexIn; 19 | Texture2D _TexIn2; 20 | RWTexture2D _TexOut; 21 | 22 | RWStructuredBuffer _Red; 23 | RWStructuredBuffer _Green; 24 | RWStructuredBuffer _Blue; 25 | RWStructuredBuffer _Alpha; 26 | RWStructuredBuffer _IndexR; 27 | RWStructuredBuffer _IndexG; 28 | RWStructuredBuffer _IndexB; 29 | RWStructuredBuffer _IndexA; 30 | 31 | RWStructuredBuffer _BufferAvg; 32 | 33 | 34 | uniform int _Mip; 35 | uniform uint _TexWidth; 36 | uniform uint _TexHeight; 37 | uniform uint _KernelWidth; 38 | uniform uint _KernelHeight; 39 | 40 | uniform float4 _EigenVector1; 41 | uniform float4 _EigenVector2; 42 | uniform float4 _EigenVector3; 43 | uniform float4 _MinColor; 44 | uniform float4 _MaxColor; 45 | uniform int4 _SwizzleMask; 46 | 47 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 48 | void CopyTexture(uint3 id: SV_DispatchThreadID) 49 | { 50 | uint2 uv = uint2(id.x, id.y); 51 | if (uv.x < _TexWidth && uv.y < _TexHeight) 52 | { 53 | _TexOut[uv] = _TexIn.Load(int3(uv, _Mip)); 54 | } 55 | } 56 | 57 | /* 58 | * Splits RBGA channels of _TexIn image into separate 1d compute buffers, 59 | * as well as storing the unrolled 1d coordinates of each pixel in 4 60 | * identical compute buffers corresponding to each color channel. 61 | */ 62 | [numthreads(1024,1,1)] 63 | void SplitTextureAndIndex (uint3 id : SV_DispatchThreadID) 64 | { 65 | if (id.x < _TexWidth * _TexHeight) 66 | { 67 | int x = id.x % _TexWidth; 68 | int y = id.x / _TexWidth; 69 | float4 colorIn = _TexIn.Load(int3(x, y, _Mip)); 70 | _Red[id.x] = colorIn.r; 71 | _Green[id.x] = colorIn.g; 72 | _Blue[id.x] = colorIn.b; 73 | _Alpha[id.x] = colorIn.a; 74 | _IndexR[id.x] = id.x; 75 | _IndexG[id.x] = id.x; 76 | _IndexB[id.x] = id.x; 77 | _IndexA[id.x] = id.x; 78 | } 79 | } 80 | 81 | /* AverageImage 82 | * 83 | * Iteratively computes the average color of an image by averaging _KernelWidth * _KernelHeight blocks of pixels, 84 | * and storing the results in the 1st (_TexWidth / _KernelWidth) by (_TexWidth / _KernelHeight) rectangle in the 85 | * output image. 86 | * 87 | * To use, repeatedly call the kernel and after each call swap the input an output rendertextures and divide 88 | * _TexWidth and _TexHeight by the kernel width and height respectively. Repeat until either _TexWidth or 89 | * _TexHeight is equal to the respective dimension of the kernel. Then set the kernel dimensions to the image 90 | * dimensions, and run the kernel one final time, during which it will copy the final average values computed 91 | * into the _BufferAvg buffer rather than back into the output texture. 92 | */ 93 | 94 | [numthreads(1024,1,1)] 95 | void AverageImage(uint3 id : SV_DispatchThreadID) 96 | { 97 | 98 | int isInBounds = id.x < ((_TexWidth * _TexHeight) / (_KernelWidth * _KernelHeight)); 99 | 100 | uint2 BlockCoords = uint2(id.x % (_TexWidth / _KernelWidth), id.x / ((_TexWidth / _KernelWidth))); 101 | float4 sum = float4(0.0, 0.0, 0.0, 0.0); 102 | 103 | if (isInBounds) 104 | { 105 | uint2 PixelCoords = BlockCoords * int2(_KernelWidth, _KernelHeight); 106 | 107 | for (uint i = 0; i < _KernelWidth; i++) 108 | { 109 | for (uint j = 0; j < _KernelHeight; j++) 110 | { 111 | sum += _TexIn[PixelCoords + uint2(i, j)]; 112 | } 113 | } 114 | } 115 | AllMemoryBarrierWithGroupSync(); 116 | if ((id.x == 0) && (_KernelWidth * _KernelHeight == _TexWidth * _TexHeight)) 117 | { 118 | float4 avg = sum / (_KernelWidth * _KernelHeight); 119 | _BufferAvg[0] = avg.r; 120 | _BufferAvg[1] = avg.g; 121 | _BufferAvg[2] = avg.b; 122 | _BufferAvg[3] = avg.a; 123 | } 124 | else if (isInBounds) 125 | { 126 | _TexOut[BlockCoords] = sum / (_KernelWidth * _KernelHeight); 127 | } 128 | } 129 | 130 | 131 | /* ImageCovDiagonal 132 | * 133 | * Computes the square of the color of each pixel minus the mean color for each pixel of _TexIn, 134 | * and stores it in _TexOut. This is used to compute the diagonal of the RGB covariance matrix. 135 | * 136 | */ 137 | [numthreads(1024, 1, 1)] 138 | void ImageCovDiagonal(uint id : SV_DispatchThreadID) 139 | { 140 | if (id.x < _TexWidth * _TexHeight) 141 | { 142 | int2 uv = int2(id.x % _TexWidth, id.x / _TexWidth); 143 | float4 texColor = _TexIn[uv]; 144 | float4 meanDifference = texColor - float4(_BufferAvg[0], _BufferAvg[1], _BufferAvg[2], _BufferAvg[3]); 145 | _TexOut[uv] = meanDifference * meanDifference; 146 | } 147 | } 148 | 149 | /* ImageCovDiagonal 150 | * 151 | * Computes (red - red average) * (green - green average), (green - green average) * (blue - blue average), 152 | * and (red - red average) * (blue - blue average) of eac pixel of _TexIn, and stores it in the rgb 153 | * components of _texOut. These values, when averaged, correspond to the upper 3 non-diagonal components 154 | * of the RGB covarience matrix (and the lower values, as the matrix is symmetric). 155 | * 156 | */ 157 | 158 | [numthreads(1024, 1, 1)] 159 | void ImageCovUpper(uint id : SV_DispatchThreadID) 160 | { 161 | if (id.x < _TexWidth * _TexHeight) 162 | { 163 | int2 uv = int2(id.x % _TexWidth, id.x / _TexWidth); 164 | float4 texColor = _TexIn[uv]; 165 | float4 meanDifference = texColor - float4(_BufferAvg[0], _BufferAvg[1], _BufferAvg[2], _BufferAvg[3]); 166 | _TexOut[uv] = float4(meanDifference.x * meanDifference.y, meanDifference.y * meanDifference.z, meanDifference.x * meanDifference.z, 1.0); 167 | } 168 | } 169 | 170 | /* MaxImage 171 | * 172 | * Iteratively computes the max of each color channel in _TexIn. Each thread computes the max of a _KernelWidth 173 | * by _KernelHeight block of pixels and stores the result in a pixel in the first _TexWidth / _KernelWidth, 174 | * _TexHeight / _KernelHeight block of _TexOut. The kernel should be called repeatedly, swapping the render- 175 | * textures assigned to _TexIn and _TexOut and dividing _TexWidth and _TexHeight by the kernel dimensions every 176 | * dispatch. Once _TexWidth and _TexHeight reach the size of the kernel, only one thread will execute and it will 177 | * instead output the max value to _BufferAvg 178 | * 179 | */ 180 | 181 | [numthreads(1024, 1, 1)] 182 | void MaxImage(uint3 id : SV_DispatchThreadID) 183 | { 184 | int isInBounds = id.x < ((_TexWidth * _TexHeight) / (_KernelWidth * _KernelHeight)); 185 | 186 | uint2 BlockCoords = uint2(id.x % (_TexWidth / _KernelWidth), id.x / ((_TexWidth / _KernelWidth))); 187 | float4 maxPix = float4(-1.0e125, -1.0e125, -1.0e125, -1.0e125); 188 | 189 | if (isInBounds) 190 | { 191 | uint2 PixelCoords = BlockCoords * int2(_KernelWidth, _KernelHeight); 192 | 193 | for (uint i = 0; i < _KernelWidth; i++) 194 | { 195 | for (uint j = 0; j < _KernelHeight; j++) 196 | { 197 | maxPix = max(_TexIn[PixelCoords + uint2(i, j)], maxPix); 198 | } 199 | } 200 | } 201 | AllMemoryBarrierWithGroupSync(); 202 | if ((id.x == 0) && (_KernelWidth * _KernelHeight == _TexWidth * _TexHeight)) 203 | { 204 | _BufferAvg[0] = maxPix.r; 205 | _BufferAvg[1] = maxPix.g; 206 | _BufferAvg[2] = maxPix.b; 207 | _BufferAvg[3] = maxPix.a; 208 | } 209 | else if (isInBounds) 210 | { 211 | _TexOut[BlockCoords] = maxPix; 212 | } 213 | } 214 | 215 | 216 | /* MinImage 217 | * 218 | * Iteratively computes the min of each color channel in _TexIn. Each thread computes the min of a _KernelWidth 219 | * by _KernelHeight block of pixels and stores the result in a pixel in the first _TexWidth / _KernelWidth, 220 | * _TexHeight / _KernelHeight block of _TexOut. The kernel should be called repeatedly, swapping the render- 221 | * textures assigned to _TexIn and _TexOut and dividing _TexWidth and _TexHeight by the kernel dimensions every 222 | * dispatch. Once _TexWidth and _TexHeight reach the size of the kernel, only one thread will execute and it will 223 | * instead output the min value to _BufferAvg 224 | * 225 | */ 226 | 227 | [numthreads(1024, 1, 1)] 228 | void MinImage(uint3 id : SV_DispatchThreadID) 229 | { 230 | int isInBounds = id.x < ((_TexWidth * _TexHeight) / (_KernelWidth * _KernelHeight)); 231 | 232 | uint2 BlockCoords = uint2(id.x % (_TexWidth / _KernelWidth), id.x / ((_TexWidth / _KernelWidth))); 233 | float4 minPix = float4(1.0e125, 1.0e125, 1.0e125, 1.0e125); 234 | 235 | if (isInBounds) 236 | { 237 | uint2 PixelCoords = BlockCoords * int2(_KernelWidth, _KernelHeight); 238 | 239 | for (uint i = 0; i < _KernelWidth; i++) 240 | { 241 | for (uint j = 0; j < _KernelHeight; j++) 242 | { 243 | minPix = min(_TexIn[PixelCoords + uint2(i, j)], minPix); 244 | } 245 | } 246 | } 247 | AllMemoryBarrierWithGroupSync(); 248 | if ((id.x == 0) && (_KernelWidth * _KernelHeight == _TexWidth * _TexHeight)) 249 | { 250 | _BufferAvg[0] = minPix.r; 251 | _BufferAvg[1] = minPix.g; 252 | _BufferAvg[2] = minPix.b; 253 | _BufferAvg[3] = minPix.a; 254 | } 255 | else if (isInBounds) 256 | { 257 | _TexOut[BlockCoords] = minPix; 258 | } 259 | } 260 | 261 | /* TransformColorsToEigenSpace 262 | * 263 | * Treats the RGB colors stored in _TexOut as 3d coordinates and transforms them to the space using 264 | * _EigenVector1, _EigenVector2, and _EigenVector3 as its basis vectors 265 | * 266 | */ 267 | 268 | [numthreads(1024, 1, 1)] 269 | void TransformColorsToEigenSpace(uint3 id : SV_DispatchThreadID) 270 | { 271 | int2 uv = int2(id.x % _TexWidth, id.x / _TexWidth); 272 | float4 color = _TexOut[uv]; 273 | float4 colorNew = float4(0, 0, 0, color.a); 274 | colorNew.r = dot(color.rgb, float3(_EigenVector1[0], _EigenVector1[1], _EigenVector1[2])); 275 | colorNew.g = dot(color.rgb, float3(_EigenVector2[0], _EigenVector2[1], _EigenVector2[2])); 276 | colorNew.b = dot(color.rgb, float3(_EigenVector3[0], _EigenVector3[1], _EigenVector3[2])); 277 | _TexOut[uv] = colorNew; 278 | } 279 | 280 | 281 | /* TransformColorsToBoundingBox 282 | * 283 | * Treats the RGB colors stored in _TexOut as 3d coordinates and transforms them to a new space that is 284 | * translated so that _MinColor is the center, and that is scaled on each axis so that the axis has a length 285 | * of the _MaxColor - _MinColor corresponding to that axis. This makes it so that each channel's values 286 | * cover the 0 to 1 range. 287 | * 288 | */ 289 | 290 | [numthreads(1024, 1, 1)] 291 | void TransformColorsToBoundingBox(uint3 id : SV_DispatchThreadID) 292 | { 293 | int2 uv = int2(id.x % _TexWidth, id.x / _TexWidth); 294 | float4 color = _TexOut[uv]; 295 | float3 min = float3(_MinColor[0], _MinColor[1], _MinColor[2]); 296 | float3 max = float3(_MaxColor[0], _MaxColor[1], _MaxColor[2]); 297 | float3 length = max - min; 298 | color.rgb = length == 0 ? float3(0,0,0) : (color.rgb - min) / length; 299 | _TexOut[uv] = color; 300 | } 301 | 302 | /* SwizzleColors 303 | * 304 | * Rearranges the color channels in _TexOut to match the index order stored in _SwizzleMask 305 | * 306 | */ 307 | 308 | [numthreads(1024, 1, 1)] 309 | void SwizzleColors(uint3 id : SV_DispatchThreadID) 310 | { 311 | int2 uv = int2(id.x % _TexWidth, id.x / _TexWidth); 312 | float4 color = _TexOut[uv]; 313 | _TexOut[uv] = float4(color[_SwizzleMask[0]], color[_SwizzleMask[1]], color[_SwizzleMask[2]], color[_SwizzleMask[3]]); 314 | } 315 | 316 | /* PopulateRGVariance 317 | * 318 | * Calculates R*R and G*G of each pixel of _TexIn, and stores R, R*R, G, G*G in the corresponding 319 | * pixel of _TexOut. Used to prepare for calculating the variance of blocks of pixels during creation 320 | * of the filtered LUT 321 | * 322 | */ 323 | 324 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 325 | void PopulateRGVariance(uint3 id : SV_DispatchThreadID) 326 | { 327 | if (id.x < _TexWidth && id.y < _TexHeight) 328 | { 329 | float4 color = _TexIn.Load(int3(id.xy, 0)); 330 | _TexOut[id.xy] = float4(color.r, color.r * color.r, color.g, color.g * color.g); 331 | } 332 | } 333 | 334 | 335 | /* PopulateRGVariance 336 | * 337 | * Calculates B*B and A*A of each pixel of _TexIn, and stores B, B*B, A, A*A in the corresponding 338 | * pixel of _TexOut. Used to prepare for calculating the variance of blocks of pixels during creation 339 | * of the filtered LUT 340 | * 341 | */ 342 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 343 | void PopulateBAVariance(uint3 id : SV_DispatchThreadID) 344 | { 345 | if (id.x < _TexWidth && id.y < _TexHeight) 346 | { 347 | float4 color = _TexIn.Load(int3(id.xy, 0)); 348 | _TexOut[id.xy] = float4(color.b, color.b * color.b, color.a, color.a * color.a); 349 | } 350 | } 351 | 352 | /* CalculateVariance 353 | * 354 | * Calculates the variance from the outputs of populateRGVariance and populateBGVariance. 355 | * 356 | */ 357 | [numthreads(NUM_THREADS_2D, NUM_THREADS_2D, 1)] 358 | void CalculateVariance(uint3 id : SV_DispatchThreadID) 359 | { 360 | if (id.x < _TexWidth && id.y < _TexHeight) 361 | { 362 | float4 RGVar = _TexIn[id.xy]; 363 | float4 BAVar = _TexIn2[id.xy]; 364 | float4 output = float4(RGVar.y - RGVar.x * RGVar.x, RGVar.w - RGVar.z * RGVar.z, 365 | BAVar.y - BAVar.x * BAVar.x, BAVar.w - BAVar.z * BAVar.z); 366 | output = max(0.0, output); 367 | _TexOut[id.xy] = output; 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /GaussianTex/Editor/Shaders/TexturePreprocessor.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6e9abb5ff65ce243893086d1bfa781b 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/demo.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1ba980be2499383418dc47e078a5c55c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/demo/materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 374ee86371067ea459cf961de9e3fc64 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/demo/materials/Blending Comparison Demo.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: Blending Comparison Demo 11 | m_Shader: {fileID: 4800000, guid: 7a9317eaaf59b894b820e8cccf1a33f5, type: 3} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _LUTTex: 43 | m_Texture: {fileID: 18700000, guid: 9f4c4c7f4b1c6f448bdc369f9add9b0d, type: 2} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MainTex: 47 | m_Texture: {fileID: 2800000, guid: 40824fb20e843b745b9d7e472eacdace, type: 3} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _MainTex2: 51 | m_Texture: {fileID: 2800000, guid: 3a9d854077c03ba40b34c0ef4f80da2d, type: 3} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _MetallicGlossMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | - _OcclusionMap: 59 | m_Texture: {fileID: 0} 60 | m_Scale: {x: 1, y: 1} 61 | m_Offset: {x: 0, y: 0} 62 | - _ParallaxMap: 63 | m_Texture: {fileID: 0} 64 | m_Scale: {x: 1, y: 1} 65 | m_Offset: {x: 0, y: 0} 66 | m_Floats: 67 | - _BumpScale: 1 68 | - _Cutoff: 0.5 69 | - _DetailNormalMapScale: 1 70 | - _DstBlend: 0 71 | - _GOn: 2 72 | - _GlossMapScale: 1 73 | - _Glossiness: 0.5 74 | - _GlossyReflections: 1 75 | - _Metallic: 0 76 | - _Mip: 0 77 | - _Mode: 0 78 | - _OcclusionStrength: 1 79 | - _Parallax: 0.02 80 | - _SmoothnessTextureChannel: 0 81 | - _SpecularHighlights: 1 82 | - _SrcBlend: 1 83 | - _UVSec: 0 84 | - _ZWrite: 1 85 | m_Colors: 86 | - _CX: {r: 0.017838398, g: -0.1237444, b: 0.35707602, a: 1} 87 | - _CY: {r: 0.7077192, g: 0.578709, b: 0.1651957, a: 1} 88 | - _CZ: {r: -0.32468608, g: 0.35711047, b: 0.13997664, a: 1} 89 | - _Center: {r: 0.16525924, g: -0.15559298, b: -0.15682475, a: 0} 90 | - _Color: {r: 1, g: 1, b: 1, a: 1} 91 | - _CsCenter: {r: 0.16619706, g: -0.1369825, b: -0.18609843, a: 0} 92 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 93 | -------------------------------------------------------------------------------- /GaussianTex/demo/materials/Blending Comparison Demo.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 626a4aa6c491df64a94d176d829c5a49 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/demo/textures.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 73a8a5a360b8a4c42a11b0ff3bced2ba 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Error-mdl/UnityGaussianTex/51f5ba11b5390e352d3581cb41ec3c5dd6fdb807/GaussianTex/demo/textures/mossdemo.jpg -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3a9d854077c03ba40b34c0ef4f80da2d 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: 1 35 | aniso: 1 36 | mipBias: 0 37 | wrapU: 0 38 | wrapV: 0 39 | wrapW: 0 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | spriteSheet: 75 | serializedVersion: 2 76 | sprites: [] 77 | outline: [] 78 | physicsShape: [] 79 | bones: [] 80 | spriteID: 81 | internalID: 0 82 | vertices: [] 83 | indices: 84 | edges: [] 85 | weights: [] 86 | secondaryTextures: [] 87 | spritePackingTag: 88 | pSDRemoveMatte: 0 89 | pSDShowRemoveMatteOption: 0 90 | userData: 91 | assetBundleName: 92 | assetBundleVariant: 93 | -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo_colorspace.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &11400000 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: e273ecb3e5512c84898715e24802c7c6, type: 3} 13 | m_Name: mossdemo_colorspace 14 | m_EditorClassIdentifier: 15 | Axis0: {x: 0.017838398, y: -0.1237444, z: 0.35707602, w: 1} 16 | Axis1: {x: 0.7077192, y: 0.578709, z: 0.1651957, w: 1} 17 | Axis2: {x: -0.32468608, y: 0.35711047, z: 0.13997664, w: 1} 18 | Center: {x: 0.16619706, y: -0.1369825, z: -0.18609843, w: 0} 19 | -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo_colorspace.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df0bcaf89508bde47a6820ecfa360a8e 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 11400000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo_gauss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Error-mdl/UnityGaussianTex/51f5ba11b5390e352d3581cb41ec3c5dd6fdb807/GaussianTex/demo/textures/mossdemo_gauss.jpg -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo_gauss.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40824fb20e843b745b9d7e472eacdace 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 0 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: 1 35 | aniso: 1 36 | mipBias: 0 37 | wrapU: 0 38 | wrapV: 0 39 | wrapW: 0 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | spriteSheet: 75 | serializedVersion: 2 76 | sprites: [] 77 | outline: [] 78 | physicsShape: [] 79 | bones: [] 80 | spriteID: 81 | internalID: 0 82 | vertices: [] 83 | indices: 84 | edges: [] 85 | weights: [] 86 | secondaryTextures: [] 87 | spritePackingTag: 88 | pSDRemoveMatte: 0 89 | pSDShowRemoveMatteOption: 0 90 | userData: 91 | assetBundleName: 92 | assetBundleVariant: 93 | -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo_lut.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!187 &18700000 4 | Texture2DArray: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: mossdemo_lut 10 | m_ImageContentsHash: 11 | serializedVersion: 2 12 | Hash: 00000000000000000000000000000000 13 | m_ForcedFallbackFormat: 4 14 | m_DownscaleFallback: 0 15 | serializedVersion: 2 16 | m_ColorSpace: 0 17 | m_Format: 7 18 | m_Width: 16 19 | m_Height: 16 20 | m_Depth: 11 21 | m_MipCount: 1 22 | m_DataSize: 8448 23 | m_TextureSettings: 24 | serializedVersion: 2 25 | m_FilterMode: 1 26 | m_Aniso: 1 27 | m_MipBias: 0 28 | m_WrapU: 0 29 | m_WrapV: 0 30 | m_WrapW: 0 31 | m_IsReadable: 1 32 | image data: 8448 33 | _typelessdata: a3a5c4a0a1c29d9fc09b9bbe9899bc9697ba9495b89393b79291b5908fb48f8db38e8bb28d89b08c88af8b86ad8a85ad8983ac8882ab8781aa8680a9857ea8847da7847ba6837aa58279a48178a48077a37f75a27e74a17e73a17d72a07c71a07b709f7a6f9e796e9e786d9d786c9d776b9c776a9c76699b75689b75679a74669a7365997364997264987263987162977161977060976f5f966f5e966e5d956e5c956d5b956d5b946c5a946c59946c58936b57936b56936a56926a5592695492695391695291685191685191675090674f90674e90664d8f664c8f654c8f654b8f654a8e644a8e64498e64488d63478d63478d63468d62458c62458c62448c62438c61438b61428b61418b60418b60408a60408a5f3f8a5f3e8a5f3e895f3d895e3d895e3c895e3b895e3b885d3b885d3a885d39885c39875c38875c38875c37875b37875b36865b36865b35865a35865a34865a34855a3485593385593385593285593285583184583184583084583084572f84572f83572e83572e83562d83562d83562c83562c82552b82552b82552a82552a8254298254298154288153288153278153278153268052268052258052258051248051247f51237f51237f50227f50227f50227e4f217e4f217e4f207e4f207e4e1f7d4e1f7d4e1e7d4d1e7d4d1e7c4d1d7c4d1d7c4c1c7c4c1c7b4c1b7b4b1b7b4b1b7b4b1a7a4a1a7a4a197a4a19794a197949187949187849187848177848177848177747167747167647167647157646157546157546147445147445147445147344137344137244127243127243127143127043117042116f42116f42116e41106e41106d41106d40106c400f6c3f0f6b3f0f6a3f0f6a3f0e693e0e693e0e683d0d673d0d663c0d663c0d653c0c643c0c633b0c623b0c613a0c603a0c5f390b5e390b5d390b5c380b5b380b5b370a5a370a59360a58360a56360a55350a5534095434095333095233095032095032094e31094e30084d30084c2f084a2f08492e08482e07472e07462d07452c07432b07422a07412a063f29063e29063c27063a2606392505382405359994bc9894bb9893ba9792ba9691b99590b8948fb7938eb6928db5918cb4908bb3908ab38f89b28e88b18d87b08c86af8b85ae8a84ad8983ac8882ab8781aa867fa9857ea8847da8837ca7827ba6827aa58179a48078a47f77a37e76a27d74a17d73a17c72a07b719f7a709f7a6f9e796e9d786d9d776c9c776b9c766a9b75699b75689a74679a7366997365997264987263987162977161977060976f5f966f5e966e5d956e5c956d5c956d5b946c5a946c59946b58936b57936b56926a56926a5592695492695391685291685291685190675090674f90664e8f664e8f664d8f654c8f654b8e654b8e644a8e64498d64498d63488d63478d63478c62468c62458c62458c61448b61438b61438b60428b60418a60418a5f408a5f3f8a5f3f895f3e895e3e895e3d895e3c895d3c885d3b885d3b885d3a885c3a875c39875c38875c38875b37875b37865b36865a36865a35865a35865a3485593485593385593285593285583184583184583084583084572f84572f83572e83572e83562d83562d83562c83552c82552b82552b82552a82542a82542a8154298154298153288153288153278052278052268052268052258051257f51247f51247f50247f50237f50237e50227e4f227e4f217e4f217d4e207d4e207d4e207d4e1f7c4d1f7c4d1e7c4d1e7c4c1e7b4c1d7b4c1d7b4c1c7b4b1c7a4b1c7a4b1b7a4a1b794a1a794a1a79491a7949197849197849197848187748187748187647177647177647177546167546167546167445157445157345157345147244147244147144137143137043137043126f42126f42126e42126e41116d41116d41116c40116b40106b3f106a3f106a3f10693e0f683e0f683e0f673d0f663d0e653c0e653c0e643c0e633b0d623b0d613a0d613a0d603a0c5f390c5e390c5d380c5c380c5b370b5b370b5a360b59360b58360b57350b56350a55340a54340a53330a52330a5132095032094f31094e31094d30094c30094b2f094a2f08492e08482e08472d08462d08452c08442c08432b07422b07422a07412a074029073f29073e29073e28073d9791ba9690b9968fb9958eb8948eb7948db7938cb6928bb5918ab49089b49089b38f88b28e87b18d86b08c85b08c84af8b83ae8a82ad8981ac8880ac877fab877faa867ea9857da8847ca8837ba7827aa68279a58178a58077a47f76a37e75a37e74a27d73a17c72a17b71a07b709f7a6f9f796e9e796d9e786c9d776b9c766a9c76699b75689b75679a74669a7365997364997264987263987162977061977060966f5f966f5e966e5d956e5c956d5b946d5b946c5a946c59936b58936b57936a56926a5692695592695491695391685291685290675190675090674f8f664e8f664e8f654d8e654c8e654b8e644b8e644a8d64498d63498d63488d63478c62468c62468c62458c61448b61448b61438b60428b60428a60418a5f408a5f408a5f3f895e3f895e3e895e3d895e3d885d3c885d3c885d3b885c3a885c3a875c39875c39875b38875b38875b37865b36865a36865a35865a3585593485593485593385593385583284583284583184583184573084572f83572f83562e83562e83562d83562d83552c82552c82552b82552b82542b82542a81542a8153298153298153288053288052278052278052268052267f51257f51257f51257f50247f50247e50237e50237e4f227e4f227d4f227d4e217d4e217d4e207c4e207c4d1f7c4d1f7c4d1f7b4c1e7b4c1e7b4c1d7a4c1d7a4b1d7a4b1c7a4b1c794a1c794a1b794a1b78491a78491a78491a7748197748197748197648187647187547187547177546177446177446167345167345167245157244157244157144147143147043147043136f42136f42136e42136d41126d41126c41126c40126b40116a40116a3f11693f10683e10683e10673e10663d0f663d0f653c0f643c0f643c0e633b0e623b0e613a0e603a0e603a0d5f390d5e390d5d380d5c380d5b370c5b370c5a370c59360c58360c57350b56350b55340b54340b54330b53330a52330a51320a50320a4f310a4e310a4d30094c30094b2f094b2f094a2e09492e09482e09472d09462d08462c08452c08442c08432b08432b08422b08412a08402a074029073f948eb7948eb6938db6938cb5928bb5918bb4918ab49089b38f88b28f88b28e87b18d86b18d85b08c84af8b84af8b83ae8a82ae8981ad8980ac887fac877eab877eaa867daa857ca9847ba9847aa88379a78278a78277a68176a68076a58075a47f74a47e73a37d72a37d71a27c70a17b6fa17b6ea07a6da0796c9f796c9f786b9e776a9d77699d76689c76679c75669b74659b74649a73639a736399726299726198716098705f98705e976f5d976f5d966e5c966e5b956d5a956d59956c58946c58946b57936b56936a55936a5492695492695392695291685191685091675090674f90664e90664d8f664d8f654c8f654b8e644a8e644a8e64498d63488d63488d63478d62468c62468c62458c61448b61448b61438b60428b60428a60418a5f408a5f408a5f3f895e3e895e3e895e3d895d3c885d3c885d3b885c3b885c3a875c39875c39875b38875b38875b37865a37865a36865a35865a3585593485593485593385583385583284583284583184573184573083573083562f83562f83562e83562e82552d82552d82552c82552c81542b81542b81542a81532a8153298053298053288052288052277f52277f51267f51267f51267e51257e50257e50247e50247d4f237d4f237d4f237d4f227c4e227c4e217c4e217b4d207b4d207b4d207a4d1f7a4c1f7a4c1e7a4c1e794b1e794b1d794b1d784a1d784a1c774a1c77491b77491b76491b76491a76481a75481a7548197447197447197447187346187346187246177245177145177145167044167044166f44156f43156e43156e43146d42146d42146c42146c41136b41136a40136a4012694012693f12683f12673f11673e11663e11653d11653d10643d10643c10633c10623c0f613b0f613b0f603a0f5f3a0e5f3a0e5e390e5d390e5d380e5c380d5b380d5a370d5a370d59360d58360c57360c57350c56350c55340c54340b54340b53330b52330b51320b51320b50320a4f310a4f310a4e300a4d300a4c300a4c2f094b2f094a2f094a2e09492e09482e09482d09472d09462d08462c08452c08452c08442b08442b0843938db5928cb5928cb5918bb4918ab4908ab39089b38f88b28e87b28e87b18d86b08d85b08c84af8b84af8b83ae8a82ae8a81ad8980ad887fac887fac877eab867daa867caa857ba9847aa9847aa88379a88278a78277a78176a68175a58074a57f74a47f73a47e72a37d71a37d70a27c6fa27b6ea17b6da07a6da07a6c9f796b9f786a9e78699e77689d77679d76669c75669c75659b74649b74639a73629a72619a726099716099715f98705e98705d976f5c976f5b966e5b966e5a966d59956d58956c57946c57946b56946b55936a54936a5392695392695292695191685091685091674f90674e90664d90664d8f664c8f654b8f654a8e644a8e64498e64488d63488d63478d63468d62458c62458c61448c61438b61438b60428b60418b60418a5f408a5f408a5f3f8a5e3e895e3e895e3d895d3c885d3c885d3b885c3b885c3a875c39875c39875b38875b38875b37865a37865a36865a3586593585593485593485593385583384583284583284573184573183573083572f83562f83562e82562e82552d82552d82552c82552c81542b81542b81542b81532a80532a8053298053297f52287f52287f52277f51277e51267e51267e50257e50257d50257d50247d4f247c4f237c4f237c4e227c4e227b4e227b4e217b4d217a4d207a4d207a4c20794c1f794c1f794b1e784b1e784b1e784b1d774a1d774a1c764a1c76491c76491b75491b75481b74481a74481a74471a7347197347197246197246187146187145187045177045177044176f44166f44166e43166d43156d43156c42156c42146b42146b41146a41146a4113694013694013683f12673f12673f12663e12663e11653e11643d11643d11633c10633c10623c10613b10613b0f603b0f5f3a0f5f3a0f5e390f5d390e5d390e5c380e5b380e5b380d5a370d59370d59360d58360d57360c57350c56350c55350c55340c54340c53330b53330b52330b51320b51320b50320b4f310a4f310a4e310a4d300a4d300a4c300a4c2f0a4b2f094a2f094a2e09492e09492e09482d09472d09472d09462c08462c08452c0845928db5928cb4918bb4918ab3908ab38f89b28f88b28e88b18e87b18d86b08d85b08c85af8c84af8b83ae8a82ae8a82ad8981ad8980ac887fac877eab877eab867daa867caa857ba9847aa98479a88379a88278a78277a78176a68175a68074a57f73a47f73a47e72a37e71a37d70a27c6fa27c6ea17b6da17b6da07a6ca0796b9f796a9f78699e78689e77679d76679d76669c75659c75649b74639b74629b73619a73619a726099715f99715e98705d98705c976f5c976f5b976e5a966e59966d58956d58956c57956c56946b55946b54936a54936a53936a5292695192695192685091684f91674e91674e90674d90664c8f664b8f654b8f654a8f65498e64498e64488e63478d63468d63468d62458c62448c61448c61438b61428b60428b60418b60408a5f408a5f3f8a5f3f8a5e3e895e3d895e3d895d3c885d3b885d3b885c3a885c3a875c39875b38875b38875b37865a37865a36865a36865a3585593585593485593385583384583284583284573184573183573083573083562f83562f82562e82552e82552d82552d81552c81542c81542b81542b80532a80532a8053297f52297f52287f52287f52277e51277e51277e51267e50267d50257d50257d50247c4f247c4f237c4f237b4e237b4e227b4e227a4d217a4d217a4d21794d20794c20794c1f784c1f784b1f784b1e774b1e774a1d774a1d764a1d76491c75491c75491c75481b74481b74481a73471a73471a7247197247197246197146187146187045187045176f45176f44176e44166e43166d43166d43166c42156c42156b42156b41146a41146a4114694013684013684013673f13673f12663f12663e12653e12643d11643d11633d11633c11623c10613c10613b10603b10603b0f5f3a0f5e3a0f5e390f5d390e5c390e5c380e5b380e5b380e5a370d59370d59370d58360d57360d57350c56350c55350c55340c54340c54340c53330b52330b52330b51320b50320b50320b4f310a4f310a4e310a4d300a4d300a4c300a4c2f0a4b2f094b2f094a2e09492e09492e09482d09482d09472d09472d08462c0846918cb4918cb4908bb3908ab38f8ab28f89b28e88b28e87b18d87b18d86b08c85b08c84af8b84af8b83ae8a82ae8981ad8981ad8880ac887fac877eab877dab867daa857caa857ba9847aa98479a88378a88278a78277a78176a68175a68074a57f73a57f73a47e72a47e71a37d70a37d6fa27c6ea27b6da17b6da17a6ca07a6ba0796a9f79699f78689e77679e77679d76669d76659c75649c75639b74629b74629a73619a72609a725f99715e99715d98705d98705c976f5b976f5a976e59966e59966d58956d57956c56956c55946b55946b54936b53936a52936a5192695192695092684f91684e91674e91674d90674c90664c90664b8f654a8f65498f65498e64488e64478e63478d63468d63458d62458c62448c62438c61438b61428b60418b60418b60408a5f3f8a5f3f8a5f3e895e3d895e3d895e3c895d3c885d3b885d3a885c3a885c39875c39875b38875b37865b37865a36865a36865a3585593585593485593485583384583284583284583184573183573083573083562f83562f82562e82552e82552d81552d81552c81542c81542b80542b80532a80532a8053297f52297f52287f52287e52287e51277e51277e51267d50267d50257d50257c4f247c4f247c4f247b4f237b4e237b4e227a4e227a4d217a4d21794d21794c20794c20784c1f784b1f784b1f774b1e774b1e764a1e764a1d764a1d75491c75491c74491c74481b74481b73481b73471a72471a72471a7146197146197146197045187045186f45186f44176e44176e44176d43166d43166c43166c42156b42156b42156a41146a4114694114694014684013674013673f13663f12663e12653e12653e12643d11633d11633d11623c11623c10613c10613b10603b105f3b0f5f3a0f5e3a0f5e390f5d390f5c390e5c380e5b380e5a380e5a370e59370d59370d58360d57360d57360d56350c56350c55350c54340c54340c53340b53330b52330b51330b51320b50320b50320a4f310a4e310a4e310a4d300a4d300a4c300a4c2f094b2f094b2f094a2e09492e09492e09482e09482d09472d09472d0846918cb4918bb4908bb3908ab38f89b28f89b28e88b18d87b18d87b08c86b08c85af8b84af8b84af8a83ae8a82ae8981ad8980ad8880ac887fac877eab867dab867daa857caa857ba9847aa98479a88378a88278a78277a78176a68175a68074a58073a57f73a47e72a47e71a37d70a37d6fa27c6ea27c6da17b6da17a6ca07a6ba0796a9f79699f78689e78679e77679d76669d76659d75649c75639c74629b74629b73619a73609a725f99725e99715d99715d98705c98705b976f5a976f59966e59966e58966d57956d56956c55946c55946b54946b53936a52936a5293695192695092694f91684f91684e91674d90674c90664c90664b8f664a8f65498f65498e64488e64478e64478d63468d63458d62458c62448c62438c61438c61428b61418b60418b60408a603f8a5f3f8a5f3e8a5e3e895e3d895e3c895d3c885d3b885d3a885c3a885c39875c39875b38875b38865b37865a36865a36865a3585593585593485593485593384583384583284583184573183573083573083562f82562f82562e82552e82552d81552d81552c81542c81542b80542b80532a80532a7f53297f52297f52297f52287e52287e51277e51277d51267d50267d50257c50257c4f247c4f247b4f247b4f237b4e237b4e227a4e227a4d22794d21794d21794c20784c20784c1f784b1f774b1f774b1e774a1e764a1e764a1d754a1d75491c75491c74491c74481b73481b73481b72471a72471a72471a7146197146197046197045186f45186f45186e44176e44176d44176d43166c43166c43166b42156b42156a42156a4115694114694114684014684013673f13673f13663f13663e12653e12643e12643d11633d11633d11623c11623c10613c10603b10603b105f3b105f3a0f5e3a0f5d390f5d390f5c390e5c380e5b380e5a380e5a370e59370d59370d58360d57360d57360d56350c56350c55350c54340c54340c53340c53330b52330b52330b51320b50320b50320b4f310a4f310a4e310a4e300a4d300a4c300a4c300a4b2f094b2f094a2f094a2e09492e09492e09482e09482d09472d0847918cb4908bb3908bb38f8ab38f89b28e89b28e88b18d87b18d86b08c86b08c85af8b84af8b84ae8a83ae8a82ae8981ad8980ad8880ac877fac877eab867dab867caa857caa857ba9847aa98479a88378a88278a78277a78176a68175a68074a58073a57f73a47e72a47e71a37d70a37d6fa27c6ea27c6da17b6da17a6ca07a6ba0796a9f79699f78689e78679e77679e77669d76659d75649c75639c74629b74629b73619a73609a725f99725e99715d99715d98705c98705b976f5a976f59976e59966e58966d57956d56956c55956c55946b54946b53936b52936a52936a5192695092694f92684f91684e91674d90674c90674c90664b8f664a8f65498f65498e65488e64478e64478d63468d63458d63458d62448c62438c61438c61428b61418b60418b60408a603f8a5f3f8a5f3e8a5f3e895e3d895e3c895e3c885d3b885d3a885c3a885c39875c39875b38875b38875b37865b36865a36865a35855a3585593485593485593384583384583284583184573183573083573083562f82562f82562e82552e82552d81552d81552c81542c81542b80542b80532a80532a7f53297f52297f52297e52287e52287e51277e51277d51267d50267d50257c50257c4f247c4f247b4f247b4e237b4e237a4e227a4e227a4d22794d21794d21794c20784c20784c20784b1f774b1f774b1e764a1e764a1e764a1d75491d75491c74491c74491c74481b73481b73481b72471a72471a71471a7146197146197046197045186f45186f45186e44176e44176d44176d43166c43166c43166b42156b42156a42156a4115694114694014684014684013673f13673f13663f13653e12653e12643e12643d11633d11633d11623c11613c10613c10603b10603b105f3b105f3a0f5e3a0f5d3a0f5d390f5c390e5c380e5b380e5a380e5a370e59370d59370d58360d57360d57360d56350c56350c55350c55340c54340c53340c53330b52330b52330b51320b50320b50320b4f310a4f310a4e310a4e310a4d300a4d300a4c300a4c2f094b2f094a2f094a2f09492e09492e09482e09482d09472d0847918cb4908bb3908bb38f8ab28f89b28e89b28e88b18d87b18d86b08c86b08c85af8b84af8b83ae8a83ae8a82ad8981ad8880ad8880ac877fac877eab867dab867caa857caa857ba9847aa98379a88378a88278a78277a78176a68175a68074a58073a57f73a47e72a47e71a37d70a37d6fa27c6ea27c6da17b6da17a6ca07a6ba0796a9f79699f78689e78679e77679e77669d76659d76649c75639c74629b74629b73619a73609a725f9a725e99715d99715d98705c98705b976f5a976f59976e59966e58966d57956d56956c55956c55946c54946b53936b52936a52936a5192695092694f92684f91684e91684d91674c90674c90664b90664a8f654a8f65498f65488e64478e64478e63468d63458d63458d62448c62438c62438c61428b61418b60418b60408a603f8a5f3f8a5f3e8a5f3e895e3d895e3c895e3c885d3b885d3b885d3a885c39875c39875c38875b38875b37865b36865a36865a35855a3585593485593485593384583384583284583184573183573083573083562f82562f82562e82552e82552d81552d81552c81542c80542b80542b80532a80532a7f53297f52297f52297e52287e52287e51277e51277d51267d50267d50257c50257c4f247c4f247b4f247b4e237b4e237a4e227a4e227a4d22794d21794d21794c20784c20784c20784b1f774b1f774b1e764a1e764a1e764a1d75491d75491c74491c74481c74481b73481b73481b72471a72471a71471a7146197146197046197045186f45186f45186e44176e44176d44176d43166c43166c43166b42156b42156a42156a4115694114694014684014684013673f13673f13663f13653e12653e12643e12643d12633d11633d11623c11613c11613c10603b10603b105f3b105f3a0f5e3a0f5d3a0f5d390f5c390e5c380e5b380e5a380e5a370e59370d59370d58360d57360d57360d56350c56350c55350c55340c54340c53340c53330b52330b52330b51320b51320b50320b4f320a4f310a4e310a4e310a4d300a4d300a4c300a4c2f094b2f094b2f094a2f094a2e09492e09492e09482e09482d0847918cb4908bb3908bb38f8ab28f89b28e89b28e88b18d87b18d86b08c86b08c85af8b84af8b83ae8a83ae8a82ad8981ad8880ad8880ac877fac877eab867dab867caa857caa857ba9847aa98379a88378a88278a78277a78176a68175a68074a58073a57f73a47e72a47e71a37d70a37d6fa27c6ea27c6da17b6da17a6ca07a6ba0796a9f79699f78689e78689e77679e77669d76659d76649c75639c74629b74629b73619a73609a725f9a725e99715d99715d98705c98705b976f5a976f59976e59966e58966d57956d56956c55956c55946c54946b53936b52936a52936a5192695092694f92684f91684e91684d91674c90674c90664b90664a8f654a8f65498f65488e64478e64478e63468d63458d63458d62448c62438c62438c61428b61418b60418b60408a603f8a5f3f8a5f3e8a5f3e895e3d895e3c895e3c885d3b885d3b885d3a885c39875c39875c38875b38875b37865b36865a36865a35855a3585593485593485593384583384583284583184573183573083573083562f82562f82562e82552e82552d81552d81552c81542c80542b80542b80532a80532a7f53297f52297f52297e52287e52287e51277e51277d51267d50267d50257c50257c4f247c4f247b4f247b4e237b4e237a4e227a4e227a4d22794d21794d21794c20784c20784c20774b1f774b1f774b1e764a1e764a1e764a1d75491d75491c74491c74481c74481b73481b73481b72471a72471a71471a7146197046197046197045186f45186f45186e44176e44176d44176d43166c43166c43166b42156b42156a42156a4115694114694014684014684013673f13663f13663f13653e12653e12643e12643d12633d11633d11623c11613c11613c10603b10603b105f3b105f3a0f5e3a0f5d3a0f5d390f5c390e5c380e5b380e5a380e5a370e59370d59370d58360d57360d57360d56350c56350c55350c55340c54340c53340c53330b52330b52330b51320b51320b50320b4f320a4f310a4e310a4e310a4d300a4d300a4c300a4c2f094b2f094b2f094a2f094a2e09492e09492e09482e09482d0847 34 | m_StreamData: 35 | offset: 0 36 | size: 0 37 | path: 38 | -------------------------------------------------------------------------------- /GaussianTex/demo/textures/mossdemo_lut.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f4c4c7f4b1c6f448bdc369f9add9b0d 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 18700000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b376f952ef9615449bf2996089ef3e0 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/shaders/Demo.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6728e3b5f7f99248b92eeedb2bff98f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/shaders/Demo/BlendDemo.shader: -------------------------------------------------------------------------------- 1 | Shader "Error.mdl/Blending Demo" 2 | { 3 | Properties 4 | { 5 | _MainTex ("Gaussian Texture", 2D) = "white" {} 6 | _MainTex2 ("Original Texture", 2D) = "black" {} 7 | _LUTTex("Lookup Table Texture", 2DArray) = "white" {} 8 | _CsCenter("Colorspace Center", Vector) = (0,0,0,0) 9 | _CX("Colorspace X", Vector) = (1, 0, 0, 1) 10 | _CY("Colorspace Y", Vector) = (0, 1, 0, 1) 11 | _CZ("Colorspace Z", Vector) = (0, 0, 1, 1) 12 | [Enum(Gaussian Blend, 2, Linear Blend, 1, None, 0)] _GOn ("Blend Mode", int) = 1 13 | } 14 | SubShader 15 | { 16 | Tags { "RenderType"="Opaque" } 17 | LOD 100 18 | 19 | Pass 20 | { 21 | CGPROGRAM 22 | #pragma vertex vert 23 | #pragma fragment frag 24 | // make fog work 25 | #pragma multi_compile_fog 26 | #pragma target 5.0 27 | 28 | #include "UnityCG.cginc" 29 | #include "../GaussianBlend.cginc" 30 | 31 | struct appdata 32 | { 33 | float4 vertex : POSITION; 34 | float2 uv : TEXCOORD0; 35 | }; 36 | 37 | struct v2f 38 | { 39 | float2 uv : TEXCOORD0; 40 | UNITY_FOG_COORDS(1) 41 | float4 vertex : SV_POSITION; 42 | }; 43 | 44 | UNITY_DECLARE_TEX2D(_MainTex); 45 | sampler2D _MainTex2; 46 | 47 | Texture2DArray _LUTTex; 48 | float4 _LUTTex_TexelSize; 49 | float4 _MainTex_ST; 50 | int _GOn; 51 | float4 _CsCenter; 52 | float4 _CX; 53 | float4 _CY; 54 | float4 _CZ; 55 | float _Mip; 56 | 57 | v2f vert (appdata v) 58 | { 59 | v2f o; 60 | o.vertex = UnityObjectToClipPos(v.vertex); 61 | o.uv = TRANSFORM_TEX(v.uv, _MainTex); 62 | UNITY_TRANSFER_FOG(o,o.vertex); 63 | return o; 64 | } 65 | 66 | 67 | fixed4 frag(v2f i) : SV_Target 68 | { 69 | 70 | float4 output = float4(0,0,0,0); 71 | 72 | //Compute random tiling offsets and weights 73 | float3 weights = float3(0,0,0); 74 | float2 uvVertex0 = 0, uvVertex1 = 0, uvVertex2 = 0; 75 | RandomOffsetTiling(i.uv * SQRT_3, weights, uvVertex0, uvVertex1, uvVertex2); 76 | 77 | //Compute the screenspace derivatives on the original unaltered uvs for mip-mapping 78 | float2 constDx = ddx(i.uv); 79 | float2 constDy = ddy(i.uv); 80 | 81 | // Variance preserving blending on the gaussian texture inputs and 82 | if (_GOn == 2) 83 | { 84 | //Sample the gaussian texture 3 times, one for each offset, using the macro defined in GaussianBlend.cginc that wraps tex.SampleGrad 85 | float4 gaussian1 = UNITY_SAMPLE_TEX2D_GRAD(_MainTex, i.uv + uvVertex0, constDx, constDy); 86 | float4 gaussian2 = UNITY_SAMPLE_TEX2D_GRAD(_MainTex, i.uv + uvVertex1, constDx, constDy); 87 | float4 gaussian3 = UNITY_SAMPLE_TEX2D_GRAD(_MainTex, i.uv + uvVertex2, constDx, constDy); 88 | 89 | //Fill out the colorspace structure with the colorspace information defined in the material to make it easier to pass the information to functions 90 | colorspace cs; 91 | cs.axis0 = _CX; 92 | cs.axis1 = _CY; 93 | cs.axis2 = _CZ; 94 | cs.center = _CsCenter; 95 | 96 | //Call 97 | float3 gaussianTotal = Blend3GaussianRGB(gaussian1, gaussian2, gaussian3, weights, cs); 98 | 99 | float mip = CalcMipLevel(UNITY_PASS_TEX2D(_MainTex), i.uv); 100 | float4 LUT = LookUpTableRGB(_LUTTex, _LUTTex_TexelSize.zw, gaussianTotal.rgb, mip); 101 | LUT.rgb = ConvertColorspaceToRGB(LUT.rgb, cs); 102 | 103 | output = LUT; 104 | } 105 | else if (_GOn == 1) 106 | { 107 | float4 reference1 = tex2Dgrad(_MainTex2, i.uv + uvVertex0, constDx, constDy); 108 | float4 reference2 = tex2Dgrad(_MainTex2, i.uv + uvVertex1, constDx, constDy); 109 | float4 reference3 = tex2Dgrad(_MainTex2, i.uv + uvVertex2, constDx, constDy); 110 | output = reference1 * weights.x + reference2*weights.y + reference3*weights.z; 111 | } 112 | else 113 | { 114 | output = tex2Dgrad(_MainTex2, i.uv, constDx, constDy); 115 | } 116 | return output; 117 | // return float4(uvVertex0 * weights.x + uvVertex1 * weights.y + uvVertex2 * weights.z, 0 , 1); 118 | } 119 | ENDCG 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /GaussianTex/shaders/Demo/BlendDemo.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7a9317eaaf59b894b820e8cccf1a33f5 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /GaussianTex/shaders/GaussianBlend.cginc: -------------------------------------------------------------------------------- 1 | /***************************************************************************************** 2 | * @file GaussianBlend.cginc 3 | * @brief Library of functions relating to sampling, blending, and inverting gaussian textures 4 | * @author Error-mdl 5 | * @date 2021 6 | *****************************************************************************************/ 7 | 8 | // Missing macros for defining texture2D's and their samplers and for sampling a texture2d with the gradient function 9 | #ifndef UNITY_PASS_TEX2D 10 | #define UNITY_PASS_TEX2D(tex) tex, sampler##tex 11 | #endif 12 | 13 | #ifndef UNITY_SAMPLE_TEX2D_GRAD 14 | #define UNITY_SAMPLE_TEX2D_GRAD(tex, coord, DDX, DDY) tex.SampleGrad (sampler##tex, coord, DDX, DDY) 15 | #endif 16 | 17 | #define SQRT_3 1.732050808 18 | #define TWO_SQRT_3 3.464101615 19 | #define TWO_TAN_30 1.154700538 20 | #define TAN_30 0.577350269 21 | 22 | /** 23 | * A structure containing the three basis vectors of the colorspace, the inverse of the lengths of the basis vectors, 24 | * and the center of the colorspace 25 | */ 26 | struct colorspace 27 | { 28 | float4 axis0; ////< First basis vector of the colorspace in the xyz components, inverse length of the axis in w 29 | float4 axis1; ////< Second basis vector of the colorspace in the xyz components, inverse length of the axis in w 30 | float4 axis2; ////< Third basis vector of the colorspace in the xyz components, inverse length of the axis in w 31 | float4 center; ///< Center of the colorspace in the xyz components, w is unused 32 | }; 33 | 34 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 35 | //-LUT Functions----------------------------------------------------------------------------------------------------------------------------------------------- 36 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 37 | 38 | 39 | /** @brief Takes the R color from gaussian textures and samples the lookup table to find the true color 40 | * 41 | * @param lut The look up table (LUT) 42 | * @param lutDim float2 containing the width, height of the LUT 43 | * @param coords float2 containing the R and A colors from the gaussian texture, which represents coordinates in the LUT 44 | * @param mip mip level used when sampling the gaussian texture 45 | * 46 | * @returns The non-gaussian color of the texture in the colorspace associated with the LUT. Note that for single channel 47 | * textures, there is only one variable so gaussian textures generated from these should not have decorrelated 48 | * colorspaces 49 | */ 50 | 51 | float4 LookUpTableR(Texture2DArray lut, float2 lutDim, const float coords, const float mip) 52 | { 53 | uint coords1 = floor(coords * (lutDim.x * lutDim.y - 1.0)); 54 | uint LUTWidth = (uint)lutDim.x; 55 | uint LUTModW = LUTWidth - 1; // x % y is equivalent to x & (y - 1) if y is power of 2 56 | uint LUTDivW = firstbithigh(LUTWidth); // LUTWidth is a power of 2, so firstbithigh gives log2(LUTWidth) 57 | float4 sample1 = float4( 58 | lut.Load(int4(coords1 & LUTModW, coords1 >> LUTDivW, mip, 0)).r, 59 | 0, 60 | 0, 61 | 1 62 | ); 63 | return sample1; 64 | } 65 | 66 | 67 | /** @brief Takes the R and G color from gaussian textures and samples the lookup table 2 times to find the true color 68 | * 69 | * @param lut The look up table (LUT) 70 | * @param lutDim float2 containing the width, height of the LUT 71 | * @param coords float2 containing the R and G colors from the gaussian texture, which represents coordinates in the LUT 72 | * @param mip mip level used when sampling the gaussian texture 73 | * 74 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT 75 | */ 76 | 77 | float4 LookUpTableRG(Texture2DArray lut, float2 lutDim, const float2 coords, const float mip) 78 | { 79 | uint2 coords1 = floor(coords * (lutDim.x * lutDim.y - 1.0)); 80 | uint LUTWidth = (uint)lutDim.x; 81 | uint LUTModW = LUTWidth - 1; // x % y is equivalent to x & (y - 1) if y is power of 2 82 | uint LUTDivW = firstbithigh(LUTWidth); // LUTWidth is a power of 2, so firstbithigh gives log2(LUTWidth) 83 | float4 sample1 = float4( 84 | lut.Load(int4(coords1.x & LUTModW, coords1.x >> LUTDivW, mip, 0)).r, 85 | lut.Load(int4(coords1.y & LUTModW, coords1.y >> LUTDivW, mip, 0)).g, 86 | 0, 87 | 1 88 | ); 89 | return sample1; 90 | } 91 | 92 | 93 | /** @brief Takes the R and A color from gaussian textures and samples the lookup table 2 times to find the true color, 94 | * useful for unity metallic-smoothness maps. 95 | * 96 | * @param lut The look up table (LUT) 97 | * @param lutDim float2 containing the width, height of the LUT 98 | * @param coords float2 containing the R and A colors from the gaussian texture, which represents coordinates in the LUT 99 | * @param mip mip level used when sampling the gaussian texture 100 | * 101 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT. Note that 102 | * for RA textures, red and alpha are practically independent so gaussian textures generated from these images 103 | * should not have decorrelated colorspaces and thus the return value should already be in the proper space 104 | */ 105 | 106 | float4 LookUpTableRA(Texture2DArray lut, float2 lutDim, const float2 coords, const float mip) 107 | { 108 | uint2 coords1 = floor(coords * (lutDim.x * lutDim.y - 1.0)); 109 | uint LUTWidth = (uint)lutDim.x; 110 | uint LUTModW = LUTWidth - 1; // x % y is equivalent to x & (y - 1) if y is power of 2 111 | uint LUTDivW = firstbithigh(LUTWidth); // LUTWidth is a power of 2, so firstbithigh gives log2(LUTWidth) 112 | float4 sample1 = float4( 113 | lut.Load(int4(coords1.x & LUTModW, coords1.x >> LUTDivW, mip, 0)).r, 114 | 0, 115 | 0, 116 | lut.Load(int4(coords1.y & LUTModW, coords1.y >> LUTDivW, mip, 0)).a 117 | ); 118 | return sample1; 119 | } 120 | 121 | /** @brief Takes the RGB (no alpha) color from gaussian textures and samples the lookup table 3 times to find the true color 122 | * 123 | * @param lut The look up table (LUT) 124 | * @param lutDim float2 containing the width, height of the LUT 125 | * @param coords float3 containing the RGB colors from the gaussian texture, which represents coordinates in the LUT 126 | * @param mip mip level used when sampling the gaussian texture 127 | * 128 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT 129 | */ 130 | 131 | float4 LookUpTableRGB(Texture2DArray lut, float2 lutDim, const float3 coords, const float mip) 132 | { 133 | uint3 coords1 = floor(coords * (lutDim.x * lutDim.y - 1.0)); 134 | uint LUTWidth = (uint)lutDim.x; 135 | uint LUTModW = LUTWidth - 1; // x % y is equivalent to x & (y - 1) if y is power of 2 136 | uint LUTDivW = firstbithigh(LUTWidth); // LUTWidth is a power of 2, so firstbithigh gives log2(LUTWidth) 137 | float3 sample1 = float3( 138 | lut.Load(int4(coords1.x & LUTModW, coords1.x >> LUTDivW, mip, 0)).r, 139 | lut.Load(int4(coords1.y & LUTModW, coords1.y >> LUTDivW, mip, 0)).g, 140 | lut.Load(int4(coords1.z & LUTModW, coords1.z >> LUTDivW, mip, 0)).b 141 | ); 142 | return float4(sample1, 1); 143 | } 144 | 145 | /** @brief Takes the RGBA color from gaussian textures and samples the lookup table 4 times to find the true color 146 | * 147 | * @param lut The look up table (LUT) 148 | * @param lutDim float2 containing the width, height of the LUT 149 | * @param coords float4 containing the RGBA colors from the gaussian texture, which represents coordinates in the LUT 150 | * @param mip mip level used when sampling the gaussian texture 151 | * 152 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT 153 | */ 154 | 155 | float4 LookUpTableRGBA(Texture2DArray lut, float2 lutDim, const float4 coords, const float mip) 156 | { 157 | uint4 coords1 = floor(coords * (lutDim.x * lutDim.y - 1.0)); 158 | uint LUTWidth = (uint)lutDim.x; 159 | uint LUTModW = LUTWidth - 1; // x % y is equivalent to x & (y - 1) if y is power of 2 160 | uint LUTDivW = firstbithigh(LUTWidth); // LUTWidth is a power of 2, so firstbithigh gives log2(LUTWidth) 161 | float4 sample1 = float4( 162 | lut.Load(int4(coords1.x & LUTModW, coords1.x >> LUTDivW, mip, 0)).r, 163 | lut.Load(int4(coords1.y & LUTModW, coords1.y >> LUTDivW, mip, 0)).g, 164 | lut.Load(int4(coords1.z & LUTModW, coords1.z >> LUTDivW, mip, 0)).b, 165 | lut.Load(int4(coords1.w & LUTModW, coords1.w >> LUTDivW, mip, 0)).a 166 | ); 167 | return sample1; 168 | } 169 | 170 | 171 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 172 | //-Blend Functions--------------------------------------------------------------------------------------------------------------------------------------------- 173 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 174 | 175 | /** @brief Takes the R color from two samples of a gaussian texture and blends them with variance preserving blending 176 | * 177 | * @param gaussian1 First gaussian sample R 178 | * @param gaussian2 Second gaussian sample R 179 | * @param weights float2 containing the 0-1 weights of the two gaussian samples 180 | * @returns The non-gaussian color of the texture, note that single channel images should not be generated with decorrleated colorspaces 181 | */ 182 | 183 | float Blend2GaussianR(float2 gaussian1, float2 gaussian2, 184 | float2 weights) 185 | { 186 | float gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 - 0.5) 187 | / sqrt(weights.x * weights.x + weights.y * weights.y)) + 0.5; 188 | return saturate(gaussian); 189 | } 190 | 191 | 192 | /** @brief Takes the R color from three samples of a gaussian texture and blends them with variance preserving blending 193 | * 194 | * @param gaussian1 First gaussian sample R 195 | * @param gaussian2 Second gaussian sample R 196 | * @param gaussian3 Third gaussian sample R 197 | * @param weights float3 containing the 0-1 weights of each of the three gaussian samples 198 | * @returns The non-gaussian color of the texture, note that single channel images should not be generated with decorrleated colorspaces 199 | */ 200 | 201 | float Blend3GaussianR(float gaussian1, float2 gaussian2, float gaussian3, 202 | float3 weights) 203 | { 204 | float gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 + weights.z * gaussian3 - 0.5) 205 | / sqrt(weights.x * weights.x + weights.y * weights.y + weights.z * weights.z)) + 0.5; 206 | return saturate(gaussian); 207 | } 208 | 209 | /** @brief Takes the RG colors from two samples of a gaussian texture and blends them with variance preserving blending, assumes RGB colorspace 210 | * 211 | * @param gaussian1 First gaussian sample RG 212 | * @param gaussian2 Second gaussian sample RG 213 | * @param weights float2 containing the 0-1 weights of the two gaussian samples 214 | * @returns The non-gaussian color of the texture, assuming the texture does not have a decorrelated colorspace 215 | */ 216 | 217 | float2 Blend2GaussianRG(float2 gaussian1, float2 gaussian2, 218 | float2 weights) 219 | { 220 | float2 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 - float2(0.5,0.5)) 221 | / sqrt(weights.x * weights.x + weights.y * weights.y)) + float2(0.5, 0.5); 222 | return saturate(gaussian); 223 | } 224 | 225 | 226 | /** @brief Takes the RG colors from three samples of a gaussian texture and blends them with variance preserving blending 227 | * 228 | * @param gaussian1 First gaussian sample RG 229 | * @param gaussian2 Second gaussian sample RG 230 | * @param gaussian3 Third gaussian sample RG 231 | * @param weights float3 containing the 0-1 weights of each of the three gaussian samples 232 | * @returns The non-gaussian color of the texture, assuming the texture does not have a decorrelated colorspace 233 | */ 234 | 235 | float2 Blend3GaussianRG(float2 gaussian1, float2 gaussian2, float2 gaussian3, 236 | float3 weights) 237 | { 238 | float2 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 + weights.z * gaussian3 - float2(0.5,0.5)) 239 | / sqrt(weights.x * weights.x + weights.y * weights.y + weights.z * weights.z)) + float2(0.5, 0.5); 240 | return saturate(gaussian); 241 | } 242 | 243 | /** @brief Takes the RG or RA colors from two samples of a gaussian texture and blends them with variance preserving blending 244 | * 245 | * @param gaussian1 First gaussian sample RA 246 | * @param gaussian2 Second gaussian sample RA 247 | * @param weights float2 containing the 0-1 weights of the two gaussian samples 248 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT. Note that 249 | * for RA textures, red and alpha are practically independent so gaussian textures generated from these images 250 | * should not have decorrelated colorspaces and thus cs.axis0.w should be 1 251 | */ 252 | 253 | float2 Blend2GaussianRA(float2 gaussian1, float2 gaussian2, 254 | float2 weights) 255 | { 256 | float2 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 - float2(0.5,0.5)) 257 | / sqrt(weights.x * weights.x + weights.y * weights.y)) + float2(0.5, 0.5); 258 | return saturate(gaussian); 259 | } 260 | 261 | 262 | /** @brief Takes the RA colors from three samples of a gaussian texture and blends them with variance preserving blending 263 | * 264 | * @param gaussian1 First gaussian sample RA 265 | * @param gaussian2 Second gaussian sample RA 266 | * @param gaussian3 Third gaussian sample RA 267 | * @param weights float3 containing the 0-1 weights of each of the three gaussian samples 268 | * @returns The non-gaussian color of the texture in normal RGBA colorspace. Note that 269 | * for RA textures, red and alpha are practically independent so gaussian textures generated from these images 270 | * should not have decorrelated colorspaces 271 | */ 272 | 273 | float2 Blend3GaussianRA(float2 gaussian1, float2 gaussian2, float2 gaussian3, 274 | float3 weights) 275 | { 276 | float2 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 + weights.z * gaussian3 - float2(0.5,0.5)) 277 | / sqrt(weights.x * weights.x + weights.y * weights.y + weights.z * weights.z)) + float2(0.5, 0.5); 278 | return saturate(gaussian); 279 | } 280 | 281 | /** @brief Takes the RGB colors from two samples of a gaussian texture and blends them with variance preserving blending 282 | * 283 | * @param gaussian1 First gaussian sample RGB 284 | * @param gaussian2 Second gaussian sample RGB 285 | * @param weights float2 containing the 0-1 weights of the two gaussian samples 286 | * @param cs colorspace struct containing the inverse lengths of the basis vectors as the w component of the vectors 287 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT 288 | */ 289 | 290 | float3 Blend2GaussianRGB(float3 gaussian1, float3 gaussian2, 291 | float2 weights, colorspace cs) 292 | { 293 | float3 gaussian = float3(cs.axis0.w, cs.axis1.w, cs.axis2.w) * ((weights.x * gaussian1 + weights.y * gaussian2 - float3(0.5,0.5,0.5)) 294 | / sqrt(weights.x * weights.x + weights.y * weights.y)) + float3(0.5, 0.5, 0.5); 295 | return saturate(gaussian); 296 | } 297 | 298 | 299 | /** @brief Takes the RGB colors from three samples of a gaussian texture and blends them with variance preserving blending 300 | * 301 | * @param gaussian1 First gaussian sample RGB 302 | * @param gaussian2 Second gaussian sample RGB 303 | * @param gaussian3 Third gaussian sample RGB 304 | * @param weights float3 containing the 0-1 weights of each of the three gaussian samples 305 | * @param cs colorspace struct containing the inverse lengths of the basis vectors as the w component of the vectors 306 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT 307 | */ 308 | 309 | float3 Blend3GaussianRGB(float3 gaussian1, float3 gaussian2, float3 gaussian3, 310 | float3 weights, colorspace cs) 311 | { 312 | float3 gaussian = float3(cs.axis0.w, cs.axis1.w, cs.axis2.w) * ((weights.x * gaussian1 + weights.y * gaussian2 + weights.z * gaussian3 - float3(0.5,0.5,0.5)) 313 | / sqrt(weights.x * weights.x + weights.y * weights.y + weights.z * weights.z)) + float3(0.5, 0.5, 0.5); 314 | return saturate(gaussian); 315 | } 316 | 317 | /** @brief Takes the RGB colors from two samples of a gaussian texture and blends them with variance preserving blending, assumes normal RGB colorspace 318 | * 319 | * @param gaussian1 First gaussian sample RGB 320 | * @param gaussian2 Second gaussian sample RGB 321 | * @param weights float2 containing the 0-1 weights of the two gaussian samples 322 | * @returns The non-gaussian color of the texture, assuming the texture does not have a decorrelated colorspace 323 | */ 324 | 325 | float3 Blend2GaussianRGBNoCs(float3 gaussian1, float3 gaussian2, 326 | float2 weights) 327 | { 328 | float3 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 - float3(0.5,0.5,0.5)) 329 | / sqrt(weights.x * weights.x + weights.y * weights.y)) + float3(0.5, 0.5, 0.5); 330 | return saturate(gaussian); 331 | } 332 | 333 | 334 | /** @brief Takes the RGB colors from three samples of a gaussian texture and blends them with variance preserving blending, assumes normal RGB colorspace 335 | * 336 | * @param gaussian1 First gaussian sample RGB 337 | * @param gaussian2 Second gaussian sample RGB 338 | * @param gaussian3 Third gaussian sample RGB 339 | * @param weights float3 containing the 0-1 weights of each of the three gaussian samples 340 | * @returns The non-gaussian color of the texture, assuming the texture does not have a decorrelated colorspace 341 | */ 342 | 343 | float3 Blend3GaussianRGBNoCs(float3 gaussian1, float3 gaussian2, float3 gaussian3, 344 | float3 weights, colorspace cs) 345 | { 346 | float3 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 + weights.z * gaussian3 - float3(0.5,0.5,0.5)) 347 | / sqrt(weights.x * weights.x + weights.y * weights.y + weights.z * weights.z)) + float3(0.5, 0.5, 0.5); 348 | return saturate(gaussian); 349 | } 350 | 351 | 352 | /** @brief Takes the RGBA colors from two samples of a gaussian texture and blends them with variance preserving blending 353 | * 354 | * @param gaussian1 First gaussian sample RGBA 355 | * @param gaussian2 Second gaussian sample RGBA 356 | * @param weights float2 containing the 0-1 weights of the two gaussian samples 357 | * @param cs colorspace struct containing the inverse lengths of the basis vectors as the w component of the vectors 358 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT 359 | */ 360 | 361 | float4 Blend2GaussianRGBA(float4 gaussian1, float4 gaussian2, 362 | float2 weights, colorspace cs) 363 | { 364 | float4 gaussian = float4(cs.axis0.w, cs.axis1.w, cs.axis2.w, 1) * ((weights.x * gaussian1 + weights.y * gaussian2 - float4(0.5,0.5,0.5,0.5)) 365 | / sqrt(weights.x * weights.x + weights.y * weights.y)) + float4(0.5, 0.5, 0.5, 0.5); 366 | return saturate(gaussian); 367 | } 368 | 369 | 370 | /** @brief Takes the RGBA colors from three samples of a gaussian texture and blends them with variance preserving blending 371 | * 372 | * @param gaussian1 First gaussian sample RGBA 373 | * @param gaussian2 Second gaussian sample RGBA 374 | * @param gaussian3 Third gaussian sample RGBA 375 | * @param weights float3 containing the 0-1 weights of each of the three gaussian samples 376 | * @param cs colorspace struct containing the inverse lengths of the basis vectors as the w component of the vectors 377 | * @returns The non-gaussian color of the texture in the (possibly non-rgb) colorspace associated with the LUT 378 | */ 379 | 380 | float4 Blend3GaussianRGBA(float4 gaussian1, float4 gaussian2, float4 gaussian3, 381 | float3 weights, colorspace cs) 382 | { 383 | float4 gaussian = float4(cs.axis0.w, cs.axis1.w, cs.axis2.w, 1) * ((weights.x * gaussian1 + weights.y * gaussian2 + weights.z * gaussian3 - float4(0.5,0.5,0.5,0.5)) 384 | / sqrt(weights.x * weights.x + weights.y * weights.y + weights.z * weights.z)) + float4(0.5, 0.5, 0.5, 0.5); 385 | return saturate(gaussian); 386 | } 387 | 388 | /** @brief Takes the RGBA colors from two samples of a gaussian texture and blends them with variance preserving blending, assumes normal RGB colorspace 389 | * 390 | * @param gaussian1 First gaussian sample RGBA 391 | * @param gaussian2 Second gaussian sample RGBA 392 | * @param weights float2 containing the 0-1 weights of the two gaussian samples 393 | * @returns The non-gaussian color of the texture, assuming the texture does not have a decorrelated colorspace 394 | */ 395 | 396 | float4 Blend2GaussianRGBANoCs(float4 gaussian1, float4 gaussian2, 397 | float2 weights) 398 | { 399 | float4 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 - float4(0.5,0.5,0.5,0.5)) 400 | / sqrt(weights.x * weights.x + weights.y * weights.y)) + float4(0.5, 0.5, 0.5, 0.5); 401 | return saturate(gaussian); 402 | } 403 | 404 | 405 | /** @brief Takes the RGBA colors from three samples of a gaussian texture and blends them with variance preserving blending, assumes normal RGB colorspace 406 | * 407 | * @param gaussian1 First gaussian sample RGBA 408 | * @param gaussian2 Second gaussian sample RGBA 409 | * @param gaussian3 Third gaussian sample RGBA 410 | * @param weights float3 containing the 0-1 weights of each of the three gaussian samples 411 | * @returns The non-gaussian color of the texture, assuming the texture does not have a decorrelated colorspace 412 | */ 413 | 414 | float4 Blend3GaussianRGBANoCs(float4 gaussian1, float4 gaussian2, float4 gaussian3, 415 | float3 weights) 416 | { 417 | float4 gaussian = ((weights.x * gaussian1 + weights.y * gaussian2 + weights.z * gaussian3 - float4(0.5,0.5,0.5,0.5)) 418 | / sqrt(weights.x * weights.x + weights.y * weights.y + weights.z * weights.z)) + float4(0.5, 0.5, 0.5, 0.5); 419 | return saturate(gaussian); 420 | } 421 | 422 | 423 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 424 | //-Misc Functions---------------------------------------------------------------------------------------------------------------------------------------------- 425 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 426 | 427 | /** @brief Takes the color output from the LUT in the decorrolated colorspace and converts it back to RGB 428 | * 429 | * @param lutColor The color from the LUT 430 | * @param cs Colorspace to convert from 431 | * 432 | * @returns the final correct RGB color 433 | */ 434 | 435 | float3 ConvertColorspaceToRGB(float3 lutColor, const colorspace cs) 436 | { 437 | return (lutColor.r * cs.axis0.xyz + lutColor.g * cs.axis1.xyz + lutColor.b * cs.axis2.xyz) + cs.center.xyz; 438 | } 439 | 440 | /** @brief Finds the mip level of a given texture and sampler with given uvs 441 | * 442 | * @param tex The texture to determine the mip level of 443 | * @param tex_sampler The sampler corresponding to tex 444 | * @param uv uv coordinates that tex was sampled with 445 | * 446 | * @returns the mip level of tex 447 | */ 448 | 449 | inline float CalcMipLevel(Texture2D tex, sampler sampler_tex, float2 uv) 450 | { 451 | return tex.CalculateLevelOfDetail(sampler_tex, uv); 452 | } 453 | 454 | 455 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 456 | //-Random Tiling Functions------------------------------------------------------------------------------------------------------------------------------------- 457 | //------------------------------------------------------------------------------------------------------------------------------------------------------------- 458 | 459 | 460 | 461 | float2 hash2(float2 input) 462 | { 463 | return frac(sin(mul(float2x2(137.231, 512.37, 199.137, 373.351), input)) * 23597.3733); 464 | } 465 | 466 | /* 467 | (1,0) 468 | Y Y'_______ 469 | | /\ / 470 | |30/ \ / 471 | | / \ / 472 | |/ \ / 473 | 0-----X--X' 474 | (0,0) (1,0) 475 | 476 | Triangle Grid For Random Offset Tiling 477 | X and Y are the original basis vectors in the UV space (1,0) and (0,1) respectively. Do 478 | a space transformation with basis vectors X' and Y' such that X' is X scaled up by 479 | 2/sqrt(3) and Y' is Y sheared along the X axis so that the angle Y0Y' is 30 degrees. 480 | The line connecting X' and Y' splits the new space's unit cell into two triangles which 481 | map back to two equilateral triangles in the original space 482 | 483 | Y' = ( tan(30), 1) = (1/sqrt(3), 1) 484 | X' = ( 2 * tan(30), 0) = (2/sqrt(3), 0); 485 | */ 486 | 487 | /** @brief For a given UV coordinate, generates 3 random offsets associated with the closest 3 points of a triangular grid 488 | * and calculates the normalized weights of each three offsets based on the distance to each point from the input uv 489 | * 490 | * @param uv Input uv to calculate the offsets and weights for 491 | * @param triWeights float3 into which the weights of each offset UV will be output 492 | * @param uvVertex0 float3 into which the offset associated with the first point in the grid will be Output 493 | * @param uvVertex1 float3 into which the offset associated with the second point in the grid will be Output 494 | * @param uvVertex2 float3 into which the offset associated with the third point in the grid will be Output 495 | */ 496 | 497 | void RandomOffsetTiling(float2 uv, inout float3 triWeights, 498 | inout float2 uvVertex0, inout float2 uvVertex1, inout float2 uvVertex2) 499 | { 500 | 501 | float2x2 ShearedUVSpace = float2x2(-TAN_30, 1, TWO_TAN_30, 0); //WHY MUST TAN 30 BE NEGATIVE? WHY? IT SHOULDN"T BE BUT IT DOESN'T WORK OTHERWISE. I CAN'T FUCKING MATH. 502 | 503 | float2 shearedUVs = mul(ShearedUVSpace, uv); 504 | float2 intSUVs = floor(shearedUVs); 505 | float2 fracSUVs = frac(shearedUVs); 506 | float Ternary3rdComponent = 1.0 - fracSUVs.x - fracSUVs.y; 507 | float2 vertex0Offset = Ternary3rdComponent > 0 ? float2(0, 0) : float2(1, 1); 508 | float2 hashVertex0 = intSUVs + vertex0Offset; 509 | float2 hashVertex1 = intSUVs + float2(0, 1); 510 | float2 hashVertex2 = intSUVs + float2(1, 0); 511 | hashVertex0 = hash2(hashVertex0); 512 | hashVertex1 = hash2(hashVertex1); 513 | hashVertex2 = hash2(hashVertex2); 514 | /* 515 | float sin0, cos0, sin1, cos1, sin2, cos2; 516 | sincos(0.5 * (hashVertex0.x + hashVertex0.y) - 0.25, sin0, cos0); 517 | sincos(0.5 * (hashVertex1.x + hashVertex1.y) - 0.25, sin1, cos1); 518 | sincos(0.5 * (hashVertex2.x + hashVertex2.y) - 0.25, sin2, cos2); 519 | */ 520 | uvVertex0 += hashVertex0; 521 | uvVertex1 += hashVertex1; 522 | uvVertex2 += hashVertex2; 523 | 524 | /* 525 | uvVertex0 = uv * lerp(0.8, 1.2, hashVertex0.x) + hashVertex0; 526 | uvVertex1 = uv * lerp(0.8, 1.2, hashVertex1.x) + hashVertex1; 527 | uvVertex2 = uv * lerp(0.8, 1.2, hashVertex2.x) + hashVertex2; 528 | */ 529 | if (Ternary3rdComponent > 0) 530 | { 531 | triWeights = float3(Ternary3rdComponent, fracSUVs.y, fracSUVs.x); 532 | } 533 | else 534 | { 535 | triWeights = float3(-Ternary3rdComponent, 1.0 - fracSUVs.x, 1.0 - fracSUVs.y); 536 | } 537 | } 538 | -------------------------------------------------------------------------------- /GaussianTex/shaders/GaussianBlend.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b000878cecd1eb4a9cf0072cd2e411d 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 560cad6cf185fdc43bdee7c61072d9b7 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardCG.cginc: -------------------------------------------------------------------------------- 1 | 2 | uniform float4 _Color; 3 | uniform Texture2D _MainTex; 4 | uniform sampler sampler_MainTex; 5 | uniform Texture2DArray _MainTex_LUT; 6 | uniform float4 _MainTex_LUT_TexelSize; 7 | uniform float4 _MainTex_ST; 8 | uniform Texture2D _MetallicGlossMap; 9 | uniform Texture2DArray _MetGloss_LUT; 10 | uniform float4 _MetGloss_LUT_TexelSize; 11 | uniform Texture2D _NormalMap; 12 | uniform Texture2DArray _NormalMap_LUT; 13 | uniform float4 _NormalMap_LUT_TexelSize; 14 | uniform float4 _NormalMap_ST; 15 | uniform float _TilingScale; 16 | uniform float _Smoothness; 17 | uniform float _Metallic; 18 | uniform float _NormalStrength; 19 | 20 | uniform float4 _CsCenter; 21 | uniform float4 _CX; 22 | uniform float4 _CY; 23 | uniform float4 _CZ; 24 | 25 | uniform float4 _NormCsCenter; 26 | uniform float4 _NCX; 27 | uniform float4 _NCY; 28 | uniform float4 _NCZ; 29 | 30 | 31 | #include "RTStandardCommon.cginc" 32 | #include "../GaussianBlend.cginc" 33 | 34 | v2f vert(vertexIn v) 35 | { 36 | v2f o; 37 | 38 | UNITY_SETUP_INSTANCE_ID(v); 39 | UNITY_INITIALIZE_OUTPUT(v2f, o); 40 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); 41 | 42 | StdCommonVert(v, o); 43 | 44 | return o; 45 | } 46 | 47 | float4 frag(v2f i) : SV_TARGET 48 | { 49 | 50 | //Compute random tiling offsets and weights 51 | float3 weights = float3(0,0,0); 52 | float2 uvVertex0 = 0, uvVertex1 = 0, uvVertex2 = 0; 53 | RandomOffsetTiling(i.uv * _TilingScale, weights, uvVertex0, uvVertex1, uvVertex2); 54 | 55 | //Compute the screenspace derivatives on the original unaltered uvs for mip-mapping 56 | float2 constDx = ddx(i.uv); 57 | float2 constDy = ddy(i.uv); 58 | float mip = CalcMipLevel(_MainTex, sampler_MainTex, i.uv); 59 | 60 | colorspace cs; 61 | cs.axis0 = _CX; 62 | cs.axis1 = _CY; 63 | cs.axis2 = _CZ; 64 | cs.center = _CsCenter; 65 | 66 | float3 mainGauss1 = _MainTex.SampleGrad(sampler_MainTex, i.uv + uvVertex0, constDx, constDy).rgb; 67 | float3 mainGauss2 = _MainTex.SampleGrad(sampler_MainTex, i.uv + uvVertex1, constDx, constDy).rgb; 68 | float3 mainGauss3 = _MainTex.SampleGrad(sampler_MainTex, i.uv + uvVertex2, constDx, constDy).rgb; 69 | 70 | float3 mainGaussTotal = Blend3GaussianRGB(mainGauss1, mainGauss2, mainGauss3, weights, cs); 71 | 72 | float4 mainColor = LookUpTableRGB(_MainTex_LUT, _MainTex_LUT_TexelSize.zw, mainGaussTotal, mip); 73 | mainColor.rgb = ConvertColorspaceToRGB(mainColor.rgb, cs); 74 | 75 | mainColor *= _Color; 76 | 77 | //float4 metallicGlossMap = tex2D(_MetallicGlossMap, i.uv); 78 | float2 mGlossGauss1 = _MetallicGlossMap.SampleGrad(sampler_MainTex, i.uv + uvVertex0, constDx, constDy).ra; 79 | float2 mGlossGauss2 = _MetallicGlossMap.SampleGrad(sampler_MainTex, i.uv + uvVertex1, constDx, constDy).ra; 80 | float2 mGlossGauss3 = _MetallicGlossMap.SampleGrad(sampler_MainTex, i.uv + uvVertex2, constDx, constDy).ra; 81 | 82 | float2 mGlossTotal = Blend3GaussianRA(mGlossGauss1, mGlossGauss2, mGlossGauss3, weights); 83 | float4 mGlossColor = LookUpTableRA(_MetGloss_LUT, _MetGloss_LUT_TexelSize.zw, mGlossTotal, mip); 84 | 85 | 86 | float4 normGauss1 = _NormalMap.SampleGrad(sampler_MainTex, i.uv + uvVertex0, constDx, constDy); 87 | float4 normGauss2 = _NormalMap.SampleGrad(sampler_MainTex, i.uv + uvVertex1, constDx, constDy); 88 | float4 normGauss3 = _NormalMap.SampleGrad(sampler_MainTex, i.uv + uvVertex2, constDx, constDy); 89 | 90 | 91 | float4 normTotal = Blend3GaussianRGBANoCs(normGauss1, normGauss2, normGauss3, weights); 92 | float4 normColor = LookUpTableRGBA(_NormalMap_LUT, _NormalMap_LUT_TexelSize.zw, normTotal, mip); 93 | 94 | 95 | //float4 normColor = normGauss1 * weights.x + normGauss2 * weights.y + normGauss2 * weights.z / (weights.x + weights.y + weights.z); 96 | //float3 tNormal = normalize(2*normColor - 1 ); 97 | float3 tNormal = saturate(UnpackNormal(normColor)) + float3(0,0,0.0001); 98 | tNormal.xy *= _NormalStrength; 99 | tNormal = normalize(tNormal); 100 | 101 | float3x3 TangentToWorld = float3x3(i.tangent.x, i.bitangent.x, i.normal.x, 102 | i.tangent.y, i.bitangent.y, i.normal.y, 103 | i.tangent.z, i.bitangent.z, i.normal.z); 104 | 105 | float3 normal = normalize(mul(TangentToWorld, tNormal)); 106 | 107 | //clip(texCol.a - _Cutoff); 108 | 109 | 110 | float smoothness = mGlossColor.a * _Smoothness; 111 | 112 | float metallic = mGlossColor.r * _Metallic; 113 | 114 | float4 color = StdCommonFrag(i, mainColor, normal, smoothness, metallic); 115 | return color; 116 | } 117 | 118 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardCG.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 14004e4a2fd581d4da1380a017c3f7c5 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardCommon.cginc: -------------------------------------------------------------------------------- 1 | 2 | #include "UnityCG.cginc" 3 | #include "Lighting.cginc" 4 | #include "AutoLight.cginc" 5 | #include "UnityPBSLighting.cginc" 6 | 7 | struct vertexIn { 8 | float4 vertex : POSITION; 9 | float4 tangent : TANGENT; 10 | float3 normal : NORMAL; 11 | float4 uv0 : TEXCOORD0; 12 | #ifdef LIGHTMAP_ON 13 | float2 uv1 : TEXCOORD1; 14 | #endif 15 | #ifdef UNITY_PASS_FORWARDBASE 16 | float4 uv2 : TEXCOORD2; 17 | float4 uv3 : TEXCOORD3; 18 | #endif 19 | UNITY_VERTEX_INPUT_INSTANCE_ID 20 | }; 21 | 22 | struct v2f 23 | { 24 | float4 pos : SV_POSITION; 25 | float3 normal : NORMAL; 26 | float4 tangent : TANGENT; 27 | float2 uv : TEXCOORD0; 28 | float3 wPos : TEXCOORD1; 29 | float3 bitangent : TEXCOORD2; 30 | SHADOW_COORDS(3) 31 | #ifdef LIGHTMAP_ON 32 | float2 lightmapUV : LIGHTMAPUV; 33 | #endif 34 | UNITY_VERTEX_OUTPUT_STEREO 35 | }; 36 | 37 | 38 | float3 vertex_lighting(float3 vertexPos, float3 normal) 39 | { 40 | float3 light = float3(0.0, 0.0, 0.0); 41 | for (int index = 0; index < 4; index++) 42 | { 43 | float4 lightPosition = float4(unity_4LightPosX0[index], 44 | unity_4LightPosY0[index], 45 | unity_4LightPosZ0[index], 1.0); 46 | 47 | float3 originToLightSource = 48 | lightPosition.xyz - vertexPos; 49 | float3 lightDirection = normalize(originToLightSource); 50 | float squaredDistance = 51 | dot(originToLightSource, originToLightSource); 52 | float attenuation = 1.0 / (1.0 + 53 | unity_4LightAtten0[index] * squaredDistance); 54 | float3 diffuseReflection = attenuation 55 | * unity_LightColor[index].rgb; 56 | diffuseReflection *= max(0.0, dot(normal, lightDirection)); 57 | light += diffuseReflection; 58 | } 59 | return light; 60 | } 61 | 62 | void StdCommonVert(in vertexIn v, inout v2f o) 63 | { 64 | o.wPos = mul(unity_ObjectToWorld, v.vertex); 65 | o.pos = UnityWorldToClipPos(o.wPos); 66 | o.normal = UnityObjectToWorldNormal(v.normal); 67 | o.tangent = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w); 68 | o.bitangent = cross(o.normal, o.tangent.xyz) * o.tangent.w; 69 | TRANSFER_SHADOW(o); 70 | o.uv = TRANSFORM_TEX(v.uv0.xy, _MainTex); 71 | 72 | #ifdef LIGHTMAP_ON 73 | o.lightmapUV = v.uv1 * unity_LightmapST.xy + unity_LightmapST.zw; 74 | #endif 75 | 76 | } 77 | 78 | float3 tanToWrldNormal(float3 tNormal, v2f i) 79 | { 80 | float3x3 TangentToWorld = float3x3(i.tangent.x, i.bitangent.x, i.normal.x, 81 | i.tangent.y, i.bitangent.y, i.normal.y, 82 | i.tangent.z, i.bitangent.z, i.normal.z); 83 | 84 | float3 normal = normalize(mul(TangentToWorld, tNormal)); 85 | return normal; 86 | } 87 | 88 | float4 StdCommonFrag(v2f i, float4 albedoCol, float3 normal, float smoothness, float metallic) 89 | { 90 | UNITY_LIGHT_ATTENUATION(attenuation, i, i.wPos.xyz); 91 | 92 | float3 specularTint; 93 | float oneMinusReflectivity; 94 | 95 | float3 albedo = DiffuseAndSpecularFromMetallic( 96 | albedoCol, metallic, specularTint, oneMinusReflectivity 97 | ); 98 | 99 | float3 viewDir = normalize(_WorldSpaceCameraPos - i.wPos); 100 | UnityLight light; 101 | 102 | light.color = attenuation * _LightColor0.rgb; 103 | light.dir = normalize(UnityWorldSpaceLightDir(i.wPos)); 104 | UnityIndirect indirectLight; 105 | #ifdef UNITY_PASS_FORWARDADD 106 | indirectLight.diffuse = indirectLight.specular = 0; 107 | #else 108 | 109 | #ifdef LIGHTMAP_ON 110 | indirectLight.diffuse = float3(0, 0, 0); 111 | #else 112 | indirectLight.diffuse = max(0, ShadeSH9(float4(normal, 1))); 113 | #endif 114 | 115 | float3 reflectionDir = reflect(-viewDir, normal); 116 | 117 | Unity_GlossyEnvironmentData envData; 118 | envData.roughness = 1 - smoothness; 119 | envData.reflUVW = reflectionDir; 120 | indirectLight.specular = Unity_GlossyEnvironment( 121 | UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, envData 122 | ); 123 | #endif 124 | 125 | float3 col = UNITY_BRDF_PBS( 126 | albedo, specularTint, 127 | oneMinusReflectivity, smoothness, 128 | normal, viewDir, 129 | light, indirectLight 130 | ); 131 | 132 | 133 | #ifdef UNITY_PASS_FORWARDBASE 134 | #ifdef LIGHTMAP_ON 135 | float3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lightmapUV.xy)); 136 | col.rgb += (1 - metallic) * albedoCol.rgb * lm; 137 | #endif 138 | col.rgb += (1 - metallic) * albedoCol.rgb * vertex_lighting(i.wPos, normal); 139 | #endif 140 | 141 | #ifdef _ALPHAPREMULTIPLY_ON 142 | col.rgb *= albedoCol.a; 143 | //albedoCol.a = 1 - oneMinusReflectivity + albedoCol.a * oneMinusReflectivity; 144 | #endif 145 | 146 | #ifdef UNITY_PASS_FORWARDADD 147 | return float4(col, 0); 148 | #else 149 | return float4(col, albedoCol.a); 150 | #endif 151 | } 152 | 153 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardCommon.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 21f684d0aea37294d9811c10a2c36ead 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardMeta.cginc: -------------------------------------------------------------------------------- 1 | // Normal-map enabled Bakery-specific meta pass 2 | #pragma multi_compile_instancing 3 | #include"UnityStandardMeta.cginc" 4 | 5 | 6 | 7 | // Include Bakery meta pass 8 | 9 | #ifndef BAKERY_META 10 | #define BAKERY_META 11 | 12 | Texture2D bestFitNormalMap; 13 | 14 | float _IsFlipped; 15 | 16 | struct BakeryMetaInput 17 | { 18 | float2 uv0 : TEXCOORD0; 19 | float2 uv1 : TEXCOORD1; 20 | float3 normal : NORMAL; 21 | float4 tangent : TANGENT; 22 | UNITY_VERTEX_INPUT_INSTANCE_ID 23 | }; 24 | 25 | struct v2f_bakeryMeta 26 | { 27 | float4 pos : SV_POSITION; 28 | float4 uv : TEXCOORD0; 29 | float3 normal : TEXCOORD1; 30 | float3 tangent : TEXCOORD2; 31 | float3 binormal : TEXCOORD3; 32 | #ifdef EDITOR_VISUALIZATION 33 | float2 vizUV : TEXCOORD4; 34 | float4 lightCoord : TEXCOORD5; 35 | #endif 36 | }; 37 | 38 | v2f_bakeryMeta vert_bakerymt(BakeryMetaInput v) 39 | { 40 | v2f_bakeryMeta o; 41 | UNITY_SETUP_INSTANCE_ID(v); 42 | UNITY_INITIALIZE_OUTPUT(v2f_bakeryMeta, o); 43 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); 44 | o.pos = float4(((v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw) * 2 - 1) * float2(1,-1), 0.5, 1); 45 | o.uv = v.uv0.xyxy; 46 | o.normal = normalize(mul((float3x3)unity_ObjectToWorld, v.normal).xyz); 47 | o.tangent = normalize(mul((float3x3)unity_ObjectToWorld, v.tangent.xyz).xyz); 48 | o.binormal = cross(o.normal, o.tangent) * v.tangent.w * _IsFlipped; 49 | 50 | #ifdef EDITOR_VISUALIZATION 51 | o.vizUV = 0; 52 | o.lightCoord = 0; 53 | if (unity_VisualizationMode == EDITORVIZ_TEXTURE) 54 | o.vizUV = UnityMetaVizUV(unity_EditorViz_UVIndex, v.uv0.xy, v.uv1.xy, v.uv2.xy, unity_EditorViz_Texture_ST); 55 | else if (unity_VisualizationMode == EDITORVIZ_SHOWLIGHTMASK) 56 | { 57 | o.vizUV = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw; 58 | o.lightCoord = mul(unity_EditorViz_WorldToLight, mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1))); 59 | } 60 | #endif 61 | 62 | return o; 63 | } 64 | 65 | float3 EncodeNormalBestFit(float3 n) 66 | { 67 | float3 nU = abs(n); 68 | float maxNAbs = max(nU.z, max(nU.x, nU.y)); 69 | float2 TC = nU.z < maxNAbs ? (nU.y < maxNAbs ? nU.yz : nU.xz) : nU.xy; 70 | //if (TC.x != TC.y) 71 | //{ 72 | TC = TC.x < TC.y ? TC.yx : TC.xy; 73 | TC.y /= TC.x; 74 | 75 | n /= maxNAbs; 76 | float fittingScale = bestFitNormalMap.Load(int3(TC.x * 1023, TC.y * 1023, 0)).a; 77 | n *= fittingScale; 78 | //} 79 | return n * 0.5 + 0.5; 80 | } 81 | 82 | float3 TransformNormalMapToWorld(v2f_bakeryMeta i, float3 tangentNormalMap) 83 | { 84 | float3x3 TBN = float3x3(normalize(i.tangent), normalize(i.binormal), normalize(i.normal)); 85 | return mul(tangentNormalMap, TBN); 86 | } 87 | 88 | #define BakeryEncodeNormal EncodeNormalBestFit 89 | 90 | #endif 91 | 92 | float4 frag_customMeta (v2f_bakeryMeta i): SV_Target 93 | { 94 | UnityMetaInput o; 95 | UNITY_INITIALIZE_OUTPUT(UnityMetaInput, o); 96 | 97 | // Output custom normal to use with Bakery's "Baked Normal Map" mode 98 | if (unity_MetaFragmentControl.z) 99 | { 100 | // Calculate custom normal 101 | float3 customNormalMap = UnpackNormal(tex2D(_BumpMap, pow(abs(i.uv), 1.5))); // example: UVs are procedurally distorted 102 | float3 customWorldNormal = TransformNormalMapToWorld(i, customNormalMap); 103 | 104 | // Output 105 | return float4(BakeryEncodeNormal(customWorldNormal),1); 106 | } 107 | 108 | FragmentCommonData data = UNITY_SETUP_BRDF_INPUT(i.uv); 109 | // Regular Unity meta pass 110 | #ifdef EDITOR_VISUALIZATION 111 | o.Albedo = data.diffColor; 112 | o.VizUV = i.vizUV; 113 | o.LightCoord = i.lightCoord; 114 | #else 115 | o.Albedo = UnityLightmappingAlbedo (data.diffColor, data.specColor, data.smoothness); 116 | #endif 117 | o.SpecularColor = data.specColor; 118 | return UnityMetaFragment(o); 119 | } 120 | 121 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardMeta.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0bcb5a68fc680fb4e913ea4e862a3374 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardOpaque.shader: -------------------------------------------------------------------------------- 1 | // Based on d4rkpl4y3r's BRDF PBS Macro 2 | Shader "Error.mdl/PBR Opaque Random Tiling" 3 | { 4 | Properties 5 | { 6 | [Enum(Off, 0, Front, 1, Back, 2)] _Culling ("Culling Mode", Int) = 2 7 | _MainTex("Albedo Gaussian Texture", 2D) = "white" {} 8 | [NoScaleOffset] _MainTex_LUT("Albedo Lookup Table", 2DArray) = "white" {} 9 | _Color("Albedo Color", color) = (1,1,1,1) 10 | [NoScaleOffset] _MetallicGlossMap ("Metallic Gloss Gaussian", 2D) = "white" {} 11 | [NoScaleOffset] _MetGloss_LUT("Met-Gloss Lookup Table", 2DArray) = "white" {} 12 | [NoScaleOffset] _NormalMap("Normal Gaussian", 2D) = "white" {} 13 | [NoScaleOffset]_NormalMap_LUT("Normal Lookup Table", 2DArray) = "bump" {} 14 | [hdr] _Color("Albedo", Color) = (1,1,1,1) 15 | [Gamma] _Metallic("Metallic", Range(0, 1)) = 0 16 | _Smoothness("Smoothness/Roughness", Range(0, 1)) = 1 17 | _NormalStrength ("Normal Map Strength", float) = 1 18 | _TilingScale("Random Tiling Scale", float) = 1.732050808 19 | _CsCenter("Colorspace Center", Vector) = (0,0,0,0) 20 | _CX("Colorspace X", Vector) = (1, 0, 0, 1) 21 | _CY("Colorspace Y", Vector) = (0, 1, 0, 1) 22 | _CZ("Colorspace Z", Vector) = (0, 0, 1, 1) 23 | 24 | } 25 | SubShader 26 | { 27 | Tags 28 | { 29 | "RenderType"="Opaque" 30 | "Queue"="Geometry" 31 | } 32 | 33 | Cull [_Culling] 34 | 35 | Pass 36 | { 37 | Tags { "LightMode" = "ForwardBase" } 38 | CGPROGRAM 39 | #pragma vertex vert 40 | #pragma fragment frag 41 | #pragma multi_compile_fwdbase_fullshadows 42 | #pragma multi_compile UNITY_PASS_FORWARDBASE 43 | #pragma multi_compile _ LIGHTMAP_ON 44 | #pragma multi_compile_instancing 45 | #pragma target 5.0 46 | #include "RTStandardCG.cginc" 47 | ENDCG 48 | } 49 | 50 | Pass 51 | { 52 | Tags { "LightMode" = "ForwardAdd" } 53 | Blend One One 54 | CGPROGRAM 55 | #pragma vertex vert 56 | #pragma fragment frag 57 | #pragma multi_compile_fwdadd_fullshadows 58 | #pragma multi_compile_instancing 59 | #pragma multi_compile UNITY_PASS_FORWARDADD 60 | #pragma target 5.0 61 | #include "RTStandardCG.cginc" 62 | ENDCG 63 | } 64 | 65 | Pass 66 | { 67 | Tags { "LightMode" = "ShadowCaster" } 68 | CGPROGRAM 69 | #pragma vertex vert 70 | #pragma fragment frag 71 | #pragma multi_compile_shadowcaster 72 | #pragma multi_compile UNITY_PASS_SHADOWCASTER 73 | #pragma target 5.0 74 | 75 | #include "UnityCG.cginc" 76 | #include "Lighting.cginc" 77 | #include "AutoLight.cginc" 78 | #include "UnityPBSLighting.cginc" 79 | 80 | uniform float4 _Color; 81 | uniform float _Metallic; 82 | uniform float _Smoothness; 83 | uniform sampler2D _MainTex; 84 | uniform float4 _MainTex_ST; 85 | uniform float _Cutoff; 86 | 87 | struct v2f 88 | { 89 | V2F_SHADOW_CASTER; 90 | }; 91 | 92 | v2f vert(appdata_base v) 93 | { 94 | v2f o; 95 | 96 | TRANSFER_SHADOW_CASTER_NOPOS(o, o.pos); 97 | 98 | return o; 99 | } 100 | 101 | float4 frag(v2f i) : SV_Target 102 | { 103 | SHADOW_CASTER_FRAGMENT(i) 104 | } 105 | 106 | ENDCG 107 | } 108 | 109 | Pass 110 | { 111 | Name "META_BAKERY" 112 | Tags {"LightMode" = "Meta"} 113 | Cull Off 114 | CGPROGRAM 115 | // Must use vert_bakerymt vertex shader 116 | #pragma vertex vert_bakerymt 117 | #pragma fragment frag_customMeta 118 | #pragma shader_feature EDITOR_VISUALIZATION 119 | #include "RTStandardMeta.cginc" 120 | ENDCG 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /GaussianTex/shaders/PBRExample/RTStandardOpaque.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ff069ae92f00d34cb8fed0a67925302 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Error-mdl 4 | All rights reserved. 5 | 6 | The contents of this project may be licensed under either the MIT or NewBSD licenses. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its 19 | contributors may be used to endorse or promote products derived from 20 | this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 26 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityGaussianTex 2 | Editor utility that transforms a texture to have gaussian histogram and generates lookup table (LUT) that maps the transformed image back to the original image, for the purpose 3 | of detail preserving blending as described by [Deliot and Heitz (2019)](https://eheitzresearch.wordpress.com/738-2/). The authors of this paper created their own unity plugin available [here](https://github.com/UnityLabs/procedural-stochastic-texturing). The tool presented here was created based soley on the paper, prior to me knowing of the existence of Deliot and Heitz's own unity implementation. Hopefully, this tool still has some value as it is not tied to a particular render pipeline or version of unity, and is implemented very differently using compute shaders for most of the calculations, making it dramatically faster even in the tool's currently unoptimized state. 4 | 5 | # Purpose 6 | In shaders, it is sometimes useful to blend multiple samples of the same texture with different UV coordinates. For example this is done in triplanar mapping, where a texture is sampled three times using flattened world-space coordinates as UVs and the three samples are blended based on normal direction. This is also done in random tiling, where the UVs are divided into a triangular grid. Each grid point has an associated random UV offset, and each pixel blends between three texture samples taken with the offsets of the closest grid points. Doing a simple linear blend produces poor results in many cases, having reduced contrast with details from each texture being washed out and ghostly. 7 | 8 | Linear Blending | Variance Preserving Blending 9 | :---------------:|:---------------------------: 10 | ![Linear Blending](https://github.com/Error-mdl/UnityGaussianTex/blob/284daf7ef6b060c122e9e534ef47012c883bcfbf/Moss_LinearBlend.jpg) | ![Variance Preserving Blending](https://github.com/Error-mdl/UnityGaussianTex/blob/284daf7ef6b060c122e9e534ef47012c883bcfbf/Moss_VariancePreservingBlend.jpg) 11 | 12 | A different solution to blending the texture samples together was first proposed by [Heitz and Neyret (2018)](https://eheitzresearch.wordpress.com/722-2/) and later refined by Deliot and Heitz (2019). They observed that linear blending does not preserve the statistcal properties of the image, which is expressed by the histogram of the image. However, in the case where the input has a gaussian histogram there exists a different blending formula called a variance-preserving blend which retains the input's histogram. Their solution is to transform the input image's histogram to be gaussian using the inverse cumulative distribution function and to create a lookup table which maps the values in the gaussian-transformed texture back to the colors in the original image. Shaders using these textures first blend samples of the gaussian texture using the variance-preserving formula and then obtain the true color from the lookup table. This produces dramatically better results at the cost of only three (or four if alpha is needed) extra texture samples from the lookup table and a few other operations. 13 | 14 | Normal Tiling | Random Tiling with Variance Preserving Blending 15 | :---------------:|:---------------------------: 16 | ![Normal Tiling](https://github.com/Error-mdl/UnityGaussianTex/blob/284daf7ef6b060c122e9e534ef47012c883bcfbf/NormalTiling.jpg) | ![Variance Preserving Blending](https://github.com/Error-mdl/UnityGaussianTex/blob/284daf7ef6b060c122e9e534ef47012c883bcfbf/RandomTiling.jpg) 17 | # Details 18 | This script was based almost entirely off of Deliot and Heitz (2019), with a few minor adjustments from the Burley (2019) implementation. 19 | 20 | The first step performed by the script is optionally computing a new color-space for the image where each axis is independent, and converting the image to this new colorspace. When blending a gaussian texture derived from normal RGB images, it is possible to generate colors that never existed in the original input as the RGB colors may be correlated. To generate a colorspace where each axis is independent, the tool calculates the covariance matrix of the input's RGB channels and finds its eigenvectors. For simplicity, alpha is not included and assumed to be independent. The eigenvectors are calculated using the iterative formula for 3x3 symmetric, real matricies described by [Eberly (2021)](https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf). The new color space is constructed using the eigenvectors as the basis vectors. The space is then shifted so that it is centered on the minimum value of each axis in the image, and the basis vectors are scaled to the bounds of the images values so that all values are in the 0 to 1 range. This step of creating a de-correlated space is optional as not all images will obviously show false colors when blended, and my implementation seems to slightly reduce color accuracy (possibly due to compression issues or loss of precision during the creation of the gaussian texture?). 21 | 22 | The next step is to sort each color channel of the image. This is accomplished by a compute shader first splitting each color channel of the input into four compute buffers and generating four identical compute buffers that map the array index to flattened pixel coordinates. These four buffers will be sorted with each color channel, and are used to keep track of the original coordinates of each pixel. Then, each channel is sorted by a compute shader using the bitonic merge sort algorithm. This shader was written following [this tutorial by tgfrerer](https://poniesandlight.co.uk/reflect/bitonic_merge_sort/). 23 | 24 | Once the texture is sorted, the actual generation of the gaussian transformed texture and associated lookup table can begin. For each element of each sorted color channel, a compute shader calculates the inverse cumulative distribution function (invCDF) of the element's index divided by the total number of elements and stores the color in the output gaussian image at the coordinates stored in the associated index buffer. Computing the invCDF is not trivial, as it involves the inverse error function. This function is properly calculated by an infinite series that converges incredibly slowly for values close to 1. Methods which very accurately approximate the inverse error function in fewer steps usually involve complex tricks ill-suited to gpu computation. Rather than actually calculate the inverse error function proper, I have opted to use the approximation described Vedder (1987) which uses a function composed a few sinh and tanh functions. This approximation is massively simpler to compute and is close enough for our purposes to the true value on the 0-1 range. Calculating the LUT is similar, and involves calculating the cumulative distribution function of a value U ranging from 0 to 1, finding the colors stored in the each of the four color channels at the index equal to value of the CDF of U times the total number of elements, and storing it in the output lookup table at a flattened coordinate equal to U. Additionally, as suggested by [Burely (2019)](https://www.jcgt.org/published/0008/04/02/paper-lowres.pdf) the CDF and invCDF functions were scaled to ensure no clipping occurs for input values close to 0 or 1. 25 | 26 | In addition to the main LUT, different versions of the LUT must be generated for each mip level greater than the lowest mip using a different method. First the average variance of all 2^mip level blocks of pixels in the gaussian image is calculated. Then for each pixel of the LUT, the invCDF is calculated for (2x the number of elements in the LUT) values in the 0 to 1 range using the square root of the previously computed variance as the standard deviation and the element's index as the mean. These values are then averaged to get the final value at that element. 27 | 28 | Additionally, a small improvement over the Deliot and Heitz (2019) implementation was made here. Rather than store each LUT as a horizontal strip of pixels in a (LUT elements) x (mip levels) image, this script generates a texture array where each element of the array is a single LUT wrapped into a sqrt(LUT elements) x sqrt(LUT elements) image. For an average 256 element LUT, this creates a texture array of 16x16 images. This incurs a cost of a few math operations to turn the 1d coordinate from the gaussian texture into a 2d coordinate. However, sampling from this tiny square LUT should have much better cache hit rate than sampling from a 256 pixel long strip of a larger image. 29 | 30 | # Using the Editor Utility 31 | 32 | Included with this tool is a GUI for generating the gaussian texture and lookup table, and for assigning the colorspace data to a material from a colorspace asset. The GUI can be found under "Window/Convert Texture to Gaussian". 33 | 34 | ![Editor Window](https://github.com/Error-mdl/UnityGaussianTex/blob/284daf7ef6b060c122e9e534ef47012c883bcfbf/Window.png) 35 | 36 | Controls: 37 | 1. Convert Texture To Gaussian 38 | 1. Texture To Convert: Input field for the texture to be converted 39 | 1. Save as: Filetype to save the gaussian transformed texture as 40 | 1. Decorrolate Colorspace: Convert image's RGB color space to one with independent basis vectors before perfoming the gaussian transformation. This prevents colors that do not exist in the original image from being created by the blending process, but also seems to increase banding in some cases. Not recommended for red-alpha images like metallic-smoothness maps or grayscale images 41 | 1. Compression Correction: This is *supposed* to reduce issues with DXT compression by scaling the colors in the input texture by the inverse lengths of the colorspace vectors, but either I'm doing something wrong or this just plain causes compression issues in a lot of cases. 42 | 1. Lookup Table Dimensions: Width and height of each slice of the LUT texture2Darray, set as powers of 2 by the sliders below. 16x16 slices with 256 elements is ideal for most textures. Smaller dimensions decrease the number of possible colors but should improve the per-pixel cost of sampling the LUT, while larger dimensions get better color accuracy at the cost of slower sampling of the LUT. 43 | 1. Compute Shaders: dropdown underneath which the three compute shaders are assigned. These should be automatically assigned assuming the "shader" folder containing the compute shaders is in the same directory as the "scripts" folder containing TexToGaussian.cs 44 | 1. Create Gaussian Texture and Lookup Table: Pressing this button will create three assets in the same directory as the input texture with the same name as the input texture plus an additional identifier at the end. These are texture name + "_gauss.png" which is the gaussian texture, texture name + "_lut.asset" which is the lut saved as unity's texture2Darray asset, and texture name + "_colorspace.asset" which is a scriptable object containing the image's decorrolated colorspace basis vectors and center. 45 | 1. Copy Colorspace Settings to Material 46 | 1. Material to copy the colorspace settings to 47 | 1. Colorspace scriptable object to copy the settings from 48 | 1. Material Property Names: names of the material properties that store the colorspace basis vectors and center 49 | 50 | # Notes About Utilizing Gaussian Blending in Shaders 51 | 52 | Included with this tool is GaussianBlend.cginc, which defines many functions for blending multiple samples of gaussian textures, obtaining the color from the lookup texture, and converting the color from the decorrolated colorspace to RGB as well as functions for doing random tiling. An example of random tiling is included in shaders/Demo/BlendDemo.shader. See that shader for a basic implementation of doing random tiling on an unlit diffuse texture. Additionally, a more complex example of random tiling in a PBR shader is included in shaders/PBRExample/RTStandardOpaque 53 | 54 | All shaders using GaussianBlend.cginc need to add `#pragma target 5.0` to the header as the included functions use shader model 5 specific functions. Additionally, rather than defining the main texture as `sampler2D _MainTex;` use either the unity macro `UNITY_DECLARE_TEX2D(_MainTex)` or declare the texture and sampler separately like: 55 | ``` 56 | Texture2D _MainTex; 57 | sampler sampler_MainTex; 58 | ``` 59 | This is so you will have access to the sampler for calculating the mip level with CalcMipLevel. Also declare the LUT as just a Texture2DArray like `Texture2DArray _LUTTex;` as we do not need a sampler state for this texture. 60 | 61 | When blending the main albedo texture use the `Blend3GaussianRGB` or `Blend3GaussianRGBA` to get the variance-preserved blend of three samples of the gaussian texture, use CalcMipLevel with the uvs of one of the samples and the main texture sampler to get the mip level, then use `LookUpTableRGB` or `LookUpTableRGBA` to get the true color in the decorrolated colorspace, and then use ConvertColorspaceToRGB to get the final color. 62 | 63 | For supporting textures like the metallic-smoothness map where the color channels contain non-color, completely independent information it is advisable to generate the textures with "decorrolate colorspace" unchecked. It is not necessary and requires the shader have an additional 4 float4's to contain that texture's colorspace. The functions in the cginc for blending red-alpha (used for unity's metallic smoothness), red-green, and red (single channel or grayscale images) do not correct for decorrelated colorspaces. If you use a packed map that utilizes other channels you should use `Blend3GaussianRGBNoCs` or `Blend3GaussianRGBANoCs` which don't use a colorspace. In theses cases just directly use the color returned by the blend function. 64 | 65 | # References 66 | 67 | Deliot, Thomas, and Eric Heitz. "Procedural stochastic textures by tiling and blending." GPU Zen 2 (2019). [https://eheitzresearch.wordpress.com/738-2/](https://eheitzresearch.wordpress.com/738-2/) 68 | 69 | Burley, Brent, and Walt Disney Animation Studios. "On Histogram-Preserving Blending for Randomized Texture Tiling." Journal of Computer Graphics Techniques (JCGT) 8.4 (2019): 8. [https://www.jcgt.org/published/0008/04/02/paper-lowres.pdf](https://www.jcgt.org/published/0008/04/02/paper-lowres.pdf) 70 | 71 | Implementing Bitonic Merge Sort in Vulkan Compute. [https://poniesandlight.co.uk/reflect/bitonic_merge_sort/](https://poniesandlight.co.uk/reflect/bitonic_merge_sort/) 72 | 73 | Eberly, David. "A Robust Eigensolver for 3x3 Symmetric Matrices." Geometric Tools (2021). [https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf](https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf) 74 | 75 | Vedder, John D. "Simple approximations for the error function and its inverse." American Journal of Physics 55.8 (1987): 762-763. [https://aapt.scitation.org/doi/abs/10.1119/1.15018?journalCode=ajp](https://aapt.scitation.org/doi/abs/10.1119/1.15018?journalCode=ajp) 76 | --------------------------------------------------------------------------------