three-gpu-pathtracer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +386 -0
- package/build/index.module.js +1825 -0
- package/build/index.module.js.map +1 -0
- package/build/index.umd.cjs +1840 -0
- package/build/index.umd.cjs.map +1 -0
- package/package.json +57 -0
- package/src/core/MaterialReducer.js +256 -0
- package/src/core/PathTracingRenderer.js +140 -0
- package/src/core/PathTracingSceneGenerator.js +46 -0
- package/src/index.js +21 -0
- package/src/materials/AmbientOcclusionMaterial.js +197 -0
- package/src/materials/LambertPathTracingMaterial.js +285 -0
- package/src/materials/MaterialBase.js +56 -0
- package/src/materials/PhysicalPathTracingMaterial.js +370 -0
- package/src/shader/shaderGGXFunctions.js +107 -0
- package/src/shader/shaderMaterialSampling.js +333 -0
- package/src/shader/shaderStructs.js +30 -0
- package/src/shader/shaderUtils.js +140 -0
- package/src/uniforms/EquirectPdfUniform.js +132 -0
- package/src/uniforms/MaterialStructArrayUniform.js +18 -0
- package/src/uniforms/MaterialStructUniform.js +94 -0
- package/src/uniforms/RenderTarget2DArray.js +80 -0
- package/src/utils/GeometryPreparationUtils.js +172 -0
- package/src/utils/UVUnwrapper.js +101 -0
- package/src/viewers/PathTracingViewer.js +259 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { Matrix4, Matrix3, Color } from 'three';
|
|
2
|
+
import { MaterialBase } from './MaterialBase.js';
|
|
3
|
+
import {
|
|
4
|
+
MeshBVHUniformStruct, FloatVertexAttributeTexture, UIntVertexAttributeTexture,
|
|
5
|
+
shaderStructs, shaderIntersectFunction,
|
|
6
|
+
} from 'three-mesh-bvh';
|
|
7
|
+
import { shaderMaterialStructs } from '../shader/shaderStructs.js';
|
|
8
|
+
import { MaterialStructArrayUniform } from '../uniforms/MaterialStructArrayUniform.js';
|
|
9
|
+
import { RenderTarget2DArray } from '../uniforms/RenderTarget2DArray.js';
|
|
10
|
+
import { shaderMaterialSampling } from '../shader/shaderMaterialSampling.js';
|
|
11
|
+
import { shaderUtils } from '../shader/shaderUtils.js';
|
|
12
|
+
|
|
13
|
+
export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
14
|
+
|
|
15
|
+
// three.js relies on this field to add env map functions and defines
|
|
16
|
+
get envMap() {
|
|
17
|
+
|
|
18
|
+
return this.environmentMap;
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
constructor( parameters ) {
|
|
23
|
+
|
|
24
|
+
super( {
|
|
25
|
+
|
|
26
|
+
transparent: true,
|
|
27
|
+
depthWrite: false,
|
|
28
|
+
|
|
29
|
+
defines: {
|
|
30
|
+
BOUNCES: 3,
|
|
31
|
+
TRANSPARENT_TRAVERSALS: 5,
|
|
32
|
+
MATERIAL_LENGTH: 0,
|
|
33
|
+
GRADIENT_BG: 0,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
uniforms: {
|
|
37
|
+
bvh: { value: new MeshBVHUniformStruct() },
|
|
38
|
+
normalAttribute: { value: new FloatVertexAttributeTexture() },
|
|
39
|
+
tangentAttribute: { value: new FloatVertexAttributeTexture() },
|
|
40
|
+
uvAttribute: { value: new FloatVertexAttributeTexture() },
|
|
41
|
+
materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
|
|
42
|
+
materials: { value: new MaterialStructArrayUniform() },
|
|
43
|
+
textures: { value: new RenderTarget2DArray().texture },
|
|
44
|
+
cameraWorldMatrix: { value: new Matrix4() },
|
|
45
|
+
invProjectionMatrix: { value: new Matrix4() },
|
|
46
|
+
environmentBlur: { value: 0.2 },
|
|
47
|
+
environmentIntensity: { value: 2.0 },
|
|
48
|
+
environmentMap: { value: null },
|
|
49
|
+
environmentRotation: { value: new Matrix3() },
|
|
50
|
+
seed: { value: 0 },
|
|
51
|
+
opacity: { value: 1 },
|
|
52
|
+
filterGlossyFactor: { value: 0.0 },
|
|
53
|
+
|
|
54
|
+
gradientTop: { value: new Color( 0xbfd8ff ) },
|
|
55
|
+
gradientBottom: { value: new Color( 0xffffff ) },
|
|
56
|
+
|
|
57
|
+
bgGradientTop: { value: new Color( 0x111111 ) },
|
|
58
|
+
bgGradientBottom: { value: new Color( 0x000000 ) },
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
vertexShader: /* glsl */`
|
|
62
|
+
|
|
63
|
+
varying vec2 vUv;
|
|
64
|
+
void main() {
|
|
65
|
+
|
|
66
|
+
vec4 mvPosition = vec4( position, 1.0 );
|
|
67
|
+
mvPosition = modelViewMatrix * mvPosition;
|
|
68
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
69
|
+
|
|
70
|
+
vUv = uv;
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
`,
|
|
75
|
+
|
|
76
|
+
fragmentShader: /* glsl */`
|
|
77
|
+
#define RAY_OFFSET 1e-5
|
|
78
|
+
|
|
79
|
+
precision highp isampler2D;
|
|
80
|
+
precision highp usampler2D;
|
|
81
|
+
precision highp sampler2DArray;
|
|
82
|
+
vec4 envMapTexelToLinear( vec4 a ) { return a; }
|
|
83
|
+
#include <common>
|
|
84
|
+
#include <cube_uv_reflection_fragment>
|
|
85
|
+
|
|
86
|
+
${ shaderStructs }
|
|
87
|
+
${ shaderIntersectFunction }
|
|
88
|
+
${ shaderMaterialStructs }
|
|
89
|
+
|
|
90
|
+
${ shaderUtils }
|
|
91
|
+
${ shaderMaterialSampling }
|
|
92
|
+
|
|
93
|
+
#ifdef USE_ENVMAP
|
|
94
|
+
|
|
95
|
+
uniform float environmentBlur;
|
|
96
|
+
uniform sampler2D environmentMap;
|
|
97
|
+
uniform mat3 environmentRotation;
|
|
98
|
+
|
|
99
|
+
#else
|
|
100
|
+
|
|
101
|
+
uniform vec3 gradientTop;
|
|
102
|
+
uniform vec3 gradientBottom;
|
|
103
|
+
|
|
104
|
+
#endif
|
|
105
|
+
|
|
106
|
+
#if GRADIENT_BG
|
|
107
|
+
|
|
108
|
+
uniform vec3 bgGradientTop;
|
|
109
|
+
uniform vec3 bgGradientBottom;
|
|
110
|
+
|
|
111
|
+
#endif
|
|
112
|
+
|
|
113
|
+
uniform mat4 cameraWorldMatrix;
|
|
114
|
+
uniform mat4 invProjectionMatrix;
|
|
115
|
+
uniform sampler2D normalAttribute;
|
|
116
|
+
uniform sampler2D tangentAttribute;
|
|
117
|
+
uniform sampler2D uvAttribute;
|
|
118
|
+
uniform usampler2D materialIndexAttribute;
|
|
119
|
+
uniform BVH bvh;
|
|
120
|
+
uniform float environmentIntensity;
|
|
121
|
+
uniform float filterGlossyFactor;
|
|
122
|
+
uniform int seed;
|
|
123
|
+
uniform float opacity;
|
|
124
|
+
uniform Material materials[ MATERIAL_LENGTH ];
|
|
125
|
+
uniform sampler2DArray textures;
|
|
126
|
+
varying vec2 vUv;
|
|
127
|
+
|
|
128
|
+
void main() {
|
|
129
|
+
|
|
130
|
+
rng_initialize( gl_FragCoord.xy, seed );
|
|
131
|
+
|
|
132
|
+
// get [-1, 1] normalized device coordinates
|
|
133
|
+
vec2 ndc = 2.0 * vUv - vec2( 1.0 );
|
|
134
|
+
vec3 rayOrigin, rayDirection;
|
|
135
|
+
ndcToCameraRay( ndc, cameraWorldMatrix, invProjectionMatrix, rayOrigin, rayDirection );
|
|
136
|
+
|
|
137
|
+
// Lambertian render
|
|
138
|
+
gl_FragColor = vec4( 0.0 );
|
|
139
|
+
|
|
140
|
+
vec3 throughputColor = vec3( 1.0 );
|
|
141
|
+
|
|
142
|
+
// hit results
|
|
143
|
+
uvec4 faceIndices = uvec4( 0u );
|
|
144
|
+
vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
|
|
145
|
+
vec3 barycoord = vec3( 0.0 );
|
|
146
|
+
float side = 1.0;
|
|
147
|
+
float dist = 0.0;
|
|
148
|
+
float accumulatedRoughness = 0.0;
|
|
149
|
+
int i;
|
|
150
|
+
int transparentTraversals = TRANSPARENT_TRAVERSALS;
|
|
151
|
+
for ( i = 0; i < BOUNCES; i ++ ) {
|
|
152
|
+
|
|
153
|
+
if ( ! bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
|
|
154
|
+
|
|
155
|
+
#if GRADIENT_BG
|
|
156
|
+
|
|
157
|
+
if ( i == 0 ) {
|
|
158
|
+
|
|
159
|
+
rayDirection = normalize( rayDirection + randDirection() * 0.05 );
|
|
160
|
+
float value = ( rayDirection.y + 1.0 ) / 2.0;
|
|
161
|
+
|
|
162
|
+
value = pow( value, 2.0 );
|
|
163
|
+
|
|
164
|
+
gl_FragColor = vec4( mix( bgGradientBottom, bgGradientTop, value ), 1.0 );
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
#endif
|
|
170
|
+
|
|
171
|
+
#ifdef USE_ENVMAP
|
|
172
|
+
|
|
173
|
+
vec3 skyColor = textureCubeUV( environmentMap, environmentRotation * rayDirection, environmentBlur ).rgb;
|
|
174
|
+
|
|
175
|
+
#else
|
|
176
|
+
|
|
177
|
+
rayDirection = normalize( rayDirection );
|
|
178
|
+
float value = ( rayDirection.y + 1.0 ) / 2.0;
|
|
179
|
+
vec3 skyColor = mix( gradientBottom, gradientTop, value );
|
|
180
|
+
|
|
181
|
+
#endif
|
|
182
|
+
|
|
183
|
+
gl_FragColor += vec4( skyColor * throughputColor * environmentIntensity, 1.0 );
|
|
184
|
+
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
|
|
190
|
+
Material material = materials[ materialIndex ];
|
|
191
|
+
|
|
192
|
+
vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
|
|
193
|
+
|
|
194
|
+
// albedo
|
|
195
|
+
vec4 albedo = vec4( material.color, material.opacity );
|
|
196
|
+
if ( material.map != - 1 ) {
|
|
197
|
+
|
|
198
|
+
albedo *= texture2D( textures, vec3( uv, material.map ) );
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// possibly skip this sample if it's transparent or alpha test is enabled
|
|
203
|
+
// alpha test is disabled when it === 0
|
|
204
|
+
float alphaTest = material.alphaTest;
|
|
205
|
+
bool useAlphaTest = alphaTest != 0.0;
|
|
206
|
+
if (
|
|
207
|
+
useAlphaTest && albedo.a < alphaTest
|
|
208
|
+
|| ! useAlphaTest && albedo.a < rand()
|
|
209
|
+
) {
|
|
210
|
+
|
|
211
|
+
vec3 point = rayOrigin + rayDirection * dist;
|
|
212
|
+
rayOrigin += rayDirection * dist - faceNormal * RAY_OFFSET;
|
|
213
|
+
|
|
214
|
+
// only allow a limited number of transparency discards otherwise we could
|
|
215
|
+
// crash the context with too long a loop.
|
|
216
|
+
i -= sign( transparentTraversals );
|
|
217
|
+
transparentTraversals -= sign( transparentTraversals );
|
|
218
|
+
continue;
|
|
219
|
+
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// fetch the interpolated smooth normal
|
|
223
|
+
vec3 normal = normalize( textureSampleBarycoord(
|
|
224
|
+
normalAttribute,
|
|
225
|
+
barycoord,
|
|
226
|
+
faceIndices.xyz
|
|
227
|
+
).xyz );
|
|
228
|
+
|
|
229
|
+
// roughness
|
|
230
|
+
float roughness = material.roughness;
|
|
231
|
+
if ( material.roughnessMap != - 1 ) {
|
|
232
|
+
|
|
233
|
+
roughness *= texture2D( textures, vec3( uv, material.roughnessMap ) ).g;
|
|
234
|
+
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// metalness
|
|
238
|
+
float metalness = material.metalness;
|
|
239
|
+
if ( material.metalnessMap != - 1 ) {
|
|
240
|
+
|
|
241
|
+
metalness *= texture2D( textures, vec3( uv, material.metalnessMap ) ).b;
|
|
242
|
+
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// emission
|
|
246
|
+
vec3 emission = material.emissiveIntensity * material.emissive;
|
|
247
|
+
if ( material.emissiveMap != - 1 ) {
|
|
248
|
+
|
|
249
|
+
emission *= texture2D( textures, vec3( uv, material.emissiveMap ) ).xyz;
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// transmission
|
|
254
|
+
float transmission = material.transmission;
|
|
255
|
+
if ( material.transmissionMap != - 1 ) {
|
|
256
|
+
|
|
257
|
+
transmission *= texture2D( textures, vec3( uv, material.transmissionMap ) ).r;
|
|
258
|
+
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// normal
|
|
262
|
+
if ( material.normalMap != - 1 ) {
|
|
263
|
+
|
|
264
|
+
vec4 tangentSample = textureSampleBarycoord(
|
|
265
|
+
tangentAttribute,
|
|
266
|
+
barycoord,
|
|
267
|
+
faceIndices.xyz
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// some provided tangents can be malformed (0, 0, 0) causing the normal to be degenerate
|
|
271
|
+
// resulting in NaNs and slow path tracing.
|
|
272
|
+
if ( length( tangentSample.xyz ) > 0.0 ) {
|
|
273
|
+
|
|
274
|
+
vec3 tangent = normalize( tangentSample.xyz );
|
|
275
|
+
vec3 bitangent = normalize( cross( normal, tangent ) * tangentSample.w );
|
|
276
|
+
mat3 vTBN = mat3( tangent, bitangent, normal );
|
|
277
|
+
|
|
278
|
+
vec3 texNormal = texture2D( textures, vec3( uv, material.normalMap ) ).xyz * 2.0 - 1.0;
|
|
279
|
+
texNormal.xy *= material.normalScale;
|
|
280
|
+
normal = vTBN * texNormal;
|
|
281
|
+
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
normal *= side;
|
|
287
|
+
|
|
288
|
+
SurfaceRec surfaceRec;
|
|
289
|
+
surfaceRec.normal = normal;
|
|
290
|
+
surfaceRec.faceNormal = faceNormal;
|
|
291
|
+
surfaceRec.frontFace = side == 1.0;
|
|
292
|
+
surfaceRec.transmission = transmission;
|
|
293
|
+
surfaceRec.ior = material.ior;
|
|
294
|
+
surfaceRec.emission = emission;
|
|
295
|
+
surfaceRec.metalness = metalness;
|
|
296
|
+
surfaceRec.color = albedo.rgb;
|
|
297
|
+
surfaceRec.roughness = roughness;
|
|
298
|
+
|
|
299
|
+
// Compute the filtered roughness value to use during specular reflection computations. A minimum
|
|
300
|
+
// value of 1e-6 is needed because the GGX functions do not work with a roughness value of 0 and
|
|
301
|
+
// the accumulated roughness value is scaled by a user setting and a "magic value" of 5.0.
|
|
302
|
+
// If we're exiting something transmissive then scale the factor down significantly so we can retain
|
|
303
|
+
// sharp internal reflections
|
|
304
|
+
surfaceRec.filteredRoughness = clamp(
|
|
305
|
+
max( surfaceRec.roughness, accumulatedRoughness * filterGlossyFactor * 5.0 ),
|
|
306
|
+
1e-3,
|
|
307
|
+
1.0
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
mat3 normalBasis = getBasisFromNormal( surfaceRec.normal );
|
|
311
|
+
mat3 invBasis = inverse( normalBasis );
|
|
312
|
+
|
|
313
|
+
vec3 outgoing = - normalize( invBasis * rayDirection );
|
|
314
|
+
SampleRec sampleRec = bsdfSample( outgoing, surfaceRec );
|
|
315
|
+
|
|
316
|
+
// adjust the hit point by the surface normal by a factor of some offset and the
|
|
317
|
+
// maximum component-wise value of the current point to accommodate floating point
|
|
318
|
+
// error as values increase.
|
|
319
|
+
vec3 point = rayOrigin + rayDirection * dist;
|
|
320
|
+
vec3 absPoint = abs( point );
|
|
321
|
+
float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
|
|
322
|
+
rayDirection = normalize( normalBasis * sampleRec.direction );
|
|
323
|
+
|
|
324
|
+
bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
|
|
325
|
+
rayOrigin = point + faceNormal * ( maxPoint + 1.0 ) * ( isBelowSurface ? - RAY_OFFSET : RAY_OFFSET );
|
|
326
|
+
|
|
327
|
+
// accumulate a roughness value to offset diffuse, specular, diffuse rays that have high contribution
|
|
328
|
+
// to a single pixel resulting in fireflies
|
|
329
|
+
if ( ! isBelowSurface ) {
|
|
330
|
+
|
|
331
|
+
// determine if this is a rough normal or not by checking how far off straight up it is
|
|
332
|
+
vec3 halfVector = normalize( outgoing + sampleRec.direction );
|
|
333
|
+
accumulatedRoughness += sin( acos( halfVector.z ) );
|
|
334
|
+
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// accumulate color
|
|
338
|
+
gl_FragColor.rgb += ( emission * throughputColor );
|
|
339
|
+
|
|
340
|
+
// skip the sample if our PDF or ray is impossible
|
|
341
|
+
if ( sampleRec.pdf <= 0.0 || ! isDirectionValid( rayDirection, normal, faceNormal) ) {
|
|
342
|
+
|
|
343
|
+
break;
|
|
344
|
+
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
throughputColor *= sampleRec.color / sampleRec.pdf;
|
|
348
|
+
|
|
349
|
+
// discard the sample if there are any NaNs
|
|
350
|
+
if ( any( isnan( throughputColor ) ) || any( isinf( throughputColor ) ) ) {
|
|
351
|
+
|
|
352
|
+
break;
|
|
353
|
+
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
gl_FragColor.a = opacity;
|
|
359
|
+
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
`
|
|
363
|
+
|
|
364
|
+
} );
|
|
365
|
+
|
|
366
|
+
this.setValues( parameters );
|
|
367
|
+
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export const shaderGGXFunctions = /* glsl */`
|
|
2
|
+
// The GGX functions provide sampling and distribution information for normals as output so
|
|
3
|
+
// in order to get probability of scatter direction the half vector must be computed and provided.
|
|
4
|
+
// [0] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
|
|
5
|
+
// [1] https://hal.archives-ouvertes.fr/hal-01509746/document
|
|
6
|
+
// [2] http://jcgt.org/published/0007/04/01/
|
|
7
|
+
// [4] http://jcgt.org/published/0003/02/03/
|
|
8
|
+
|
|
9
|
+
// trowbridge-reitz === GGX === GTR
|
|
10
|
+
|
|
11
|
+
vec3 ggxDirection( vec3 incidentDir, float roughnessX, float roughnessY, float random1, float random2 ) {
|
|
12
|
+
|
|
13
|
+
// TODO: try GGXVNDF implementation from reference [2], here. Needs to update ggxDistribution
|
|
14
|
+
// function below, as well
|
|
15
|
+
|
|
16
|
+
// Implementation from reference [1]
|
|
17
|
+
// stretch view
|
|
18
|
+
vec3 V = normalize( vec3( roughnessX * incidentDir.x, roughnessY * incidentDir.y, incidentDir.z ) );
|
|
19
|
+
|
|
20
|
+
// orthonormal basis
|
|
21
|
+
vec3 T1 = ( V.z < 0.9999 ) ? normalize( cross( V, vec3( 0.0, 0.0, 1.0 ) ) ) : vec3( 1.0, 0.0, 0.0 );
|
|
22
|
+
vec3 T2 = cross( T1, V );
|
|
23
|
+
|
|
24
|
+
// sample point with polar coordinates (r, phi)
|
|
25
|
+
float a = 1.0 / ( 1.0 + V.z );
|
|
26
|
+
float r = sqrt( random1 );
|
|
27
|
+
float phi = ( random2 < a ) ? random2 / a * PI : PI + ( random2 - a ) / ( 1.0 - a ) * PI;
|
|
28
|
+
float P1 = r * cos( phi );
|
|
29
|
+
float P2 = r * sin( phi ) * ( ( random2 < a ) ? 1.0 : V.z );
|
|
30
|
+
|
|
31
|
+
// compute normal
|
|
32
|
+
vec3 N = P1 * T1 + P2 * T2 + V * sqrt( max( 0.0, 1.0 - P1 * P1 - P2 * P2 ) );
|
|
33
|
+
|
|
34
|
+
// unstretch
|
|
35
|
+
N = normalize( vec3( roughnessX * N.x, roughnessY * N.y, max( 0.0, N.z ) ) );
|
|
36
|
+
|
|
37
|
+
return N;
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Below are PDF and related functions for use in a Monte Carlo path tracer
|
|
42
|
+
// as specified in Appendix B of the following paper
|
|
43
|
+
// See equation (2) from reference [2]
|
|
44
|
+
float ggxLamda( float theta, float roughness ) {
|
|
45
|
+
|
|
46
|
+
float tanTheta = tan( theta );
|
|
47
|
+
float tanTheta2 = tanTheta * tanTheta;
|
|
48
|
+
float alpha2 = roughness * roughness;
|
|
49
|
+
|
|
50
|
+
float numerator = - 1.0 + sqrt( 1.0 + alpha2 * tanTheta2 );
|
|
51
|
+
return numerator / 2.0;
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// See equation (2) from reference [2]
|
|
56
|
+
float ggxShadowMaskG1( float theta, float roughness ) {
|
|
57
|
+
|
|
58
|
+
return 1.0 / ( 1.0 + ggxLamda( theta, roughness ) );
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// See equation (125) from reference [4]
|
|
63
|
+
float ggxShadowMaskG2( vec3 wi, vec3 wo, float roughness ) {
|
|
64
|
+
|
|
65
|
+
float incidentTheta = acos( wi.z );
|
|
66
|
+
float scatterTheta = acos( wo.z );
|
|
67
|
+
return 1.0 / ( 1.0 + ggxLamda( incidentTheta, roughness ) + ggxLamda( scatterTheta, roughness ) );
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
float ggxDistribution( vec3 halfVector, float roughness ) {
|
|
72
|
+
|
|
73
|
+
// See equation (33) from reference [0]
|
|
74
|
+
float a2 = roughness * roughness;
|
|
75
|
+
float cosTheta = halfVector.z;
|
|
76
|
+
float cosTheta4 = pow( cosTheta, 4.0 );
|
|
77
|
+
|
|
78
|
+
if ( cosTheta == 0.0 ) return 0.0;
|
|
79
|
+
|
|
80
|
+
float theta = acos( halfVector.z );
|
|
81
|
+
float tanTheta = tan( theta );
|
|
82
|
+
float tanTheta2 = pow( tanTheta, 2.0 );
|
|
83
|
+
|
|
84
|
+
float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
|
|
85
|
+
return a2 / denom;
|
|
86
|
+
|
|
87
|
+
// See equation (1) from reference [2]
|
|
88
|
+
// const { x, y, z } = halfVector;
|
|
89
|
+
// const a2 = roughness * roughness;
|
|
90
|
+
// const mult = x * x / a2 + y * y / a2 + z * z;
|
|
91
|
+
// const mult2 = mult * mult;
|
|
92
|
+
|
|
93
|
+
// return 1.0 / Math.PI * a2 * mult2;
|
|
94
|
+
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// See equation (3) from reference [2]
|
|
98
|
+
float ggxPDF( vec3 wi, vec3 halfVector, float roughness ) {
|
|
99
|
+
|
|
100
|
+
float incidentTheta = acos( wi.z );
|
|
101
|
+
float D = ggxDistribution( halfVector, roughness );
|
|
102
|
+
float G1 = ggxShadowMaskG1( incidentTheta, roughness );
|
|
103
|
+
|
|
104
|
+
return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
`;
|