three-gpu-pathtracer 0.0.13 → 0.0.14

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.
Files changed (78) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +981 -961
  3. package/build/index.module.js +6965 -6508
  4. package/build/index.module.js.map +1 -1
  5. package/build/index.umd.cjs +6959 -6505
  6. package/build/index.umd.cjs.map +1 -1
  7. package/package.json +73 -73
  8. package/src/core/DynamicPathTracingSceneGenerator.js +119 -119
  9. package/src/core/MaterialReducer.js +256 -256
  10. package/src/core/PathTracingRenderer.js +346 -346
  11. package/src/core/PathTracingSceneGenerator.js +69 -69
  12. package/src/core/QuiltPathTracingRenderer.js +223 -223
  13. package/src/index.js +36 -40
  14. package/src/materials/MaterialBase.js +56 -56
  15. package/src/materials/{GraphMaterial.js → debug/GraphMaterial.js} +243 -243
  16. package/src/materials/{AlphaDisplayMaterial.js → fullscreen/AlphaDisplayMaterial.js} +48 -48
  17. package/src/materials/{BlendMaterial.js → fullscreen/BlendMaterial.js} +67 -67
  18. package/src/materials/{DenoiseMaterial.js → fullscreen/DenoiseMaterial.js} +142 -142
  19. package/src/materials/{LambertPathTracingMaterial.js → pathtracing/LambertPathTracingMaterial.js} +296 -285
  20. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +635 -0
  21. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +179 -0
  22. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +81 -0
  23. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +317 -0
  24. package/src/materials/pathtracing/glsl/traceScene.glsl.js +54 -0
  25. package/src/materials/{AmbientOcclusionMaterial.js → surface/AmbientOcclusionMaterial.js} +207 -199
  26. package/src/materials/surface/FogVolumeMaterial.js +23 -0
  27. package/src/objects/EquirectCamera.js +13 -13
  28. package/src/objects/PhysicalCamera.js +28 -28
  29. package/src/objects/PhysicalSpotLight.js +14 -14
  30. package/src/objects/ShapedAreaLight.js +12 -12
  31. package/src/shader/bsdf/bsdfSampling.glsl.js +490 -0
  32. package/src/shader/bsdf/fog.glsl.js +23 -0
  33. package/src/shader/bsdf/ggx.glsl.js +102 -0
  34. package/src/shader/bsdf/iridescence.glsl.js +135 -0
  35. package/src/shader/bsdf/sheen.glsl.js +98 -0
  36. package/src/shader/{shaderLayerTexelFetchFunctions.js → common/arraySamplerTexelFetch.glsl.js} +25 -25
  37. package/src/shader/common/bvhAnyHit.glsl.js +76 -0
  38. package/src/shader/common/fresnel.glsl.js +98 -0
  39. package/src/shader/common/intersectShapes.glsl.js +62 -0
  40. package/src/shader/common/math.glsl.js +81 -0
  41. package/src/shader/common/utils.glsl.js +116 -0
  42. package/src/shader/{shaderRandFunctions.js → rand/pcg.glsl.js} +57 -57
  43. package/src/shader/{shaderSobolSampling.js → rand/sobol.glsl.js} +256 -256
  44. package/src/shader/sampling/equirectSampling.glsl.js +62 -0
  45. package/src/shader/sampling/lightSampling.glsl.js +223 -0
  46. package/src/shader/sampling/shapeSampling.glsl.js +86 -0
  47. package/src/shader/structs/cameraStruct.glsl.js +13 -0
  48. package/src/shader/structs/equirectStruct.glsl.js +14 -0
  49. package/src/shader/structs/fogMaterialBvh.glsl.js +62 -0
  50. package/src/shader/structs/lightsStruct.glsl.js +78 -0
  51. package/src/shader/{shaderStructs.js → structs/materialStruct.glsl.js} +207 -327
  52. package/src/textures/GradientEquirectTexture.js +35 -35
  53. package/src/textures/ProceduralEquirectTexture.js +75 -75
  54. package/src/uniforms/AttributesTextureArray.js +35 -35
  55. package/src/uniforms/EquirectHdrInfoUniform.js +277 -273
  56. package/src/uniforms/FloatAttributeTextureArray.js +169 -169
  57. package/src/uniforms/IESProfilesTexture.js +100 -100
  58. package/src/uniforms/LightsInfoUniformStruct.js +212 -212
  59. package/src/uniforms/MaterialsTexture.js +503 -426
  60. package/src/uniforms/PhysicalCameraUniform.js +36 -36
  61. package/src/uniforms/RenderTarget2DArray.js +97 -97
  62. package/src/uniforms/utils.js +30 -30
  63. package/src/utils/BlurredEnvMapGenerator.js +116 -116
  64. package/src/utils/GeometryPreparationUtils.js +214 -214
  65. package/src/utils/IESLoader.js +325 -325
  66. package/src/utils/SobolNumberMapGenerator.js +80 -80
  67. package/src/utils/UVUnwrapper.js +101 -101
  68. package/src/utils/macroify.js +9 -0
  69. package/src/workers/PathTracingSceneWorker.js +42 -42
  70. package/src/materials/PhysicalPathTracingMaterial.js +0 -1013
  71. package/src/shader/shaderBvhAnyHit.js +0 -76
  72. package/src/shader/shaderEnvMapSampling.js +0 -58
  73. package/src/shader/shaderGGXFunctions.js +0 -100
  74. package/src/shader/shaderIridescenceFunctions.js +0 -135
  75. package/src/shader/shaderLightSampling.js +0 -229
  76. package/src/shader/shaderMaterialSampling.js +0 -510
  77. package/src/shader/shaderSheenFunctions.js +0 -98
  78. package/src/shader/shaderUtils.js +0 -377
