three-gpu-pathtracer 0.0.6 → 0.0.7

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.
@@ -52,6 +52,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
52
52
  normalAttribute: { value: new FloatVertexAttributeTexture() },
53
53
  tangentAttribute: { value: new FloatVertexAttributeTexture() },
54
54
  uvAttribute: { value: new FloatVertexAttributeTexture() },
55
+ colorAttribute: { value: new FloatVertexAttributeTexture() },
55
56
  materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
56
57
  materials: { value: new MaterialsTexture() },
57
58
  textures: { value: new RenderTarget2DArray().texture },
@@ -130,6 +131,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
130
131
  uniform sampler2D normalAttribute;
131
132
  uniform sampler2D tangentAttribute;
132
133
  uniform sampler2D uvAttribute;
134
+ uniform sampler2D colorAttribute;
133
135
  uniform usampler2D materialIndexAttribute;
134
136
  uniform BVH bvh;
135
137
  uniform float environmentIntensity;
@@ -147,6 +149,18 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
147
149
  uniform sampler2DArray textures;
148
150
  varying vec2 vUv;
149
151
 
152
+ float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
153
+
154
+ return clamp(
155
+ max(
156
+ roughness,
157
+ accumulatedRoughness * filterGlossyFactor * 5.0 ),
158
+ 0.0,
159
+ 1.0
160
+ );
161
+
162
+ }
163
+
150
164
  vec3 sampleBackground( vec3 direction ) {
151
165
 
152
166
  #if FEATURE_GRADIENT_BG
@@ -188,6 +202,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
188
202
  // TODO: should we account for emissive surfaces here?
189
203
 
190
204
  vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
205
+ vec4 vertexColor = textureSampleBarycoord( colorAttribute, barycoord, faceIndices.xyz );
191
206
  uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
192
207
  Material material = readMaterialInfo( materials, materialIndex );
193
208
 
@@ -215,8 +230,14 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
215
230
 
216
231
  }
217
232
 
233
+ if ( material.vertexColors ) {
234
+
235
+ albedo *= vertexColor;
236
+
237
+ }
238
+
218
239
  // alphaMap
219
- if ( material.alphaMap != -1 ) {
240
+ if ( material.alphaMap != - 1 ) {
220
241
 
221
242
  albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
222
243
 
@@ -252,7 +273,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
252
273
  || useAlphaTest && albedo.a < alphaTest
253
274
 
254
275
  // opacity
255
- || ! useAlphaTest && albedo.a < rand()
276
+ || material.transparent && ! useAlphaTest && albedo.a < rand()
256
277
  )
257
278
  ) {
258
279
 
@@ -260,11 +281,16 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
260
281
 
261
282
  }
262
283
 
263
- // only attenuate on the way in
264
- if ( isBelowSurface ) {
284
+ if ( side == 1.0 && isBelowSurface ) {
265
285
 
286
+ // only attenuate by surface color on the way in
266
287
  color *= mix( vec3( 1.0 ), albedo.rgb, transmissionFactor );
267
288
 
289
+ } else if ( side == - 1.0 ) {
290
+
291
+ // attenuate by medium once we hit the opposite side of the model
292
+ color *= transmissionAttenuation( dist, material.attenuationColor, material.attenuationDistance );
293
+
268
294
  }
269
295
 
270
296
  } else {
@@ -329,7 +355,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
329
355
 
330
356
  #else
331
357
 
332
- // get [-1, 1] normalized device coordinates
358
+ // get [- 1, 1] normalized device coordinates
333
359
  vec2 ndc = 2.0 * jitteredUv - vec2( 1.0 );
334
360
 
335
361
  rayOrigin = ndcToRayOrigin( ndc );
@@ -338,7 +364,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
338
364
 
339
365
  // Orthographic projection
340
366
 
341
- rayDirection = ( cameraWorldMatrix * vec4( 0.0, 0.0, -1.0, 0.0 ) ).xyz;
367
+ rayDirection = ( cameraWorldMatrix * vec4( 0.0, 0.0, - 1.0, 0.0 ) ).xyz;
342
368
  rayDirection = normalize( rayDirection );
343
369
 
344
370
  #else
@@ -512,7 +538,10 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
512
538
 
513
539
  }
514
540
 
541
+ // uv coord for textures
515
542
  vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
