A game about forced loneliness, made by TACStudios
1#ifndef UNITY_SAMPLING_INCLUDED
2#define UNITY_SAMPLING_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// Sample generator
10//-----------------------------------------------------------------------------
11
12#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Fibonacci.hlsl"
13#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Hammersley.hlsl"
14
15//-----------------------------------------------------------------------------
16// Coordinate system conversion
17//-----------------------------------------------------------------------------
18
19// Transforms the unit vector from the spherical to the Cartesian (right-handed, Z up) coordinate.
20real3 SphericalToCartesian(real cosPhi, real sinPhi, real cosTheta)
21{
22 real sinTheta = SinFromCos(cosTheta);
23
24 return real3(real2(cosPhi, sinPhi) * sinTheta, cosTheta);
25}
26
27real3 SphericalToCartesian(real phi, real cosTheta)
28{
29 real sinPhi, cosPhi;
30 sincos(phi, sinPhi, cosPhi);
31
32 return SphericalToCartesian(cosPhi, sinPhi, cosTheta);
33}
34
35// Converts Cartesian coordinates given in the right-handed coordinate system
36// with Z pointing upwards (OpenGL style) to the coordinates in the left-handed
37// coordinate system with Y pointing up and Z facing forward (DirectX style).
38real3 TransformGLtoDX(real3 v)
39{
40 return v.xzy;
41}
42
43// Performs conversion from equiareal map coordinates to Cartesian (DirectX cubemap) ones.
44real3 ConvertEquiarealToCubemap(real u, real v)
45{
46 real phi = TWO_PI - TWO_PI * u;
47 real cosTheta = 1.0 - 2.0 * v;
48
49 return TransformGLtoDX(SphericalToCartesian(phi, cosTheta));
50}
51
52// Convert a texel position into normalized position [-1..1]x[-1..1]
53real2 CubemapTexelToNVC(uint2 unPositionTXS, uint cubemapSize)
54{
55 return 2.0 * real2(unPositionTXS) / real(max(cubemapSize - 1, 1)) - 1.0;
56}
57
58// Map cubemap face to world vector basis
59static const real3 CUBEMAP_FACE_BASIS_MAPPING[6][3] =
60{
61 //XPOS face
62 {
63 real3(0.0, 0.0, -1.0),
64 real3(0.0, -1.0, 0.0),
65 real3(1.0, 0.0, 0.0)
66 },
67 //XNEG face
68 {
69 real3(0.0, 0.0, 1.0),
70 real3(0.0, -1.0, 0.0),
71 real3(-1.0, 0.0, 0.0)
72 },
73 //YPOS face
74 {
75 real3(1.0, 0.0, 0.0),
76 real3(0.0, 0.0, 1.0),
77 real3(0.0, 1.0, 0.0)
78 },
79 //YNEG face
80 {
81 real3(1.0, 0.0, 0.0),
82 real3(0.0, 0.0, -1.0),
83 real3(0.0, -1.0, 0.0)
84 },
85 //ZPOS face
86 {
87 real3(1.0, 0.0, 0.0),
88 real3(0.0, -1.0, 0.0),
89 real3(0.0, 0.0, 1.0)
90 },
91 //ZNEG face
92 {
93 real3(-1.0, 0.0, 0.0),
94 real3(0.0, -1.0, 0.0),
95 real3(0.0, 0.0, -1.0)
96 }
97};
98
99// Convert a normalized cubemap face position into a direction
100real3 CubemapTexelToDirection(real2 positionNVC, uint faceId)
101{
102 real3 dir = CUBEMAP_FACE_BASIS_MAPPING[faceId][0] * positionNVC.x
103 + CUBEMAP_FACE_BASIS_MAPPING[faceId][1] * positionNVC.y
104 + CUBEMAP_FACE_BASIS_MAPPING[faceId][2];
105
106 return normalize(dir);
107}
108
109//-----------------------------------------------------------------------------
110// Sampling function
111// Reference : http://www.cs.virginia.edu/~jdl/bib/globillum/mis/shirley96.pdf + PBRT
112//-----------------------------------------------------------------------------
113
114// Performs uniform sampling of the unit disk.
115// Ref: PBRT v3, p. 777.
116real2 SampleDiskUniform(real u1, real u2)
117{
118 real r = sqrt(u1);
119 real phi = TWO_PI * u2;
120
121 real sinPhi, cosPhi;
122 sincos(phi, sinPhi, cosPhi);
123
124 return r * real2(cosPhi, sinPhi);
125}
126
127// Performs cubic sampling of the unit disk.
128real2 SampleDiskCubic(real u1, real u2)
129{
130 real r = u1;
131 real phi = TWO_PI * u2;
132
133 real sinPhi, cosPhi;
134 sincos(phi, sinPhi, cosPhi);
135
136 return r * real2(cosPhi, sinPhi);
137}
138
139real3 SampleConeUniform(real u1, real u2, real cos_theta)
140{
141 float r0 = cos_theta + u1 * (1.0f - cos_theta);
142 float r = sqrt(max(0.0, 1.0 - r0 * r0));
143 float phi = TWO_PI * u2;
144 return float3(r * cos(phi), r * sin(phi), r0);
145}
146
147real3 SampleSphereUniform(real u1, real u2)
148{
149 real phi = TWO_PI * u2;
150 real cosTheta = 1.0 - 2.0 * u1;
151
152 return SphericalToCartesian(phi, cosTheta);
153}
154
155// Performs cosine-weighted sampling of the hemisphere.
156// Ref: PBRT v3, p. 780.
157real3 SampleHemisphereCosine(real u1, real u2)
158{
159 real3 localL;
160
161 // Since we don't really care about the area distortion,
162 // we substitute uniform disk sampling for the concentric one.
163 localL.xy = SampleDiskUniform(u1, u2);
164
165 // Project the point from the disk onto the hemisphere.
166 localL.z = sqrt(1.0 - u1);
167
168 return localL;
169}
170
171// Cosine-weighted sampling without the tangent frame.
172// Ref: http://www.amietia.com/lambertnotangent.html
173real3 SampleHemisphereCosine(real u1, real u2, real3 normal)
174{
175 // This function needs to used safenormalize because there is a probability
176 // that the generated direction is the exact opposite of the normal and that would lead
177 // to a nan vector otheriwse.
178 real3 pointOnSphere = SampleSphereUniform(u1, u2);
179 return SafeNormalize(normal + pointOnSphere);
180}
181
182real3 SampleHemisphereUniform(real u1, real u2)
183{
184 real phi = TWO_PI * u2;
185 real cosTheta = 1.0 - u1;
186
187 return SphericalToCartesian(phi, cosTheta);
188}
189
190void SampleSphere(real2 u,
191 real4x4 localToWorld,
192 real radius,
193 out real lightPdf,
194 out real3 P,
195 out real3 Ns)
196{
197 real u1 = u.x;
198 real u2 = u.y;
199
200 Ns = SampleSphereUniform(u1, u2);
201
202 // Transform from unit sphere to world space
203 P = radius * Ns + localToWorld[3].xyz;
204
205 // pdf is inverse of area
206 lightPdf = 1.0 / (FOUR_PI * radius * radius);
207}
208
209void SampleHemisphere(real2 u,
210 real4x4 localToWorld,
211 real radius,
212 out real lightPdf,
213 out real3 P,
214 out real3 Ns)
215{
216 real u1 = u.x;
217 real u2 = u.y;
218
219 // Random point at hemisphere surface
220 Ns = -SampleHemisphereUniform(u1, u2); // We want the y down hemisphere
221 P = radius * Ns;
222
223 // Transform to world space
224 P = mul(real4(P, 1.0), localToWorld).xyz;
225 Ns = mul(Ns, (real3x3)(localToWorld));
226
227 // pdf is inverse of area
228 lightPdf = 1.0 / (TWO_PI * radius * radius);
229}
230
231// Note: The cylinder has no end caps (i.e. no disk on the side)
232void SampleCylinder(real2 u,
233 real4x4 localToWorld,
234 real radius,
235 real width,
236 out real lightPdf,
237 out real3 P,
238 out real3 Ns)
239{
240 real u1 = u.x;
241 real u2 = u.y;
242
243 // Random point at cylinder surface
244 real t = (u1 - 0.5) * width;
245 real theta = 2.0 * PI * u2;
246 real cosTheta = cos(theta);
247 real sinTheta = sin(theta);
248
249 // Cylinder are align on the right axis
250 P = real3(t, radius * cosTheta, radius * sinTheta);
251 Ns = normalize(real3(0.0, cosTheta, sinTheta));
252
253 // Transform to world space
254 P = mul(real4(P, 1.0), localToWorld).xyz;
255 Ns = mul(Ns, (real3x3)(localToWorld));
256
257 // pdf is inverse of area
258 lightPdf = 1.0 / (TWO_PI * radius * width);
259}
260
261void SampleRectangle(real2 u,
262 real4x4 localToWorld,
263 real width,
264 real height,
265 out real lightPdf,
266 out real3 P,
267 out real3 Ns)
268{
269 // Random point at rectangle surface
270 P = real3((u.x - 0.5) * width, (u.y - 0.5) * height, 0);
271 Ns = real3(0, 0, -1); // Light down (-Z)
272
273 // Transform to world space
274 P = mul(real4(P, 1.0), localToWorld).xyz;
275 Ns = mul(Ns, (real3x3)(localToWorld));
276
277 // pdf is inverse of area
278 lightPdf = 1.0 / (width * height);
279}
280
281void SampleDisk(real2 u,
282 real4x4 localToWorld,
283 real radius,
284 out real lightPdf,
285 out real3 P,
286 out real3 Ns)
287{
288 // Random point at disk surface
289 P = real3(radius * SampleDiskUniform(u.x, u.y), 0);
290 Ns = real3(0.0, 0.0, -1.0); // Light down (-Z)
291
292 // Transform to world space
293 P = mul(real4(P, 1.0), localToWorld).xyz;
294 Ns = mul(Ns, (real3x3)(localToWorld));
295
296 // pdf is inverse of area
297 lightPdf = 1.0 / (PI * radius * radius);
298}
299
300// Solid angle cone sampling.
301// Takes the cosine of the aperture as an input.
302void SampleCone(real2 u, real cosHalfAngle,
303 out real3 dir, out real rcpPdf)
304{
305 real cosTheta = lerp(1, cosHalfAngle, u.x);
306 real phi = TWO_PI * u.y;
307
308 dir = SphericalToCartesian(phi, cosTheta);
309 rcpPdf = TWO_PI * (1 - cosHalfAngle);
310}
311
312// Returns uniformly distributed sample vectors in a cone using
313// "golden angle spiral method" described here: http://blog.marmakoide.org/?p=1
314// note: the first sample is always [0, 0, 1]
315real3 SampleConeStrata(uint sampleIdx, real rcpSampleCount, real cosHalfApexAngle)
316{
317 real z = 1.0f - ((1.0f - cosHalfApexAngle) * sampleIdx) * rcpSampleCount;
318 real r = sqrt(1.0f - z * z);
319 real a = sampleIdx * 2.3999632297286f; // pi*(3-sqrt(5))
320 real sphi = sin(a);
321 real cphi = cos(a);
322 return real3(r * cphi, r * sphi, z);
323}
324
325#if SHADER_API_MOBILE || SHADER_API_GLES3 || SHADER_API_SWITCH
326#pragma warning (enable : 3205) // conversion of larger type to smaller
327#endif
328
329#endif // UNITY_SAMPLING_INCLUDED