├── 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 |  | 
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 |  | 
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 | 
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 |
--------------------------------------------------------------------------------