543
+ vec4 vertexColor = textureSampleBarycoord( colorAttribute, barycoord, faceIndices.xyz );
544
+
516
545
  // albedo
517
546
  vec4 albedo = vec4( material.color, material.opacity );
518
547
  if ( material.map != - 1 ) {
@@ -521,8 +550,14 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
521
550
  albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
522
551
  }
523
552
 
553
+ if ( material.vertexColors ) {
554
+
555
+ albedo *= vertexColor;
556
+
557
+ }
558
+
524
559
  // alphaMap
525
- if ( material.alphaMap != -1 ) {
560
+ if ( material.alphaMap != - 1 ) {
526
561
 
527
562
  albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
528
563
 
@@ -536,16 +571,15 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
536
571
  // and we're rendering the other then we skip it. Do the opposite on subsequent bounces to get incoming light.
537
572
  float alphaTest = material.alphaTest;
538
573
  bool useAlphaTest = alphaTest != 0.0;
539
- bool isFirstHit = i == 0;
540
574
  if (
541
575
  // material sidedness
542
- material.side != 0.0 && ( side != material.side ) == isFirstHit
576
+ material.side != 0.0 && side != material.side
543
577
 
544
578
  // alpha test
545
579
  || useAlphaTest && albedo.a < alphaTest
546
580
 
547
581
  // opacity
548
- || ! useAlphaTest && albedo.a < rand()
582
+ || material.transparent && ! useAlphaTest && albedo.a < rand()
549
583
  ) {
550
584
 
551
585
  vec3 point = rayOrigin + rayDirection * dist;
@@ -745,27 +779,34 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
745
779
  surfaceRec.emission = emission;
746
780
  surfaceRec.metalness = metalness;
747
781
  surfaceRec.color = albedo.rgb;
748
- surfaceRec.roughness = roughness;
749
782
  surfaceRec.clearcoat = clearcoat;
750
- surfaceRec.clearcoatRoughness = clearcoatRoughness;
751
783
  surfaceRec.sheenColor = sheenColor;
752
- surfaceRec.sheenRoughness = sheenRoughness;
753
784
  surfaceRec.iridescence = iridescence;
754
785
  surfaceRec.iridescenceIor = material.iridescenceIor;
755
786
  surfaceRec.iridescenceThickness = iridescenceThickness;
756
787
  surfaceRec.specularColor = specularColor;
757
788
  surfaceRec.specularIntensity = specularIntensity;
789
+ surfaceRec.attenuationColor = material.attenuationColor;
790
+ surfaceRec.attenuationDistance = material.attenuationDistance;
791
+
792
+ // apply perceptual roughness factor from gltf
793
+ // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#microfacet-surfaces
794
+ surfaceRec.roughness = roughness * roughness;
795
+ surfaceRec.clearcoatRoughness = clearcoatRoughness * clearcoatRoughness;
796
+ surfaceRec.sheenRoughness = sheenRoughness * sheenRoughness;
758
797
 
759
798
  // frontFace is used to determine transmissive properties and PDF. If no transmission is used
760
799
  // then we can just always assume this is a front face.
761
800
  surfaceRec.frontFace = side == 1.0 || transmission == 0.0;
801
+ surfaceRec.iorRatio = material.thinFilm || surfaceRec.frontFace ? 1.0 / material.ior : material.ior;
802
+ surfaceRec.thinFilm = material.thinFilm;
762
803
 
763
804
  // Compute the filtered roughness value to use during specular reflection computations.
764
805
  // The accumulated roughness value is scaled by a user setting and a "magic value" of 5.0.
765
806
  // If we're exiting something transmissive then scale the factor down significantly so we can retain
766
807
  // sharp internal reflections
767
- surfaceRec.filteredRoughness = clamp( max( surfaceRec.roughness, accumulatedRoughness * filterGlossyFactor * 5.0 ), 0.0, 1.0 );
768
- surfaceRec.filteredClearcoatRoughness = clamp( max( surfaceRec.clearcoatRoughness, accumulatedClearcoatRoughness * filterGlossyFactor * 5.0 ), 0.0, 1.0 );
808
+ surfaceRec.filteredRoughness = applyFilteredGlossy( surfaceRec.roughness, accumulatedRoughness );
809
+ surfaceRec.filteredClearcoatRoughness = applyFilteredGlossy( surfaceRec.clearcoatRoughness, accumulatedClearcoatRoughness );
769
810
 
770
811
  mat3 normalBasis = getBasisFromNormal( surfaceRec.normal );
771
812
  mat3 invBasis = inverse( normalBasis );
@@ -898,6 +939,13 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
898
939
 
899
940
  throughputColor *= sampleRec.color / sampleRec.pdf;
900
941
 
942
+ // attenuate the throughput color by the medium color
943
+ if ( side == - 1.0 ) {
944
+
945
+ throughputColor *= transmissionAttenuation( dist, surfaceRec.attenuationColor, surfaceRec.attenuationDistance );
946
+
947
+ }
948
+
901
949
  // discard the sample if there are any NaNs
902
950
  if ( any( isnan( throughputColor ) ) || any( isinf( throughputColor ) ) ) {
903
951
 
@@ -40,7 +40,7 @@ vec3 ggxDirection( vec3 incidentDir, float roughnessX, float roughnessY, float r
40
40
 
41
41
  // Below are PDF and related functions for use in a Monte Carlo path tracer
42
42
  // as specified in Appendix B of the following paper
43
- // See equation (2) from reference [2]
43
+ // See equation (34) from reference [0]
44
44
  float ggxLamda( float theta, float roughness ) {
45
45
 
46
46
  float tanTheta = tan( theta );
@@ -52,7 +52,7 @@ float ggxLamda( float theta, float roughness ) {
52
52
 
53
53
  }
54
54
 
55
- // See equation (2) from reference [2]
55
+ // See equation (34) from reference [0]
56
56
  float ggxShadowMaskG1( float theta, float roughness ) {
57
57
 
58
58
  return 1.0 / ( 1.0 + ggxLamda( theta, roughness ) );
@@ -68,9 +68,9 @@ float ggxShadowMaskG2( vec3 wi, vec3 wo, float roughness ) {
68
68
 
69
69
  }
70
70
 
71
+ // See equation (33) from reference [0]
71
72
  float ggxDistribution( vec3 halfVector, float roughness ) {
72
73
 
73
- // See equation (33) from reference [0]
74
74
  float a2 = roughness * roughness;
75
75
  a2 = max( EPSILON, a2 );
76
76
  float cosTheta = halfVector.z;
@@ -85,14 +85,6 @@ float ggxDistribution( vec3 halfVector, float roughness ) {
85
85
  float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
86
86
  return ( a2 / denom );
87
87
 
88
- // See equation (1) from reference [2]
89
- // const { x, y, z } = halfVector;
90
- // const a2 = roughness * roughness;
91
- // const mult = x * x / a2 + y * y / a2 + z * z;
92
- // const mult2 = mult * mult;
93
-
94
- // return 1.0 / Math.PI * a2 * mult2;
95
-
96
88
  }
97
89
 
98
90
  // See equation (3) from reference [2]
@@ -14,7 +14,9 @@ struct SurfaceRec {
14
14
  vec3 color;
15
15
  vec3 emission;
16
16
  float transmission;
17
+ bool thinFilm;
17
18
  float ior;
19
+ float iorRatio;
18
20
  float clearcoat;
19
21
  float clearcoatRoughness;
20
22
  float filteredClearcoatRoughness;
@@ -25,6 +27,8 @@ struct SurfaceRec {
25
27
  float iridescenceThickness;
26
28
  vec3 specularColor;
27
29
  float specularIntensity;
30
+ vec3 attenuationColor;
31
+ float attenuationDistance;
28
32
  };
29
33
 
30
34
  struct SampleRec {
@@ -63,9 +67,8 @@ vec3 diffuseColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
63
67
  // TODO: scale by 1 - F here
64
68
  // note on division by PI
65
69
  // https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
66
- float metalFactor = ( 1.0 - surf.metalness ) * wi.z / ( PI * PI );
67
- float transmissionFactor = 1.0 - surf.transmission;
68
- return surf.color * metalFactor * transmissionFactor;
70
+ float metalFactor = ( 1.0 - surf.metalness );
71
+ return surf.color * metalFactor * wi.z / PI;
69
72
 
70
73
  }
71
74
 
@@ -105,15 +108,15 @@ vec3 specularColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
105
108
 
106
109
  // if roughness is set to 0 then D === NaN which results in black pixels
107
110
  float metalness = surf.metalness;
108
- float ior = surf.ior;
109
- bool frontFace = surf.frontFace;
110
111
  float filteredRoughness = surf.filteredRoughness;
111
112
 
112
113
  vec3 halfVector = getHalfVector( wo, wi );
113
- float iorRatio = frontFace ? 1.0 / ior : ior;
114
+ float iorRatio = surf.iorRatio;
114
115
  float G = ggxShadowMaskG2( wi, wo, filteredRoughness );
115
116
  float D = ggxDistribution( halfVector, filteredRoughness );
116
- vec3 F = vec3( schlickFresnelFromIor( dot( wi, halfVector ), iorRatio ) ) * surf.specularColor * surf.specularIntensity;
117
+
118
+ float f0 = iorRatioToF0( iorRatio );
119
+ vec3 F = vec3( schlickFresnel( dot( wi, halfVector ), f0 ) );
117
120
 
118
121
  float cosTheta = min( wo.z, 1.0 );
119
122
  float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
@@ -124,11 +127,12 @@ vec3 specularColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
124
127
 
125
128
  }
126
129
 
127
- float f0 = pow( ( iorRatio - 1.0 ) / ( iorRatio + 1.0 ), 2.0 );
128
130
  vec3 iridescenceFresnel = evalIridescence( 1.0, surf.iridescenceIor, dot( wi, halfVector ), surf.iridescenceThickness, vec3( f0 ) );
129
- F = mix( F, iridescenceFresnel, surf.iridescence );
131
+ vec3 metalF = mix( F, iridescenceFresnel, surf.iridescence );
132
+ vec3 dialectricF = F * surf.specularIntensity;
133
+ F = mix( dialectricF, metalF, metalness );
130
134
 
131
- vec3 color = mix( vec3( 1.0 ), surf.color, metalness );
135
+ vec3 color = mix( surf.specularColor, surf.color, metalness );
132
136
  color = mix( color, vec3( 1.0 ), F );
133
137
  color *= G * D / ( 4.0 * abs( wi.z * wo.z ) );
134
138
  color *= mix( F, vec3( 1.0 ), metalness );
@@ -194,14 +198,11 @@ function transmissionColor( wo, wi, material, hit, colorTarget ) {
194
198
  // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
195
199
  float transmissionPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
196
200
 
197
- float ior = surf.ior;
198
- bool frontFace = surf.frontFace;
199
-
200
- float ratio = frontFace ? 1.0 / ior : ior;
201
+ float iorRatio = surf.iorRatio;
201
202
  float cosTheta = min( wo.z, 1.0 );
202
203
  float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
203
- float reflectance = schlickFresnelFromIor( cosTheta, ratio );
204
- bool cannotRefract = ratio * sinTheta > 1.0;
204
+ float reflectance = schlickFresnelFromIor( cosTheta, iorRatio );
205
+ bool cannotRefract = iorRatio * sinTheta > 1.0;
205
206
  if ( cannotRefract ) {
206
207
 
207
208
  return 0.0;
@@ -215,26 +216,25 @@ float transmissionPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
215
216
  vec3 transmissionDirection( vec3 wo, SurfaceRec surf ) {
216
217
 
217
218
  float roughness = surf.roughness;
218
- float ior = surf.ior;
219
- bool frontFace = surf.frontFace;
220
- float ratio = frontFace ? 1.0 / ior : ior;
219
+ float iorRatio = surf.iorRatio;
220
+
221
+ vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + randDirection() * roughness );
222
+ vec3 lightDirection = refract( normalize( - wo ), halfVector, iorRatio );
223
+
224
+ if ( surf.thinFilm ) {
221
225
 
222
- vec3 lightDirection = refract( - wo, vec3( 0.0, 0.0, 1.0 ), ratio );
223
- lightDirection += randDirection() * roughness;
226
+ lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / iorRatio );
227
+
228
+ }
224
229
  return normalize( lightDirection );
225
230
 
226
231
  }
227
232
 
228
233
  vec3 transmissionColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
229
234
 
230
- float metalness = surf.metalness;
231
- float transmission = surf.transmission;
232
-
233
- vec3 color = surf.color;
234
- color *= ( 1.0 - metalness );
235
- color *= transmission;
236
-
237
- return color;
235
+ // only attenuate the color if it's on the way in
236
+ vec3 col = surf.thinFilm || surf.frontFace ? surf.color : vec3( 1.0 );
237
+ return surf.transmission * col;
238
238
 
239
239
  }
240
240
 
@@ -316,16 +316,16 @@ vec3 sheenColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
316
316
  // bsdf
317
317
  void getLobeWeights( vec3 wo, vec3 clearcoatWo, SurfaceRec surf, out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight ) {
318
318
 
319
- float ior = surf.ior;
320
319
  float metalness = surf.metalness;
321
320
  float transmission = surf.transmission;
322
- bool frontFace = surf.frontFace;
323
321
 
324
- float ratio = frontFace ? 1.0 / ior : ior;
322
+ // TODO: we should compute a half vector ahead of time and pass it into the sampling functions
323
+ // so all functions will use the same half vector
324
+ float iorRatio = surf.iorRatio;
325
325
  float cosTheta = min( wo.z, 1.0 );
326
326
  float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
327
- float reflectance = schlickFresnelFromIor( cosTheta, ratio );
328
- bool cannotRefract = ratio * sinTheta > 1.0;
327
+ float reflectance = schlickFresnelFromIor( cosTheta, iorRatio );
328
+ bool cannotRefract = iorRatio * sinTheta > 1.0;
329
329
  if ( cannotRefract ) {
330
330
 
331
331
  reflectance = 1.0;
@@ -341,27 +341,23 @@ void getLobeWeights( vec3 wo, vec3 clearcoatWo, SurfaceRec surf, out float diffu
341
341
  transmissionWeight = transmission * ( 1.0 - transSpecularProb ) * ( 1.0 - clearcoatWeight );
342
342
 
343
343
  float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
344
- float invTotalWeight = 1.0 / totalWeight;
345
-
346
- diffuseWeight *= invTotalWeight;
347
- specularWeight *= invTotalWeight;
348
- transmissionWeight *= invTotalWeight;
349
- clearcoatWeight *= invTotalWeight;
344
+ diffuseWeight /= totalWeight;
345
+ specularWeight /= totalWeight;
346
+ transmissionWeight /= totalWeight;
347
+ clearcoatWeight /= totalWeight;
350
348
 
351
349
  }
352
350
 
353
351
  float bsdfPdf( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out float specularPdf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
354
352
 
355
- float ior = surf.ior;
356
353
  float metalness = surf.metalness;
357
354
  float transmission = surf.transmission;
358
- bool frontFace = surf.frontFace;
359
355
 
360
- float ratio = frontFace ? 1.0 / ior : ior;
356
+ float iorRatio = surf.iorRatio;
361
357
  float cosTheta = min( wo.z, 1.0 );
362
358
  float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
363
- float reflectance = schlickFresnelFromIor( cosTheta, ratio );
364
- bool cannotRefract = ratio * sinTheta > 1.0;
359
+ float reflectance = schlickFresnelFromIor( cosTheta, iorRatio );
360
+ bool cannotRefract = iorRatio * sinTheta > 1.0;
365
361
  if ( cannotRefract ) {
366
362
 
367
363
  reflectance = 1.0;
@@ -512,22 +508,22 @@ SampleRec bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis
512
508
  vec3 clearcoatWi;
513
509
 
514
510
  float r = rand();
515
- if ( r <= cdf[0] ) {
511
+ if ( r <= cdf[0] ) { // diffuse
516
512
 
517
513
  wi = diffuseDirection( wo, surf );
518
514
  clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
519
515
 
520
- } else if ( r <= cdf[1] ) {
516
+ } else if ( r <= cdf[1] ) { // specular
521
517
 
522
518
  wi = specularDirection( wo, surf );
523
519
  clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
524
520
 
525
- } else if ( r <= cdf[2] ) {
521
+ } else if ( r <= cdf[2] ) { // transmission / refraction
526
522
 
527
523
  wi = transmissionDirection( wo, surf );
528
524
  clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
529
525
 
530
- } else if ( r <= cdf[3] ) {
526
+ } else if ( r <= cdf[3] ) { // clearcoat
531
527
 
532
528
  clearcoatWi = clearcoatDirection( clearcoatWo, surf );
533
529
  wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
@@ -62,6 +62,10 @@ export const shaderMaterialStructs = /* glsl */ `
62
62
 
63
63
  float specularIntensity;
64
64
  int specularIntensityMap;
65
+ bool thinFilm;
66
+
67
+ vec3 attenuationColor;
68
+ float attenuationDistance;
65
69
 
66
70
  int alphaMap;
67
71
 
@@ -77,6 +81,9 @@ export const shaderMaterialStructs = /* glsl */ `
77
81
  float sheenRoughness;
78
82
  int sheenRoughnessMap;
79
83
 
84
+ bool vertexColors;
85
+ bool transparent;
86
+
80
87
  mat3 mapTransform;
81
88
  mat3 metalnessMapTransform;
82
89
  mat3 roughnessMapTransform;
@@ -112,7 +119,7 @@ export const shaderMaterialStructs = /* glsl */ `
112
119
 
113
120
  Material readMaterialInfo( sampler2D tex, uint index ) {
114
121
 
115
- uint i = index * 44u;
122
+ uint i = index * 45u;
116
123
 
117
124
  vec4 s0 = texelFetch1D( tex, i + 0u );
118
125
  vec4 s1 = texelFetch1D( tex, i + 1u );
@@ -128,6 +135,7 @@ export const shaderMaterialStructs = /* glsl */ `
128
135
  vec4 s11 = texelFetch1D( tex, i + 11u );
129
136
  vec4 s12 = texelFetch1D( tex, i + 12u );
130
137
  vec4 s13 = texelFetch1D( tex, i + 13u );
138
+ vec4 s14 = texelFetch1D( tex, i + 14u );
131
139
 
132
140
  Material m;
133
141
  m.color = s0.rgb;
@@ -173,17 +181,23 @@ export const shaderMaterialStructs = /* glsl */ `
173
181
 
174
182
  m.specularIntensity = s11.r;
175
183
  m.specularIntensityMap = int( round( s11.g ) );
184
+ m.thinFilm = bool( s11.b );
185
+
186
+ m.attenuationColor = s12.rgb;
187
+ m.attenuationDistance = s12.a;
176
188
 
177
- m.alphaMap = int( round( s12.r ) );
189
+ m.alphaMap = int( round( s13.r ) );
178
190
 
179
- m.opacity = s12.g;
180
- m.alphaTest = s12.b;
181
- m.side = s12.a;
191
+ m.opacity = s13.g;
192
+ m.alphaTest = s13.b;
193
+ m.side = s13.a;
182
194
 
183
- m.matte = bool( s13.r );
184
- m.castShadow = ! bool( s13.g );
195
+ m.matte = bool( s14.r );
196
+ m.castShadow = ! bool( s14.g );
197
+ m.vertexColors = bool( s14.b );
198
+ m.transparent = bool( s14.a );
185
199
 
186
- uint firstTextureTransformIdx = i + 14u;
200
+ uint firstTextureTransformIdx = i + 15u;
187
201
 
188
202
  m.mapTransform = m.map == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx );
189
203
  m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
@@ -1,5 +1,13 @@
1
1
  export const shaderUtils = /* glsl */`
2
2
 
3
+ // https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md#attenuation
4
+ vec3 transmissionAttenuation( float dist, vec3 attColor, float attDist ) {
5
+
6
+ vec3 ot = - log( attColor ) / attDist;
7
+ return exp( - ot * dist );
8
+
9
+ }
10
+
3
11
  // https://google.github.io/filament/Filament.md.html#materialsystem/diffusebrdf
4
12
  float schlickFresnel( float cosine, float f0 ) {
5
13
 
@@ -14,10 +22,16 @@ export const shaderUtils = /* glsl */`
14
22
  }
15
23
 
16
24
  // https://raytracing.github.io/books/RayTracingInOneWeekend.html#dielectrics/schlickapproximation
25
+ float iorRatioToF0( float iorRatio ) {
26
+
27
+ return pow( ( 1.0 - iorRatio ) / ( 1.0 + iorRatio ), 2.0 );
28
+
29
+ }
30
+
17
31
  float schlickFresnelFromIor( float cosine, float iorRatio ) {
18
32
 
19
33
  // Schlick approximation
20
- float r_0 = pow( ( 1.0 - iorRatio ) / ( 1.0 + iorRatio ), 2.0 );
34
+ float r_0 = iorRatioToF0( iorRatio );
21
35
  return schlickFresnel( cosine, r_0 );
22
36
 
23
37
  }
@@ -261,7 +275,7 @@ export const shaderUtils = /* glsl */`
261
275
 
262
276
  }
263
277
 
264
- vec2 square( vec2 t) {
278
+ vec2 square( vec2 t ) {
265
279
 
266
280
  return t * t;
267
281