A game about forced loneliness, made by TACStudios
1#ifndef UNITY_COMMON_MATERIAL_INCLUDED
2#define UNITY_COMMON_MATERIAL_INCLUDED
3
4#if SHADER_API_MOBILE || SHADER_API_GLES3 || SHADER_API_SWITCH || defined(UNITY_UNIFIED_SHADER_PRECISION_MODEL)
5#pragma warning (disable : 3205) // conversion of larger type to smaller
6#endif
7
8//-----------------------------------------------------------------------------
9// Define constants
10//-----------------------------------------------------------------------------
11
12#define DEFAULT_SPECULAR_VALUE 0.04
13
14// Following constant are used when we use clear coat properties that can't be store in the Gbuffer (with the Lit shader)
15#define CLEAR_COAT_IOR 1.5
16#define CLEAR_COAT_IETA (1.0 / CLEAR_COAT_IOR) // IETA is the inverse eta which is the ratio of IOR of two interface
17#define CLEAR_COAT_F0 0.04 // IORToFresnel0(CLEAR_COAT_IOR)
18#define CLEAR_COAT_ROUGHNESS 0.01
19#define CLEAR_COAT_PERCEPTUAL_SMOOTHNESS RoughnessToPerceptualSmoothness(CLEAR_COAT_ROUGHNESS)
20#define CLEAR_COAT_PERCEPTUAL_ROUGHNESS RoughnessToPerceptualRoughness(CLEAR_COAT_ROUGHNESS)
21#define CLEAR_COAT_SSR_PERCEPTUAL_ROUGHNESS 0.0 // For screen space reflections and ray traced reflections, we want to have a purely smooth surface to map the envrionement light behavior
22
23//-----------------------------------------------------------------------------
24// Helper functions for roughness
25//-----------------------------------------------------------------------------
26
27
28#ifndef BUILTIN_TARGET_API
29real PerceptualRoughnessToRoughness(real perceptualRoughness)
30{
31 return perceptualRoughness * perceptualRoughness;
32}
33
34real RoughnessToPerceptualRoughness(real roughness)
35{
36 return sqrt(roughness);
37}
38#endif
39
40real RoughnessToPerceptualSmoothness(real roughness)
41{
42 return 1.0 - sqrt(roughness);
43}
44
45real PerceptualSmoothnessToRoughness(real perceptualSmoothness)
46{
47 return (1.0 - perceptualSmoothness) * (1.0 - perceptualSmoothness);
48}
49
50real PerceptualSmoothnessToPerceptualRoughness(real perceptualSmoothness)
51{
52 return (1.0 - perceptualSmoothness);
53}
54
55// Beckmann to GGX roughness "conversions":
56//
57// As also noted for NormalVariance in this file, Beckmann microfacet models use a Gaussian distribution of slopes
58// and the roughness parameter absorbs constants in the canonical Gaussian formula and is thus not exactly the variance.
59// The relationship is:
60//
61// roughnessBeckmann^2 = 2 variance (where variance is usually denoted sigma^2 but some comp gfx papers use sigma for
62// variance or even sigma for roughness itself.)
63//
64// Microfacet BRDF models with a GGX NDF implies a Cauchy distribution of slopes (also corresponds to the distribution
65// of slopes on an ellipsoid). Cauchy distributions don't have second moments, which precludes having a variance,
66// but chopping the far tails of GGX and keeping 94% of the mass yields a distribution with a defined variance where
67// we can then relate the roughness of GGX to a variance (see Ray Tracing Gems p153 - the reference is wrong though,
68// the Conty paper doesn't mention this at all, but it can be found in stats using quantiles):
69//
70// roughnessGGX^2 = variance / 2
71//
72// From the two previous, if we want roughly comparable variances of slopes between a Beckmann and a GGX NDF, we can
73// equate the variances and get a conversion of their roughnesses:
74//
75// 2 * roughnessGGX^2 = roughnessBeckmann^2 / 2 <==>
76// 4 * roughnessGGX^2 = roughnessBeckmann^2 <==>
77// 2 * roughnessGGX = roughnessBeckmann
78//
79// (Note that the Ray Tracing Gems paper makes an error on p154 writing sqrt(2) * roughnessGGX = roughnessBeckmann;
80// Their validation study using ray tracing and LEADR - which looks good - is for the *variance to GGX* roughness mapping,
81// not the Beckmann to GGX roughness "conversion")
82real BeckmannRoughnessToGGXRoughness(real roughnessBeckmann)
83{
84 return 0.5 * roughnessBeckmann;
85}
86
87real PerceptualRoughnessBeckmannToGGX(real perceptualRoughnessBeckmann)
88{
89 //sqrt(a_ggx) = sqrt(0.5) sqrt(a_beckmann)
90 return sqrt(0.5) * perceptualRoughnessBeckmann;
91}
92
93real GGXRoughnessToBeckmannRoughness(real roughnessGGX)
94{
95 return 2.0 * roughnessGGX;
96}
97
98real PerceptualRoughnessToPerceptualSmoothness(real perceptualRoughness)
99{
100 return (1.0 - perceptualRoughness);
101}
102
103// WARNING: this has been deprecated, and should not be used!
104// Using roughness values of 0 leads to INFs and NANs. The only sensible place to use the roughness
105// value of 0 is IBL, so we do not modify the perceptual roughness which is used to select the MIP map level.
106// Note: making the constant too small results in aliasing.
107real ClampRoughnessForAnalyticalLights(real roughness)
108{
109 return max(roughness, 1.0 / 1024.0);
110}
111
112// Given that the GGX model is invalid for a roughness of 0.0. This values have been experimentally evaluated to be the limit for the roughness
113// for integration.
114real ClampRoughnessForRaytracing(real roughness)
115{
116 return max(roughness, 0.001225);
117}
118real ClampPerceptualRoughnessForRaytracing(real perceptualRoughness)
119{
120 return max(perceptualRoughness, 0.035);
121}
122
123void ConvertValueAnisotropyToValueTB(real value, real anisotropy, out real valueT, out real valueB)
124{
125 // Use the parametrization of Sony Imageworks.
126 // Ref: Revisiting Physically Based Shading at Imageworks, p. 15.
127 valueT = value * (1 + anisotropy);
128 valueB = value * (1 - anisotropy);
129}
130
131void ConvertAnisotropyToRoughness(real perceptualRoughness, real anisotropy, out real roughnessT, out real roughnessB)
132{
133 real roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
134 ConvertValueAnisotropyToValueTB(roughness, anisotropy, roughnessT, roughnessB);
135}
136
137void ConvertRoughnessTAndAnisotropyToRoughness(real roughnessT, real anisotropy, out real roughness)
138{
139 roughness = roughnessT / (1 + anisotropy);
140}
141
142real ConvertRoughnessTAndBToRoughness(real roughnessT, real roughnessB)
143{
144 return 0.5 * (roughnessT + roughnessB);
145}
146
147void ConvertRoughnessToAnisotropy(real roughnessT, real roughnessB, out real anisotropy)
148{
149 anisotropy = ((roughnessT - roughnessB) / max(roughnessT + roughnessB, 0.0001));
150}
151
152// WARNING: this has been deprecated, and should not be used!
153// Same as ConvertAnisotropyToRoughness but
154// roughnessT and roughnessB are clamped, and are meant to be used with punctual and directional lights.
155void ConvertAnisotropyToClampRoughness(real perceptualRoughness, real anisotropy, out real roughnessT, out real roughnessB)
156{
157 ConvertAnisotropyToRoughness(perceptualRoughness, anisotropy, roughnessT, roughnessB);
158
159 roughnessT = ClampRoughnessForAnalyticalLights(roughnessT);
160 roughnessB = ClampRoughnessForAnalyticalLights(roughnessB);
161}
162
163// Use with stack BRDF (clear coat / coat) - This only used same equation to convert from Blinn-Phong spec power to Beckmann roughness
164real RoughnessToVariance(real roughness)
165{
166 return 2.0 / Sq(roughness) - 2.0;
167}
168
169real VarianceToRoughness(real variance)
170{
171 return sqrt(2.0 / (variance + 2.0));
172}
173
174// Normal Map Filtering - This must match HDRP\Editor\AssetProcessors\NormalMapFilteringTexturePostprocessor.cs - highestVarianceAllowed (TODO: Move in core)
175#define NORMALMAP_HIGHEST_VARIANCE 0.03125
176
177float DecodeVariance(float gradientW)
178{
179 return gradientW * NORMALMAP_HIGHEST_VARIANCE;
180}
181
182// Return modified perceptualSmoothness based on provided variance (get from GeometricNormalVariance + TextureNormalVariance)
183float NormalFiltering(float perceptualSmoothness, float variance, float threshold)
184{
185 float roughness = PerceptualSmoothnessToRoughness(perceptualSmoothness);
186 // Ref: Geometry into Shading - http://graphics.pixar.com/library/BumpRoughness/paper.pdf - equation (3)
187 float squaredRoughness = saturate(roughness * roughness + min(2.0 * variance, threshold * threshold)); // threshold can be really low, square the value for easier control
188
189 return RoughnessToPerceptualSmoothness(sqrt(squaredRoughness));
190}
191
192float ProjectedSpaceNormalFiltering(float perceptualSmoothness, float variance, float threshold)
193{
194 float roughness = PerceptualSmoothnessToRoughness(perceptualSmoothness);
195 // Ref: Stable Geometric Specular Antialiasing with Projected-Space NDF Filtering - https://yusuketokuyoshi.com/papers/2021/Tokuyoshi2021SAA.pdf
196 float squaredRoughness = roughness * roughness;
197 float projRoughness2 = squaredRoughness / (1.0 - squaredRoughness);
198 float filteredProjRoughness2 = saturate(projRoughness2 + min(2.0 * variance, threshold * threshold));
199 squaredRoughness = filteredProjRoughness2 / (filteredProjRoughness2 + 1.0f);
200
201 return RoughnessToPerceptualSmoothness(sqrt(squaredRoughness));
202}
203
204// Reference: Error Reduction and Simplification for Shading Anti-Aliasing
205// Specular antialiasing for geometry-induced normal (and NDF) variations: Tokuyoshi / Kaplanyan et al.'s method.
206// This is the deferred approximation, which works reasonably well so we keep it for forward too for now.
207// screenSpaceVariance should be at most 0.5^2 = 0.25, as that corresponds to considering
208// a gaussian pixel reconstruction kernel with a standard deviation of 0.5 of a pixel, thus 2 sigma covering the whole pixel.
209float GeometricNormalVariance(float3 geometricNormalWS, float screenSpaceVariance)
210{
211 float3 deltaU = ddx(geometricNormalWS);
212 float3 deltaV = ddy(geometricNormalWS);
213
214 return screenSpaceVariance * (dot(deltaU, deltaU) + dot(deltaV, deltaV));
215}
216
217// Return modified perceptualSmoothness
218float GeometricNormalFiltering(float perceptualSmoothness, float3 geometricNormalWS, float screenSpaceVariance, float threshold)
219{
220 float variance = GeometricNormalVariance(geometricNormalWS, screenSpaceVariance);
221 return NormalFiltering(perceptualSmoothness, variance, threshold);
222}
223
224float ProjectedSpaceGeometricNormalFiltering(float perceptualSmoothness, float3 geometricNormalWS, float screenSpaceVariance, float threshold)
225{
226 float variance = GeometricNormalVariance(geometricNormalWS, screenSpaceVariance);
227 return ProjectedSpaceNormalFiltering(perceptualSmoothness, variance, threshold);
228}
229
230// Normal map filtering based on The Order : 1886 SIGGRAPH course notes implementation.
231// Basically Toksvig with an intermediate single vMF lobe induced dispersion (Han et al. 2007)
232//
233// This returns 2 times the variance of the induced "mesoNDF" lobe (an NDF induced from a section of
234// the normal map) from the level 0 mip normals covered by the "current texel".
235//
236// avgNormalLength gives the dispersion information for the covered normals.
237//
238// Note that hw filtering on the normal map should be trilinear to be conservative, while anisotropic
239// risk underfiltering. Could also compute average normal on the fly with a proper normal map format,
240// like Toksvig.
241float TextureNormalVariance(float avgNormalLength)
242{
243 float variance = 0.0;
244
245 if (avgNormalLength < 1.0)
246 {
247 float avgNormLen2 = avgNormalLength * avgNormalLength;
248 float kappa = (3.0 * avgNormalLength - avgNormalLength * avgNormLen2) / (1.0 - avgNormLen2);
249
250 // Ref: Frequency Domain Normal Map Filtering - http://www.cs.columbia.edu/cg/normalmap/normalmap.pdf (equation 21)
251 // Relationship between between the standard deviation of a Gaussian distribution and the roughness parameter of a Beckmann distribution.
252 // is roughness^2 = 2 variance (note: variance is sigma^2)
253 // (Ref: Filtering Distributions of Normals for Shading Antialiasing - Equation just after (14))
254 // Relationship between gaussian lobe and vMF lobe is 2 * variance = 1 / (2 * kappa) = roughness^2
255 // (Equation 36 of Normal map filtering based on The Order : 1886 SIGGRAPH course notes implementation).
256 // So to get variance we must use variance = 1 / (4 * kappa)
257 variance = 0.25 / kappa;
258 }
259
260 return variance;
261}
262
263float TextureNormalFiltering(float perceptualSmoothness, float avgNormalLength, float threshold)
264{
265 float variance = TextureNormalVariance(avgNormalLength);
266 return NormalFiltering(perceptualSmoothness, variance, threshold);
267}
268
269// ----------------------------------------------------------------------------
270// Helper for Disney parametrization
271// ----------------------------------------------------------------------------
272
273float3 ComputeDiffuseColor(float3 baseColor, float metallic)
274{
275 return baseColor * (1.0 - metallic);
276}
277
278float3 ComputeFresnel0(float3 baseColor, float metallic, float dielectricF0)
279{
280 return lerp(dielectricF0.xxx, baseColor, metallic);
281}
282
283// ----------------------------------------------------------------------------
284// Helper for normal blending
285// ----------------------------------------------------------------------------
286
287// ref https://www.gamedev.net/topic/678043-how-to-blend-world-space-normals/#entry5287707
288// assume compositing in world space
289// Note: Using vtxNormal = real3(0, 0, 1) give the BlendNormalRNM formulation.
290// TODO: Untested
291real3 BlendNormalWorldspaceRNM(real3 n1, real3 n2, real3 vtxNormal)
292{
293 // Build the shortest-arc quaternion
294 real4 q = real4(cross(vtxNormal, n2), dot(vtxNormal, n2) + 1.0) / sqrt(2.0 * (dot(vtxNormal, n2) + 1));
295
296 // Rotate the normal
297 return n1 * (q.w * q.w - dot(q.xyz, q.xyz)) + 2 * q.xyz * dot(q.xyz, n1) + 2 * q.w * cross(q.xyz, n1);
298}
299
300// ref http://blog.selfshadow.com/publications/blending-in-detail/
301// ref https://gist.github.com/selfshadow/8048308
302// Reoriented Normal Mapping
303// Blending when n1 and n2 are already 'unpacked' and normalised
304// assume compositing in tangent space
305real3 BlendNormalRNM(real3 n1, real3 n2)
306{
307 real3 t = n1.xyz + real3(0.0, 0.0, 1.0);
308 real3 u = n2.xyz * real3(-1.0, -1.0, 1.0);
309 real3 r = (t / t.z) * dot(t, u) - u;
310 return r;
311}
312
313// assume compositing in tangent space
314real3 BlendNormal(real3 n1, real3 n2)
315{
316 return normalize(real3(n1.xy * n2.z + n2.xy * n1.z, n1.z * n2.z));
317}
318
319// ----------------------------------------------------------------------------
320// Helper for triplanar
321// ----------------------------------------------------------------------------
322
323// Ref: http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html / http://www.slideshare.net/icastano/cascades-demo-secrets
324real3 ComputeTriplanarWeights(real3 normal)
325{
326 // Determine the blend weights for the 3 planar projections.
327 real3 blendWeights = abs(normal);
328 // Tighten up the blending zone
329 blendWeights = (blendWeights - 0.2);
330 blendWeights = blendWeights * blendWeights * blendWeights; // pow(blendWeights, 3);
331 // Force weights to sum to 1.0 (very important!)
332 blendWeights = max(blendWeights, real3(0.0, 0.0, 0.0));
333 blendWeights /= dot(blendWeights, 1.0);
334
335 return blendWeights;
336}
337
338// Planar/Triplanar convention for Unity in world space
339void GetTriplanarCoordinate(float3 position, out float2 uvXZ, out float2 uvXY, out float2 uvZY)
340{
341 // Caution: This must follow the same rule as what is use for SurfaceGradient triplanar
342 // TODO: Currently the normal mapping looks wrong without SURFACE_GRADIENT option because we don't handle corretly the tangent space
343 uvXZ = float2(position.x, position.z);
344 uvXY = float2(position.x, position.y);
345 uvZY = float2(position.z, position.y);
346}
347
348// ----------------------------------------------------------------------------
349// Helper for detail map operation
350// ----------------------------------------------------------------------------
351
352real LerpWhiteTo(real b, real t)
353{
354 real oneMinusT = 1.0 - t;
355 return oneMinusT + b * t;
356}
357
358#ifndef BUILTIN_TARGET_API
359real3 LerpWhiteTo(real3 b, real t)
360{
361 real oneMinusT = 1.0 - t;
362 return real3(oneMinusT, oneMinusT, oneMinusT) + b * t;
363}
364#endif
365
366#if SHADER_API_MOBILE || SHADER_API_GLES3 || SHADER_API_SWITCH
367#pragma warning (enable : 3205) // conversion of larger type to smaller
368#endif
369
370#endif // UNITY_COMMON_MATERIAL_INCLUDED