@@ -1,1013 +0,0 @@
1
- import { Matrix4, Vector2 } from 'three';
2
- import { MaterialBase } from './MaterialBase.js';
3
- import {
4
- MeshBVHUniformStruct, UIntVertexAttributeTexture,
5
- shaderStructs, shaderIntersectFunction,
6
- } from 'three-mesh-bvh';
7
- import { shaderMaterialStructs, shaderLightStruct } from '../shader/shaderStructs.js';
8
- import { MaterialsTexture } from '../uniforms/MaterialsTexture.js';
9
- import { RenderTarget2DArray } from '../uniforms/RenderTarget2DArray.js';
10
- import { shaderMaterialSampling } from '../shader/shaderMaterialSampling.js';
11
- import { shaderEnvMapSampling } from '../shader/shaderEnvMapSampling.js';
12
- import { shaderLightSampling } from '../shader/shaderLightSampling.js';
13
- import { shaderSobolCommon, shaderSobolSampling } from '../shader/shaderSobolSampling.js';
14
- import { shaderUtils } from '../shader/shaderUtils.js';
15
- import { shaderLayerTexelFetchFunctions } from '../shader/shaderLayerTexelFetchFunctions.js';
16
- import { shaderRandFunctions } from '../shader/shaderRandFunctions.js';
17
- import { PhysicalCameraUniform } from '../uniforms/PhysicalCameraUniform.js';
18
- import { EquirectHdrInfoUniform } from '../uniforms/EquirectHdrInfoUniform.js';
19
- import { LightsInfoUniformStruct } from '../uniforms/LightsInfoUniformStruct.js';
20
- import { IESProfilesTexture } from '../uniforms/IESProfilesTexture.js';
21
- import { AttributesTextureArray } from '../uniforms/AttributesTextureArray.js';
22
-
23
- export class PhysicalPathTracingMaterial extends MaterialBase {
24
-
25
- onBeforeRender() {
26
-
27
- this.setDefine( 'FEATURE_DOF', this.physicalCamera.bokehSize === 0 ? 0 : 1 );
28
- this.setDefine( 'FEATURE_BACKGROUND_MAP', this.backgroundMap ? 1 : 0 );
29
-
30
- }
31
-
32
- constructor( parameters ) {
33
-
34
- super( {
35
-
36
- transparent: true,
37
- depthWrite: false,
38
-
39
- defines: {
40
- FEATURE_MIS: 1,
41
- FEATURE_RUSSIAN_ROULETTE: 1,
42
- FEATURE_DOF: 1,
43
- FEATURE_BACKGROUND_MAP: 0,
44
- // 0 = Perspective
45
- // 1 = Orthographic
46
- // 2 = Equirectangular
47
- CAMERA_TYPE: 0,
48
-
49
- ATTR_NORMAL: 0,
50
- ATTR_TANGENT: 1,
51
- ATTR_UV: 2,
52
- ATTR_COLOR: 3,
53
- },
54
-
55
- uniforms: {
56
- resolution: { value: new Vector2() },
57
-
58
- bounces: { value: 10 },
59
- transmissiveBounces: { value: 10 },
60
- physicalCamera: { value: new PhysicalCameraUniform() },
61
-
62
- bvh: { value: new MeshBVHUniformStruct() },
63
- attributesArray: { value: new AttributesTextureArray() },
64
- materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
65
- materials: { value: new MaterialsTexture() },
66
- textures: { value: new RenderTarget2DArray().texture },
67
- lights: { value: new LightsInfoUniformStruct() },
68
- iesProfiles: { value: new IESProfilesTexture().texture },
69
- cameraWorldMatrix: { value: new Matrix4() },
70
- invProjectionMatrix: { value: new Matrix4() },
71
- backgroundBlur: { value: 0.0 },
72
- environmentIntensity: { value: 1.0 },
73
- environmentRotation: { value: new Matrix4() },
74
- envMapInfo: { value: new EquirectHdrInfoUniform() },
75
- backgroundMap: { value: null },
76
-
77
- seed: { value: 0 },
78
- opacity: { value: 1 },
79
- filterGlossyFactor: { value: 0.0 },
80
-
81
- backgroundAlpha: { value: 1.0 },
82
- sobolTexture: { value: null },
83
- },
84
-
85
- vertexShader: /* glsl */`
86
-
87
- varying vec2 vUv;
88
- void main() {
89
-
90
- vec4 mvPosition = vec4( position, 1.0 );
91
- mvPosition = modelViewMatrix * mvPosition;
92
- gl_Position = projectionMatrix * mvPosition;
93
-
94
- vUv = uv;
95
-
96
- }
97
-
98
- `,
99
-
100
- fragmentShader: /* glsl */`
101
- #define RAY_OFFSET 1e-4
102
- #define INFINITY 1e20
103
-
104
- precision highp isampler2D;
105
- precision highp usampler2D;
106
- precision highp sampler2DArray;
107
- vec4 envMapTexelToLinear( vec4 a ) { return a; }
108
- #include <common>
109
-
110
- ${ shaderRandFunctions }
111
- ${ shaderSobolCommon }
112
- ${ shaderSobolSampling }
113
- ${ shaderStructs }
114
- ${ shaderIntersectFunction }
115
- ${ shaderMaterialStructs }
116
- ${ shaderLightStruct }
117
-
118
- ${ shaderLayerTexelFetchFunctions }
119
- ${ shaderUtils }
120
- ${ shaderMaterialSampling }
121
- ${ shaderEnvMapSampling }
122
-
123
- uniform mat4 environmentRotation;
124
- uniform float backgroundBlur;
125
- uniform float backgroundAlpha;
126
-
127
- #if FEATURE_BACKGROUND_MAP
128
-
129
- uniform sampler2D backgroundMap;
130
-
131
- #endif
132
-
133
- #if FEATURE_DOF
134
-
135
- uniform PhysicalCamera physicalCamera;
136
-
137
- #endif
138
-
139
- uniform vec2 resolution;
140
- uniform int bounces;
141
- uniform int transmissiveBounces;
142
- uniform mat4 cameraWorldMatrix;
143
- uniform mat4 invProjectionMatrix;
144
- uniform sampler2DArray attributesArray;
145
- uniform usampler2D materialIndexAttribute;
146
- uniform BVH bvh;
147
- uniform float environmentIntensity;
148
- uniform float filterGlossyFactor;
149
- uniform int seed;
150
- uniform float opacity;
151
- uniform sampler2D materials;
152
- uniform LightsInfo lights;
153
- uniform sampler2DArray iesProfiles;
154
-
155
- ${ shaderLightSampling }
156
-
157
- uniform EquirectHdrInfo envMapInfo;
158
-
159
- uniform sampler2DArray textures;
160
- varying vec2 vUv;
161
-
162
- float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
163
-
164
- return clamp(
165
- max(
166
- roughness,
167
- accumulatedRoughness * filterGlossyFactor * 5.0 ),
168
- 0.0,
169
- 1.0
170
- );
171
-
172
- }
173
-
174
- vec3 sampleBackground( vec3 direction, vec2 uv ) {
175
-
176
- vec3 sampleDir = normalize( direction + getHemisphereSample( direction, uv ) * 0.5 * backgroundBlur );
177
-
178
- #if FEATURE_BACKGROUND_MAP
179
-
180
- return sampleEquirectEnvMapColor( sampleDir, backgroundMap );
181
-
182
- #else
183
-
184
- return environmentIntensity * sampleEquirectEnvMapColor( sampleDir, envMapInfo.map );
185
-
186
- #endif
187
-
188
- }
189
-
190
- // step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
191
- bool attenuateHit(
192
- BVH bvh, vec3 rayOrigin, vec3 rayDirection, float rayDist,
193
- int traversals, int transparentTraversals, bool isShadowRay, out vec3 color
194
- ) {
195
-
196
- // hit results
197
- uvec4 faceIndices = uvec4( 0u );
198
- vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
199
- vec3 barycoord = vec3( 0.0 );
200
- float side = 1.0;
201
- float dist = 0.0;
202
-
203
- color = vec3( 1.0 );
204
-
205
- // TODO: we should be using sobol sampling here instead of rand but the sobol bounce and path indices need to be incremented
206
- // and then reset.
207
- for ( int i = 0; i < traversals; i ++ ) {
208
-
209
- if ( bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
210
-
211
- if ( dist > rayDist ) {
212
-
213
- return true;
214
-
215
- }
216
-
217
- // TODO: attenuate the contribution based on the PDF of the resulting ray including refraction values
218
- // Should be able to work using the material BSDF functions which will take into account specularity, etc.
219
- // TODO: should we account for emissive surfaces here?
220
-
221
- vec2 uv = textureSampleBarycoord( attributesArray, ATTR_UV, barycoord, faceIndices.xyz ).xy;
222
- vec4 vertexColor = textureSampleBarycoord( attributesArray, ATTR_COLOR, barycoord, faceIndices.xyz );
223
-
224
- uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
225
- Material material = readMaterialInfo( materials, materialIndex );
226
-
227
- // adjust the ray to the new surface
228
- bool isEntering = side == 1.0;
229
- rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
230
-
231
- if ( ! material.castShadow && isShadowRay ) {
232
-
233
- continue;
234
-
235
- }
236
-
237
- // Opacity Test
238
-
239
- // albedo
240
- vec4 albedo = vec4( material.color, material.opacity );
241
- if ( material.map != - 1 ) {
242
-
243
- vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
244
- albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
245
-
246
- }
247
-
248
- if ( material.vertexColors ) {
249
-
250
- albedo *= vertexColor;
251
-
252
- }
253
-
254
- // alphaMap
255
- if ( material.alphaMap != - 1 ) {
256
-
257
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
258
-
259
- }
260
-
261
- // transmission
262
- float transmission = material.transmission;
263
- if ( material.transmissionMap != - 1 ) {
264
-
265
- vec3 uvPrime = material.transmissionMapTransform * vec3( uv, 1 );
266
- transmission *= texture2D( textures, vec3( uvPrime.xy, material.transmissionMap ) ).r;
267
-
268
- }
269
-
270
- // metalness
271
- float metalness = material.metalness;
272
- if ( material.metalnessMap != - 1 ) {
273
-
274
- vec3 uvPrime = material.metalnessMapTransform * vec3( uv, 1 );
275
- metalness *= texture2D( textures, vec3( uvPrime.xy, material.metalnessMap ) ).b;
276
-
277
- }
278
-
279
- float alphaTest = material.alphaTest;
280
- bool useAlphaTest = alphaTest != 0.0;
281
- float transmissionFactor = ( 1.0 - metalness ) * transmission;
282
- if (
283
- transmissionFactor < rand() && ! (
284
- // material sidedness
285
- material.side != 0.0 && side == material.side
286
-
287
- // alpha test
288
- || useAlphaTest && albedo.a < alphaTest
289
-
290
- // opacity
291
- || material.transparent && ! useAlphaTest && albedo.a < rand()
292
- )
293
- ) {
294
-
295
- return true;
296
-
297
- }
298
-
299
- if ( side == 1.0 && isEntering ) {
300
-
301
- // only attenuate by surface color on the way in
302
- color *= mix( vec3( 1.0 ), albedo.rgb, transmissionFactor );
303
-
304
- } else if ( side == - 1.0 ) {
305
-
306
- // attenuate by medium once we hit the opposite side of the model
307
- color *= transmissionAttenuation( dist, material.attenuationColor, material.attenuationDistance );
308
-
309
- }
310
-
311
- bool isTransmissiveRay = dot( rayDirection, faceNormal * side ) < 0.0;
312
- if ( ( isTransmissiveRay || isEntering ) && transparentTraversals > 0 ) {
313
-
314
- transparentTraversals --;
315
- i --;
316
-
317
- }
318
-
319
-
320
- } else {
321
-
322
- return false;
323
-
324
- }
325
-
326
- }
327
-
328
- return true;
329
-
330
- }
331
-
332
- vec3 ndcToRayOrigin( vec2 coord ) {
333
-
334
- vec4 rayOrigin4 = cameraWorldMatrix * invProjectionMatrix * vec4( coord, - 1.0, 1.0 );
335
- return rayOrigin4.xyz / rayOrigin4.w;
336
- }
337
-
338
- void getCameraRay( out vec3 rayDirection, out vec3 rayOrigin ) {
339
-
340
- vec2 ssd = vec2( 1.0 ) / resolution;
341
-
342
- // Jitter the camera ray by finding a uv coordinate at a random sample
343
- // around this pixel's UV coordinate for AA
344
- vec2 ruv = sobol2( 0 );
345
- vec2 jitteredUv = vUv + vec2( tentFilter( ruv.x ) * ssd.x, tentFilter( ruv.y ) * ssd.y );
346
-
347
- #if CAMERA_TYPE == 2
348
-
349
- // Equirectangular projection
350
- vec4 rayDirection4 = vec4( equirectUvToDirection( jitteredUv ), 0.0 );
351
- vec4 rayOrigin4 = vec4( 0.0, 0.0, 0.0, 1.0 );
352
-
353
- rayDirection4 = cameraWorldMatrix * rayDirection4;
354
- rayOrigin4 = cameraWorldMatrix * rayOrigin4;
355
-
356
- rayDirection = normalize( rayDirection4.xyz );
357
- rayOrigin = rayOrigin4.xyz / rayOrigin4.w;
358
-
359
- #else
360
-
361
- // get [- 1, 1] normalized device coordinates
362
- vec2 ndc = 2.0 * jitteredUv - vec2( 1.0 );
363
- rayOrigin = ndcToRayOrigin( ndc );
364
-
365
- #if CAMERA_TYPE == 1
366
-
367
- // Orthographic projection
368
- rayDirection = ( cameraWorldMatrix * vec4( 0.0, 0.0, - 1.0, 0.0 ) ).xyz;
369
- rayDirection = normalize( rayDirection );
370
-
371
- #else
372
-
373
- // Perspective projection
374
- rayDirection = normalize( mat3(cameraWorldMatrix) * ( invProjectionMatrix * vec4( ndc, 0.0, 1.0 ) ).xyz );
375
-
376
- #endif
377
-
378
- #endif
379
-
380
- #if FEATURE_DOF
381
- {
382
-
383
- // depth of field
384
- vec3 focalPoint = rayOrigin + normalize( rayDirection ) * physicalCamera.focusDistance;
385
-
386
- // get the aperture sample
387
- // if blades === 0 then we assume a circle
388
- vec3 shapeUVW= sobol3( 1 );
389
- int blades = physicalCamera.apertureBlades;
390
- float anamorphicRatio = physicalCamera.anamorphicRatio;
391
- vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularNGon( blades, shapeUVW );
392
- apertureSample *= physicalCamera.bokehSize * 0.5 * 1e-3;
393
-
394
- // rotate the aperture shape
395
- apertureSample =
396
- rotateVector( apertureSample, physicalCamera.apertureRotation ) *
397
- saturate( vec2( anamorphicRatio, 1.0 / anamorphicRatio ) );
398
-
399
- // create the new ray
400
- rayOrigin += ( cameraWorldMatrix * vec4( apertureSample, 0.0, 0.0 ) ).xyz;
401
- rayDirection = focalPoint - rayOrigin;
402
-
403
- }
404
- #endif
405
-
406
- rayDirection = normalize( rayDirection );
407
-
408
- }
409
-
410
- void main() {
411
-
412
- rng_initialize( gl_FragCoord.xy, seed );
413
- sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | ( uint( gl_FragCoord.y ) );
414
- sobolPathIndex = uint( seed );
415
-
416
- vec3 rayDirection;
417
- vec3 rayOrigin;
418
-
419
- getCameraRay( rayDirection, rayOrigin );
420
-
421
- // inverse environment rotation
422
- mat3 envRotation3x3 = mat3( environmentRotation );
423
- mat3 invEnvRotation3x3 = inverse( envRotation3x3 );
424
-
425
- // final color
426
- gl_FragColor = vec4( 0.0 );
427
- gl_FragColor.a = 1.0;
428
-
429
- // hit results
430
- uvec4 faceIndices = uvec4( 0u );
431
- vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
432
- vec3 barycoord = vec3( 0.0 );
433
- float side = 1.0;
434
- float dist = 0.0;
435
-
436
- // path tracing state
437
- float accumulatedRoughness = 0.0;
438
- float accumulatedClearcoatRoughness = 0.0;
439
- bool transmissiveRay = true;
440
- int transparentTraversals = transmissiveBounces;
441
- vec3 throughputColor = vec3( 1.0 );
442
- SampleRec sampleRec;
443
- int i;
444
- bool isShadowRay = false;
445
-
446
- for ( i = 0; i < bounces; i ++ ) {
447
-
448
- sobolBounceIndex ++;
449
-
450
- bool hit = bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist );
451
- bool firstRay = i == 0 && transparentTraversals == transmissiveBounces;
452
- LightSampleRec lightHit = lightsClosestHit( lights.tex, lights.count, rayOrigin, rayDirection );
453
-
454
- if ( lightHit.hit && ( lightHit.dist < dist || ! hit ) ) {
455
-
456
- if ( firstRay || transmissiveRay ) {
457
-
458
- gl_FragColor.rgb += lightHit.emission * throughputColor;
459
-
460
- } else {
461
-
462
- #if FEATURE_MIS
463
-
464
- // NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
465
- if ( lightHit.type == SPOT_LIGHT_TYPE || lightHit.type == DIR_LIGHT_TYPE || lightHit.type == POINT_LIGHT_TYPE ) {
466
-
467
- gl_FragColor.rgb += lightHit.emission * throughputColor;
468
-
469
- } else {
470
-
471
- // weight the contribution
472
- float misWeight = misHeuristic( sampleRec.pdf, lightHit.pdf / float( lights.count + 1u ) );
473
- gl_FragColor.rgb += lightHit.emission * throughputColor * misWeight;
474
-
475
- }
476
-
477
- #else
478
-
479
- gl_FragColor.rgb += lightHit.emission * throughputColor;
480
-
481
- #endif
482
-
483
- }
484
- break;
485
-
486
- }
487
-
488
- if ( ! hit ) {
489
-
490
- if ( firstRay || transmissiveRay ) {
491
-
492
- gl_FragColor.rgb += sampleBackground( envRotation3x3 * rayDirection, sobol2( 2 ) ) * throughputColor;
493
- gl_FragColor.a = backgroundAlpha;
494
-
495
- } else {
496
-
497
- #if FEATURE_MIS
498
-
499
- // get the PDF of the hit envmap point
500
- vec3 envColor;
501
- float envPdf = sampleEnvMap( envMapInfo, envRotation3x3 * rayDirection, envColor );
502
- envPdf /= float( lights.count + 1u );
503
-
504
- // and weight the contribution
505
- float misWeight = misHeuristic( sampleRec.pdf, envPdf );
506
- gl_FragColor.rgb += environmentIntensity * envColor * throughputColor * misWeight;
507
-
508
- #else
509
-
510
- gl_FragColor.rgb +=
511
- environmentIntensity *
512
- sampleEquirectEnvMapColor( envRotation3x3 * rayDirection, envMapInfo.map ) *
513
- throughputColor;
514
-
515
- #endif
516
-
517
- }
518
- break;
519
-
520
- }
521
-
522
- uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
523
- Material material = readMaterialInfo( materials, materialIndex );
524
-
525
- if ( material.matte && firstRay ) {
526
-
527
- gl_FragColor = vec4( 0.0 );
528
- break;
529
-
530
- }
531
-
532
- // if we've determined that this is a shadow ray and we've hit an item with no shadow casting
533
- // then skip it
534
- if ( ! material.castShadow && isShadowRay ) {
535
-
536
- rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
537
- continue;
538
-
539
- }
540
-
541
- // uv coord for textures
542
- vec2 uv = textureSampleBarycoord( attributesArray, ATTR_UV, barycoord, faceIndices.xyz ).xy;
543
- vec4 vertexColor = textureSampleBarycoord( attributesArray, ATTR_COLOR, barycoord, faceIndices.xyz );
544
-
545
- // albedo
546
- vec4 albedo = vec4( material.color, material.opacity );
547
- if ( material.map != - 1 ) {
548
-
549
- vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
550
- albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
551
- }
552
-
553
- if ( material.vertexColors ) {
554
-
555
- albedo *= vertexColor;
556
-
557
- }
558
-
559
- // alphaMap
560
- if ( material.alphaMap != - 1 ) {
561
-
562
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
563
-
564
- }
565
-
566
- // possibly skip this sample if it's transparent, alpha test is enabled, or we hit the wrong material side
567
- // and it's single sided.
568
- // - alpha test is disabled when it === 0
569
- // - the material sidedness test is complicated because we want light to pass through the back side but still
570
- // be able to see the front side. This boolean checks if the side we hit is the front side on the first ray
571
- // and we're rendering the other then we skip it. Do the opposite on subsequent bounces to get incoming light.
572
- float alphaTest = material.alphaTest;
573
- bool useAlphaTest = alphaTest != 0.0;
574
- if (
575
- // material sidedness
576
- material.side != 0.0 && side != material.side
577
-
578
- // alpha test
579
- || useAlphaTest && albedo.a < alphaTest
580
-
581
- // opacity
582
- || material.transparent && ! useAlphaTest && albedo.a < sobol( 3 )
583
- ) {
584
-
585
- rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
586
-
587
- // only allow a limited number of transparency discards otherwise we could
588
- // crash the context with too long a loop.
589
- i -= sign( transparentTraversals );
590
- transparentTraversals -= sign( transparentTraversals );
591
- continue;
592
-
593
- }
594
-
595
- // fetch the interpolated smooth normal
596
- vec3 normal = normalize( textureSampleBarycoord(
597
- attributesArray,
598
- ATTR_NORMAL,
599
- barycoord,
600
- faceIndices.xyz
601
- ).xyz );
602
-
603
- // roughness
604
- float roughness = material.roughness;
605
- if ( material.roughnessMap != - 1 ) {
606
-
607
- vec3 uvPrime = material.roughnessMapTransform * vec3( uv, 1 );
608
- roughness *= texture2D( textures, vec3( uvPrime.xy, material.roughnessMap ) ).g;
609
-
610
- }
611
-
612
- // metalness
613
- float metalness = material.metalness;
614
- if ( material.metalnessMap != - 1 ) {
615
-
616
- vec3 uvPrime = material.metalnessMapTransform * vec3( uv, 1 );
617
- metalness *= texture2D( textures, vec3( uvPrime.xy, material.metalnessMap ) ).b;
618
-
619
- }
620
-
621
- // emission
622
- vec3 emission = material.emissiveIntensity * material.emissive;
623
- if ( material.emissiveMap != - 1 ) {
624
-
625
- vec3 uvPrime = material.emissiveMapTransform * vec3( uv, 1 );
626
- emission *= texture2D( textures, vec3( uvPrime.xy, material.emissiveMap ) ).xyz;
627
-
628
- }
629
-
630
- // transmission
631
- float transmission = material.transmission;
632
- if ( material.transmissionMap != - 1 ) {
633
-
634
- vec3 uvPrime = material.transmissionMapTransform * vec3( uv, 1 );
635
- transmission *= texture2D( textures, vec3( uvPrime.xy, material.transmissionMap ) ).r;
636
-
637
- }
638
-
639
- // normal
640
- if ( material.flatShading ) {
641
-
642
- // if we're rendering a flat shaded object then use the face normals - the face normal
643
- // is provided based on the side the ray hits the mesh so flip it to align with the
644
- // interpolated vertex normals.
645
- normal = faceNormal * side;
646
-
647
- }
648
-
649
- vec3 baseNormal = normal;
650
- if ( material.normalMap != - 1 ) {
651
-
652
- vec4 tangentSample = textureSampleBarycoord(
653
- attributesArray,
654
- ATTR_TANGENT,
655
- barycoord,
656
- faceIndices.xyz
657
- );
658
-
659
- // some provided tangents can be malformed (0, 0, 0) causing the normal to be degenerate
660
- // resulting in NaNs and slow path tracing.
661
- if ( length( tangentSample.xyz ) > 0.0 ) {
662
-
663
- vec3 tangent = normalize( tangentSample.xyz );
664
- vec3 bitangent = normalize( cross( normal, tangent ) * tangentSample.w );
665
- mat3 vTBN = mat3( tangent, bitangent, normal );
666
-
667
- vec3 uvPrime = material.normalMapTransform * vec3( uv, 1 );
668
- vec3 texNormal = texture2D( textures, vec3( uvPrime.xy, material.normalMap ) ).xyz * 2.0 - 1.0;
669
- texNormal.xy *= material.normalScale;
670
- normal = vTBN * texNormal;
671
-
672
- }
673
-
674
- }
675
-
676
- normal *= side;
677
-
678
- // clearcoat
679
- float clearcoat = material.clearcoat;
680
- if ( material.clearcoatMap != - 1 ) {
681
-
682
- vec3 uvPrime = material.clearcoatMapTransform * vec3( uv, 1 );
683
- clearcoat *= texture2D( textures, vec3( uvPrime.xy, material.clearcoatMap ) ).r;
684
-
685
- }
686
-
687
- // clearcoatRoughness
688
- float clearcoatRoughness = material.clearcoatRoughness;
689
- if ( material.clearcoatRoughnessMap != - 1 ) {
690
-
691
- vec3 uvPrime = material.clearcoatRoughnessMapTransform * vec3( uv, 1 );
692
- clearcoatRoughness *= texture2D( textures, vec3( uvPrime.xy, material.clearcoatRoughnessMap ) ).g;
693
-
694
- }
695
-
696
- // clearcoatNormal
697
- vec3 clearcoatNormal = baseNormal;
698
- if ( material.clearcoatNormalMap != - 1 ) {
699
-
700
- vec4 tangentSample = textureSampleBarycoord(
701
- attributesArray,
702
- ATTR_TANGENT,
703
- barycoord,
704
- faceIndices.xyz
705
- );
706
-
707
- // some provided tangents can be malformed (0, 0, 0) causing the normal to be degenerate
708
- // resulting in NaNs and slow path tracing.
709
- if ( length( tangentSample.xyz ) > 0.0 ) {
710
-
711
- vec3 tangent = normalize( tangentSample.xyz );
712
- vec3 bitangent = normalize( cross( clearcoatNormal, tangent ) * tangentSample.w );
713
- mat3 vTBN = mat3( tangent, bitangent, clearcoatNormal );
714
-
715
- vec3 uvPrime = material.clearcoatNormalMapTransform * vec3( uv, 1 );
716
- vec3 texNormal = texture2D( textures, vec3( uvPrime.xy, material.clearcoatNormalMap ) ).xyz * 2.0 - 1.0;
717
- texNormal.xy *= material.clearcoatNormalScale;
718
- clearcoatNormal = vTBN * texNormal;
719
-
720
- }
721
-
722
- }
723
-
724
- clearcoatNormal *= side;
725
-
726
- // sheenColor
727
- vec3 sheenColor = material.sheenColor;
728
- if ( material.sheenColorMap != - 1 ) {
729
-
730
- vec3 uvPrime = material.sheenColorMapTransform * vec3( uv, 1 );
731
- sheenColor *= texture2D( textures, vec3( uvPrime.xy, material.sheenColorMap ) ).rgb;
732
-
733
- }
734
-
735
- // sheenRoughness
736
- float sheenRoughness = material.sheenRoughness;
737
- if ( material.sheenRoughnessMap != - 1 ) {
738
-
739
- vec3 uvPrime = material.sheenRoughnessMapTransform * vec3( uv, 1 );
740
- sheenRoughness *= texture2D( textures, vec3( uvPrime.xy, material.sheenRoughnessMap ) ).a;
741
-
742
- }
743
-
744
- // iridescence
745
- float iridescence = material.iridescence;
746
- if ( material.iridescenceMap != - 1 ) {
747
-
748
- vec3 uvPrime = material.iridescenceMapTransform * vec3( uv, 1 );
749
- iridescence *= texture2D( textures, vec3( uvPrime.xy, material.iridescenceMap ) ).r;
750
-
751
- }
752
-
753
- // iridescence thickness
754
- float iridescenceThickness = material.iridescenceThicknessMaximum;
755
- if ( material.iridescenceThicknessMap != - 1 ) {
756
-
757
- vec3 uvPrime = material.iridescenceThicknessMapTransform * vec3( uv, 1 );
758
- float iridescenceThicknessSampled = texture2D( textures, vec3( uvPrime.xy, material.iridescenceThicknessMap ) ).g;
759
- iridescenceThickness = mix( material.iridescenceThicknessMinimum, material.iridescenceThicknessMaximum, iridescenceThicknessSampled );
760
-
761
- }
762
-
763
- iridescence = iridescenceThickness == 0.0 ? 0.0 : iridescence;
764
-
765
- // specular color
766
- vec3 specularColor = material.specularColor;
767
- if ( material.specularColorMap != - 1 ) {
768
-
769
- vec3 uvPrime = material.specularColorMapTransform * vec3( uv, 1 );
770
- specularColor *= texture2D( textures, vec3( uvPrime.xy, material.specularColorMap ) ).rgb;
771
-
772
- }
773
-
774
- // specular intensity
775
- float specularIntensity = material.specularIntensity;
776
- if ( material.specularIntensityMap != - 1 ) {
777
-
778
- vec3 uvPrime = material.specularIntensityMapTransform * vec3( uv, 1 );
779
- specularIntensity *= texture2D( textures, vec3( uvPrime.xy, material.specularIntensityMap ) ).a;
780
-
781
- }
782
-
783
- SurfaceRec surfaceRec;
784
- surfaceRec.normal = normal;
785
- surfaceRec.faceNormal = faceNormal;
786
- surfaceRec.transmission = transmission;
787
- surfaceRec.ior = material.ior;
788
- surfaceRec.emission = emission;
789
- surfaceRec.metalness = metalness;
790
- surfaceRec.color = albedo.rgb;
791
- surfaceRec.clearcoat = clearcoat;
792
- surfaceRec.sheen = material.sheen;
793
- surfaceRec.sheenColor = sheenColor;
794
- surfaceRec.iridescence = iridescence;
795
- surfaceRec.iridescenceIor = material.iridescenceIor;
796
- surfaceRec.iridescenceThickness = iridescenceThickness;
797
- surfaceRec.specularColor = specularColor;
798
- surfaceRec.specularIntensity = specularIntensity;
799
- surfaceRec.attenuationColor = material.attenuationColor;
800
- surfaceRec.attenuationDistance = material.attenuationDistance;
801
-
802
- // apply perceptual roughness factor from gltf. sheen perceptual roughness is
803
- // applied by its brdf function
804
- // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#microfacet-surfaces
805
- surfaceRec.roughness = roughness * roughness;
806
- surfaceRec.clearcoatRoughness = clearcoatRoughness * clearcoatRoughness;
807
- surfaceRec.sheenRoughness = sheenRoughness;
808
-
809
- // frontFace is used to determine transmissive properties and PDF. If no transmission is used
810
- // then we can just always assume this is a front face.
811
- surfaceRec.frontFace = side == 1.0 || transmission == 0.0;
812
- surfaceRec.eta = material.thinFilm || surfaceRec.frontFace ? 1.0 / material.ior : material.ior;
813
- surfaceRec.f0 = iorRatioToF0( surfaceRec.eta );
814
- surfaceRec.thinFilm = material.thinFilm;
815
-
816
- // Compute the filtered roughness value to use during specular reflection computations.
817
- // The accumulated roughness value is scaled by a user setting and a "magic value" of 5.0.
818
- // If we're exiting something transmissive then scale the factor down significantly so we can retain
819
- // sharp internal reflections
820
- surfaceRec.filteredRoughness = applyFilteredGlossy( surfaceRec.roughness, accumulatedRoughness );
821
- surfaceRec.filteredClearcoatRoughness = applyFilteredGlossy( surfaceRec.clearcoatRoughness, accumulatedClearcoatRoughness );
822
-
823
- mat3 normalBasis = getBasisFromNormal( surfaceRec.normal );
824
- mat3 invBasis = inverse( normalBasis );
825
-
826
- mat3 clearcoatNormalBasis = getBasisFromNormal( clearcoatNormal );
827
- mat3 clearcoatInvBasis = inverse( clearcoatNormalBasis );
828
-
829
- vec3 outgoing = - normalize( invBasis * rayDirection );
830
- vec3 clearcoatOutgoing = - normalize( clearcoatInvBasis * rayDirection );
831
- sampleRec = bsdfSample( outgoing, clearcoatOutgoing, normalBasis, invBasis, clearcoatNormalBasis, clearcoatInvBasis, surfaceRec );
832
-
833
- bool wasBelowSurface = dot( rayDirection, faceNormal ) > 0.0;
834
- isShadowRay = sampleRec.specularPdf < sobol( 4 );
835
-
836
- vec3 prevRayDirection = rayDirection;
837
- rayDirection = normalize( normalBasis * sampleRec.direction );
838
-
839
- bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
840
- rayOrigin = stepRayOrigin( rayOrigin, prevRayDirection, isBelowSurface ? - faceNormal : faceNormal, dist );
841
-
842
- // direct env map sampling
843
- #if FEATURE_MIS
844
-
845
- // uniformly pick a light or environment map
846
- if( sobol( 5 ) > 1.0 / float( lights.count + 1u ) ) {
847
-
848
- // sample a light or environment
849
- LightSampleRec lightSampleRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, sobol3( 6 ) );
850
-
851
- bool isSampleBelowSurface = dot( faceNormal, lightSampleRec.direction ) < 0.0;
852
- if ( isSampleBelowSurface ) {
853
-
854
- lightSampleRec.pdf = 0.0;
855
-
856
- }
857
-
858
- // check if a ray could even reach the light area
859
- vec3 attenuatedColor;
860
- if (
861
- lightSampleRec.pdf > 0.0 &&
862
- isDirectionValid( lightSampleRec.direction, normal, faceNormal ) &&
863
- ! attenuateHit( bvh, rayOrigin, lightSampleRec.direction, lightSampleRec.dist, bounces - i, transparentTraversals, isShadowRay, attenuatedColor )
864
- ) {
865
-
866
- // get the material pdf
867
- vec3 sampleColor;
868
- float lightMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * lightSampleRec.direction ), normalize( clearcoatInvBasis * lightSampleRec.direction ), surfaceRec, sampleColor );
869
- bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
870
- if ( lightMaterialPdf > 0.0 && isValidSampleColor ) {
871
-
872
- // weight the direct light contribution
873
- float lightPdf = lightSampleRec.pdf / float( lights.count + 1u );
874
- float misWeight = lightSampleRec.type == SPOT_LIGHT_TYPE || lightSampleRec.type == DIR_LIGHT_TYPE || lightSampleRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
875
- gl_FragColor.rgb += attenuatedColor * lightSampleRec.emission * throughputColor * sampleColor * misWeight / lightPdf;
876
-
877
- }
878
-
879
- }
880
-
881
- } else {
882
-
883
- // find a sample in the environment map to include in the contribution
884
- vec3 envColor, envDirection;
885
- float envPdf = sampleEnvMapProbability( envMapInfo, sobol2( 7 ), envColor, envDirection );
886
- envDirection = invEnvRotation3x3 * envDirection;
887
-
888
- // this env sampling is not set up for transmissive sampling and yields overly bright
889
- // results so we ignore the sample in this case.
890
- // TODO: this should be improved but how? The env samples could traverse a few layers?
891
- bool isSampleBelowSurface = dot( faceNormal, envDirection ) < 0.0;
892
- if ( isSampleBelowSurface ) {
893
-
894
- envPdf = 0.0;
895
-
896
- }
897
-
898
- // check if a ray could even reach the surface
899
- vec3 attenuatedColor;
900
- if (
901
- envPdf > 0.0 &&
902
- isDirectionValid( envDirection, normal, faceNormal ) &&
903
- ! attenuateHit( bvh, rayOrigin, envDirection, INFINITY, bounces - i, transparentTraversals, isShadowRay, attenuatedColor )
904
- ) {
905
-
906
- // get the material pdf
907
- vec3 sampleColor;
908
- float envMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * envDirection ), normalize( clearcoatInvBasis * envDirection ), surfaceRec, sampleColor );
909
- bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
910
- if ( envMaterialPdf > 0.0 && isValidSampleColor ) {
911
-
912
- // weight the direct light contribution
913
- envPdf /= float( lights.count + 1u );
914
- float misWeight = misHeuristic( envPdf, envMaterialPdf );
915
- gl_FragColor.rgb += attenuatedColor * environmentIntensity * envColor * throughputColor * sampleColor * misWeight / envPdf;
916
-
917
- }
918
-
919
- }
920
-
921
- }
922
- #endif
923
-
924
- // accumulate a roughness value to offset diffuse, specular, diffuse rays that have high contribution
925
- // to a single pixel resulting in fireflies
926
- if ( ! isBelowSurface ) {
927
-
928
- // determine if this is a rough normal or not by checking how far off straight up it is
929
- vec3 halfVector = normalize( outgoing + sampleRec.direction );
930
- accumulatedRoughness += sin( acosApprox( halfVector.z ) );
931
-
932
- vec3 clearcoatHalfVector = normalize( clearcoatOutgoing + sampleRec.clearcoatDirection );
933
- accumulatedClearcoatRoughness += sin( acosApprox( clearcoatHalfVector.z ) );
934
-
935
- transmissiveRay = false;
936
-
937
- }
938
-
939
- // if we're bouncing around the inside a transmissive material then decrement
940
- // perform this separate from a bounce
941
- bool isTransmissiveRay = dot( rayDirection, faceNormal * side ) < 0.0;
942
- if ( ( isTransmissiveRay || isBelowSurface ) && transparentTraversals > 0 ) {
943
-
944
- transparentTraversals --;
945
- i --;
946
-
947
- }
948
-
949
- // accumulate color
950
- gl_FragColor.rgb += ( emission * throughputColor );
951
-
952
- // skip the sample if our PDF or ray is impossible
953
- if ( sampleRec.pdf <= 0.0 || ! isDirectionValid( rayDirection, normal, faceNormal) ) {
954
-
955
- break;
956
-
957
- }
958
-
959
- #if FEATURE_RUSSIAN_ROULETTE
960
-
961
- // russian roulette path termination
962
- // https://www.arnoldrenderer.com/research/physically_based_shader_design_in_arnold.pdf
963
- uint minBounces = 3u;
964
- float depthProb = float( sobolBounceIndex < minBounces );
965
-
966
- float rrProb = luminance( throughputColor * sampleRec.color / sampleRec.pdf );
967
- rrProb /= luminance( throughputColor );
968
- rrProb = sqrt( rrProb );
969
- rrProb = max( rrProb, depthProb );
970
- rrProb = min( rrProb, 1.0 );
971
- if ( sobol( 8 ) > rrProb ) {
972
-
973
- break;
974
-
975
- }
976
-
977
- // perform sample clamping here to avoid bright pixels
978
- throughputColor *= min( 1.0 / rrProb, 20.0 );
979
-
980
- #endif
981
-
982
- throughputColor *= sampleRec.color / sampleRec.pdf;
983
-
984
- // attenuate the throughput color by the medium color
985
- if ( side == - 1.0 ) {
986
-
987
- throughputColor *= transmissionAttenuation( dist, surfaceRec.attenuationColor, surfaceRec.attenuationDistance );
988
-
989
- }
990
-
991
- // discard the sample if there are any NaNs
992
- if ( any( isnan( throughputColor ) ) || any( isinf( throughputColor ) ) ) {
993
-
994
- break;
995
-
996
- }
997
-
998
-
999
- }
1000
-
1001
- gl_FragColor.a *= opacity;
1002
-
1003
- }
1004
-
1005
- `
1006
-
1007
- } );
1008
-
1009
- this.setValues( parameters );
1010
-
1011
- }
1012
-
1013
- }