three-gpu-pathtracer 0.0.17 → 0.0.19
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/build/index.module.js +1013 -322
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +1010 -320
- package/build/index.umd.cjs.map +1 -1
- package/package.json +2 -2
- package/src/core/DynamicPathTracingSceneGenerator.js +80 -40
- package/src/core/PathTracingRenderer.js +28 -34
- package/src/core/PathTracingSceneGenerator.js +11 -60
- package/src/materials/pathtracing/LambertPathTracingMaterial.js +1 -1
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +37 -12
- package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +19 -9
- package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
- package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +4 -4
- package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +2 -1
- package/src/materials/pathtracing/glsl/traceScene.glsl.js +1 -1
- package/src/shader/bsdf/bsdfSampling.glsl.js +14 -10
- package/src/shader/common/fresnel.glsl.js +15 -9
- package/src/shader/rand/pcg.glsl.js +4 -4
- package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
- package/src/shader/sampling/equirectSampling.glsl.js +8 -1
- package/src/shader/structs/lightsStruct.glsl.js +5 -7
- package/src/textures/BlueNoiseTexture.js +87 -0
- package/src/textures/ProceduralEquirectTexture.js +7 -8
- package/src/textures/blueNoise/BlueNoiseGenerator.js +115 -0
- package/src/textures/blueNoise/BlueNoiseSamples.js +214 -0
- package/src/textures/blueNoise/utils.js +24 -0
- package/src/uniforms/EquirectHdrInfoUniform.js +45 -8
- package/src/uniforms/LightsInfoUniformStruct.js +11 -7
- package/src/uniforms/MaterialsTexture.js +1 -1
- package/src/uniforms/RenderTarget2DArray.js +50 -3
- package/src/uniforms/StratifiedSamplesTexture.js +49 -0
- package/src/uniforms/stratified/StratifiedSampler.js +73 -0
- package/src/uniforms/stratified/StratifiedSamplerCombined.js +59 -0
- package/src/uniforms/utils.js +1 -1
- package/src/utils/GeometryPreparationUtils.js +8 -101
- package/src/workers/PathTracingSceneWorker.js +18 -8
package/build/index.module.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ShaderMaterial, NoBlending, Vector2, WebGLRenderTarget, FloatType, RGBAFormat, NearestFilter, Vector4, NormalBlending, Color, HalfFloatType, Vector3, MathUtils, Matrix4, PerspectiveCamera, BufferAttribute,
|
|
1
|
+
import { ShaderMaterial, NoBlending, Vector2, WebGLRenderTarget, FloatType, RGBAFormat, NearestFilter, Vector4, NormalBlending, Color, HalfFloatType, Vector3, MathUtils, Matrix4, PerspectiveCamera, BufferAttribute, MeshBasicMaterial, BufferGeometry, Mesh, Camera, SpotLight, RectAreaLight, Spherical, DataTexture, EquirectangularReflectionMapping, RepeatWrapping, ClampToEdgeWrapping, LinearFilter, DoubleSide, BackSide, FrontSide, WebGLArrayRenderTarget, UnsignedByteType, NoToneMapping, DataUtils, Source, RedFormat, Quaternion, Loader, FileLoader, PMREMGenerator, DataArrayTexture, RGFormat, MeshStandardMaterial, BoxGeometry } from 'three';
|
|
2
2
|
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
3
|
-
import { StaticGeometryGenerator,
|
|
4
|
-
import { mergeVertices
|
|
3
|
+
import { StaticGeometryGenerator, MeshBVH, SAH, FloatVertexAttributeTexture, MeshBVHUniformStruct, UIntVertexAttributeTexture, BVHShaderGLSL } from 'three-mesh-bvh';
|
|
4
|
+
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
5
5
|
|
|
6
6
|
class MaterialBase extends ShaderMaterial {
|
|
7
7
|
|
|
@@ -456,9 +456,6 @@ class SobolNumberMapGenerator {
|
|
|
456
456
|
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
-
const _scissor = new Vector4();
|
|
460
|
-
const _viewport = new Vector4();
|
|
461
|
-
|
|
462
459
|
function* renderTask() {
|
|
463
460
|
|
|
464
461
|
const {
|
|
@@ -500,12 +497,21 @@ function* renderTask() {
|
|
|
500
497
|
const h = _primaryTarget.height;
|
|
501
498
|
material.resolution.set( w * subW, h * subH );
|
|
502
499
|
material.sobolTexture = _sobolTarget.texture;
|
|
500
|
+
material.stratifiedTexture.init( 20, material.bounces + material.transmissiveBounces + 5 );
|
|
501
|
+
material.stratifiedTexture.next();
|
|
503
502
|
material.seed ++;
|
|
504
503
|
|
|
505
504
|
const tilesX = this.tiles.x || 1;
|
|
506
505
|
const tilesY = this.tiles.y || 1;
|
|
507
506
|
const totalTiles = tilesX * tilesY;
|
|
508
|
-
|
|
507
|
+
|
|
508
|
+
const pxSubW = Math.ceil( w * subW );
|
|
509
|
+
const pxSubH = Math.ceil( h * subH );
|
|
510
|
+
const pxSubX = Math.floor( subX * w );
|
|
511
|
+
const pxSubY = Math.floor( subY * h );
|
|
512
|
+
|
|
513
|
+
const pxTileW = Math.ceil( pxSubW / tilesX );
|
|
514
|
+
const pxTileH = Math.ceil( pxSubH / tilesY );
|
|
509
515
|
|
|
510
516
|
for ( let y = 0; y < tilesY; y ++ ) {
|
|
511
517
|
|
|
@@ -554,40 +560,28 @@ function* renderTask() {
|
|
|
554
560
|
|
|
555
561
|
}
|
|
556
562
|
|
|
563
|
+
// set the scissor and the viewport on the render target
|
|
564
|
+
// note that when using the webgl renderer set viewport the device pixel ratio
|
|
565
|
+
// is multiplied into the field causing some pixels to not be rendered
|
|
566
|
+
const reverseTy = tilesY - ty - 1;
|
|
567
|
+
_primaryTarget.scissor.set(
|
|
568
|
+
pxSubX + tx * pxTileW,
|
|
569
|
+
pxSubY + reverseTy * pxTileH,
|
|
570
|
+
Math.min( pxTileW, pxSubW - tx * pxTileW ),
|
|
571
|
+
Math.min( pxTileH, pxSubH - reverseTy * pxTileH ),
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
_primaryTarget.viewport.set(
|
|
575
|
+
pxSubX,
|
|
576
|
+
pxSubY,
|
|
577
|
+
pxSubW,
|
|
578
|
+
pxSubH,
|
|
579
|
+
);
|
|
580
|
+
|
|
557
581
|
// three.js renderer takes values relative to the current pixel ratio
|
|
558
582
|
_renderer.setRenderTarget( _primaryTarget );
|
|
559
583
|
_renderer.setScissorTest( true );
|
|
560
584
|
|
|
561
|
-
// set the scissor window for a subtile
|
|
562
|
-
_scissor.x = tx * w / tilesX;
|
|
563
|
-
_scissor.y = ( tilesY - ty - 1 ) * h / tilesY;
|
|
564
|
-
_scissor.z = w / tilesX;
|
|
565
|
-
_scissor.w = h / tilesY;
|
|
566
|
-
|
|
567
|
-
// adjust for the subframe
|
|
568
|
-
_scissor.x = subX * w + subW * _scissor.x;
|
|
569
|
-
_scissor.y = subY * h + subH * _scissor.y;
|
|
570
|
-
_scissor.z = subW * _scissor.z;
|
|
571
|
-
_scissor.w = subH * _scissor.w;
|
|
572
|
-
|
|
573
|
-
// round for floating point cases
|
|
574
|
-
_scissor.x = _scissor.x;
|
|
575
|
-
_scissor.y = _scissor.y;
|
|
576
|
-
_scissor.z = _scissor.z;
|
|
577
|
-
_scissor.w = _scissor.w;
|
|
578
|
-
|
|
579
|
-
// multiply inverse of DPR in because threes multiplies it in
|
|
580
|
-
_scissor.multiplyScalar( dprInv ).ceil();
|
|
581
|
-
|
|
582
|
-
_viewport.x = subX * w;
|
|
583
|
-
_viewport.y = subY * h;
|
|
584
|
-
_viewport.z = subW * w;
|
|
585
|
-
_viewport.w = subH * h;
|
|
586
|
-
_viewport.multiplyScalar( dprInv ).ceil();
|
|
587
|
-
|
|
588
|
-
_renderer.setScissor( _scissor );
|
|
589
|
-
_renderer.setViewport( _viewport );
|
|
590
|
-
|
|
591
585
|
_renderer.autoClear = false;
|
|
592
586
|
_fsQuad.render( _renderer );
|
|
593
587
|
|
|
@@ -1077,25 +1071,6 @@ function getGroupMaterialIndicesAttribute( geometry, materials, allMaterials ) {
|
|
|
1077
1071
|
|
|
1078
1072
|
}
|
|
1079
1073
|
|
|
1080
|
-
function trimToAttributes( geometry, attributes ) {
|
|
1081
|
-
|
|
1082
|
-
// trim any unneeded attributes
|
|
1083
|
-
if ( attributes ) {
|
|
1084
|
-
|
|
1085
|
-
for ( const key in geometry.attributes ) {
|
|
1086
|
-
|
|
1087
|
-
if ( ! attributes.includes( key ) ) {
|
|
1088
|
-
|
|
1089
|
-
geometry.deleteAttribute( key );
|
|
1090
|
-
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
1074
|
function setCommonAttributes( geometry, options ) {
|
|
1100
1075
|
|
|
1101
1076
|
const { attributes = [], normalMapRequired = false } = options;
|
|
@@ -1113,6 +1088,13 @@ function setCommonAttributes( geometry, options ) {
|
|
|
1113
1088
|
|
|
1114
1089
|
}
|
|
1115
1090
|
|
|
1091
|
+
if ( ! geometry.attributes.uv2 && ( attributes && attributes.includes( 'uv2' ) ) ) {
|
|
1092
|
+
|
|
1093
|
+
const vertCount = geometry.attributes.position.count;
|
|
1094
|
+
geometry.setAttribute( 'uv2', new BufferAttribute( new Float32Array( vertCount * 2 ), 2, false ) );
|
|
1095
|
+
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1116
1098
|
if ( ! geometry.attributes.tangent && ( attributes && attributes.includes( 'tangent' ) ) ) {
|
|
1117
1099
|
|
|
1118
1100
|
if ( normalMapRequired ) {
|
|
@@ -1161,169 +1143,46 @@ function setCommonAttributes( geometry, options ) {
|
|
|
1161
1143
|
|
|
1162
1144
|
}
|
|
1163
1145
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
options = { attributes: null, cloneGeometry: true, ...options };
|
|
1146
|
+
const dummyMaterial = new MeshBasicMaterial();
|
|
1147
|
+
function getDummyMesh() {
|
|
1167
1148
|
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1149
|
+
const emptyGeometry = new BufferGeometry();
|
|
1150
|
+
emptyGeometry.setAttribute( 'position', new BufferAttribute( new Float32Array( 9 ), 3 ) );
|
|
1151
|
+
return new Mesh( emptyGeometry, dummyMaterial );
|
|
1171
1152
|
|
|
1172
|
-
|
|
1173
|
-
const mesh = meshes[ i ];
|
|
1174
|
-
if ( mesh.visible === false ) continue;
|
|
1175
|
-
|
|
1176
|
-
if ( Array.isArray( mesh.material ) ) {
|
|
1177
|
-
|
|
1178
|
-
mesh.material.forEach( m => materialSet.add( m ) );
|
|
1179
|
-
|
|
1180
|
-
} else {
|
|
1181
|
-
|
|
1182
|
-
materialSet.add( mesh.material );
|
|
1183
|
-
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
const materials = Array.from( materialSet );
|
|
1189
|
-
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
|
1190
|
-
|
|
1191
|
-
// ensure the matrix world is up to date
|
|
1192
|
-
const mesh = meshes[ i ];
|
|
1193
|
-
if ( mesh.visible === false ) continue;
|
|
1194
|
-
|
|
1195
|
-
mesh.updateMatrixWorld();
|
|
1196
|
-
|
|
1197
|
-
// apply the matrix world to the geometry
|
|
1198
|
-
const originalGeometry = meshes[ i ].geometry;
|
|
1199
|
-
const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
|
|
1200
|
-
geometry.applyMatrix4( mesh.matrixWorld );
|
|
1201
|
-
|
|
1202
|
-
if ( mesh.matrixWorld.determinant() < 0 ) {
|
|
1203
|
-
|
|
1204
|
-
geometry.index.array.reverse();
|
|
1205
|
-
|
|
1206
|
-
}
|
|
1153
|
+
}
|
|
1207
1154
|
|
|
1208
|
-
|
|
1209
|
-
setCommonAttributes( geometry, {
|
|
1210
|
-
attributes: options.attributes,
|
|
1211
|
-
normalMapRequired: ! ! mesh.material.normalMap,
|
|
1212
|
-
} );
|
|
1213
|
-
trimToAttributes( geometry, options.attributes );
|
|
1155
|
+
class DynamicPathTracingSceneGenerator {
|
|
1214
1156
|
|
|
1215
|
-
|
|
1216
|
-
const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, mesh.material, materials );
|
|
1217
|
-
geometry.setAttribute( 'materialIndex', materialIndexAttribute );
|
|
1157
|
+
get initialized() {
|
|
1218
1158
|
|
|
1219
|
-
|
|
1159
|
+
return Boolean( this.bvh );
|
|
1220
1160
|
|
|
1221
1161
|
}
|
|
1222
1162
|
|
|
1223
|
-
|
|
1224
|
-
materials.forEach( material => {
|
|
1225
|
-
|
|
1226
|
-
for ( const key in material ) {
|
|
1163
|
+
constructor( objects ) {
|
|
1227
1164
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1165
|
+
// ensure the objects is an array
|
|
1166
|
+
if ( ! Array.isArray( objects ) ) {
|
|
1230
1167
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1168
|
+
objects = [ objects ];
|
|
1234
1169
|
|
|
1235
1170
|
}
|
|
1236
1171
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
const textures = Array.from( textureSet );
|
|
1241
|
-
return { geometry, materials, textures };
|
|
1242
|
-
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
class PathTracingSceneGenerator {
|
|
1246
|
-
|
|
1247
|
-
prepScene( scene ) {
|
|
1172
|
+
// use a dummy object for a fallback
|
|
1173
|
+
const finalObjects = [ ...objects ];
|
|
1174
|
+
if ( finalObjects.length === 0 ) {
|
|
1248
1175
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
const meshes = [];
|
|
1252
|
-
const lights = [];
|
|
1253
|
-
|
|
1254
|
-
for ( let i = 0, l = scene.length; i < l; i ++ ) {
|
|
1255
|
-
|
|
1256
|
-
scene[ i ].traverseVisible( c => {
|
|
1257
|
-
|
|
1258
|
-
if ( c.isSkinnedMesh || c.isMesh && c.morphTargetInfluences ) {
|
|
1259
|
-
|
|
1260
|
-
const generator = new StaticGeometryGenerator( c );
|
|
1261
|
-
generator.attributes = [ 'position', 'color', 'normal', 'tangent', 'uv', 'uv2' ];
|
|
1262
|
-
generator.applyWorldTransforms = false;
|
|
1263
|
-
const mesh = new Mesh(
|
|
1264
|
-
generator.generate(),
|
|
1265
|
-
c.material,
|
|
1266
|
-
);
|
|
1267
|
-
mesh.matrixWorld.copy( c.matrixWorld );
|
|
1268
|
-
mesh.matrix.copy( c.matrixWorld );
|
|
1269
|
-
mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
|
|
1270
|
-
meshes.push( mesh );
|
|
1271
|
-
|
|
1272
|
-
} else if ( c.isMesh ) {
|
|
1273
|
-
|
|
1274
|
-
meshes.push( c );
|
|
1275
|
-
|
|
1276
|
-
} else if (
|
|
1277
|
-
c.isRectAreaLight ||
|
|
1278
|
-
c.isSpotLight ||
|
|
1279
|
-
c.isPointLight ||
|
|
1280
|
-
c.isDirectionalLight
|
|
1281
|
-
) {
|
|
1282
|
-
|
|
1283
|
-
lights.push( c );
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
} );
|
|
1176
|
+
finalObjects.push( getDummyMesh() );
|
|
1288
1177
|
|
|
1289
1178
|
}
|
|
1290
1179
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
} ),
|
|
1295
|
-
lights,
|
|
1296
|
-
};
|
|
1297
|
-
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
generate( scene, options = {} ) {
|
|
1301
|
-
|
|
1302
|
-
const { materials, textures, geometry, lights } = this.prepScene( scene );
|
|
1303
|
-
const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
|
|
1304
|
-
return {
|
|
1305
|
-
scene,
|
|
1306
|
-
materials,
|
|
1307
|
-
textures,
|
|
1308
|
-
lights,
|
|
1309
|
-
bvh: new MeshBVH( geometry, bvhOptions ),
|
|
1310
|
-
};
|
|
1311
|
-
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
class DynamicPathTracingSceneGenerator {
|
|
1317
|
-
|
|
1318
|
-
get initialized() {
|
|
1319
|
-
|
|
1320
|
-
return Boolean( this.bvh );
|
|
1321
|
-
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
constructor( scene ) {
|
|
1180
|
+
// options
|
|
1181
|
+
this.bvhOptions = {};
|
|
1182
|
+
this.attributes = [ 'position', 'normal', 'tangent', 'color', 'uv', 'uv2' ];
|
|
1325
1183
|
|
|
1326
|
-
|
|
1184
|
+
// state
|
|
1185
|
+
this.objects = finalObjects;
|
|
1327
1186
|
this.bvh = null;
|
|
1328
1187
|
this.geometry = new BufferGeometry();
|
|
1329
1188
|
this.materials = null;
|
|
@@ -1347,64 +1206,75 @@ class DynamicPathTracingSceneGenerator {
|
|
|
1347
1206
|
|
|
1348
1207
|
dispose() {}
|
|
1349
1208
|
|
|
1350
|
-
|
|
1209
|
+
prepScene() {
|
|
1351
1210
|
|
|
1352
|
-
|
|
1353
|
-
if ( this.bvh === null ) {
|
|
1211
|
+
if ( this.bvh !== null ) {
|
|
1354
1212
|
|
|
1355
|
-
|
|
1213
|
+
return;
|
|
1356
1214
|
|
|
1357
|
-
|
|
1215
|
+
}
|
|
1358
1216
|
|
|
1359
|
-
|
|
1217
|
+
const { objects, staticGeometryGenerator, geometry, lights, attributes } = this;
|
|
1218
|
+
for ( let i = 0, l = objects.length; i < l; i ++ ) {
|
|
1360
1219
|
|
|
1361
|
-
|
|
1220
|
+
objects[ i ].traverse( c => {
|
|
1362
1221
|
|
|
1363
|
-
|
|
1364
|
-
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
1222
|
+
if ( c.isMesh ) {
|
|
1365
1223
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
c.isSpotLight ||
|
|
1369
|
-
c.isPointLight ||
|
|
1370
|
-
c.isDirectionalLight
|
|
1371
|
-
) {
|
|
1224
|
+
const normalMapRequired = ! ! c.material.normalMap;
|
|
1225
|
+
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
1372
1226
|
|
|
1373
|
-
|
|
1227
|
+
} else if (
|
|
1228
|
+
c.isRectAreaLight ||
|
|
1229
|
+
c.isSpotLight ||
|
|
1230
|
+
c.isPointLight ||
|
|
1231
|
+
c.isDirectionalLight
|
|
1232
|
+
) {
|
|
1374
1233
|
|
|
1375
|
-
|
|
1234
|
+
lights.push( c );
|
|
1376
1235
|
|
|
1377
|
-
}
|
|
1236
|
+
}
|
|
1378
1237
|
|
|
1379
|
-
}
|
|
1238
|
+
} );
|
|
1380
1239
|
|
|
1381
|
-
|
|
1382
|
-
const materials = staticGeometryGenerator.getMaterials();
|
|
1383
|
-
materials.forEach( material => {
|
|
1240
|
+
}
|
|
1384
1241
|
|
|
1385
|
-
|
|
1242
|
+
const textureSet = new Set();
|
|
1243
|
+
const materials = staticGeometryGenerator.getMaterials();
|
|
1244
|
+
materials.forEach( material => {
|
|
1386
1245
|
|
|
1387
|
-
|
|
1388
|
-
if ( value && value.isTexture ) {
|
|
1246
|
+
for ( const key in material ) {
|
|
1389
1247
|
|
|
1390
|
-
|
|
1248
|
+
const value = material[ key ];
|
|
1249
|
+
if ( value && value.isTexture ) {
|
|
1391
1250
|
|
|
1392
|
-
|
|
1251
|
+
textureSet.add( value );
|
|
1393
1252
|
|
|
1394
1253
|
}
|
|
1395
1254
|
|
|
1396
|
-
}
|
|
1255
|
+
}
|
|
1397
1256
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1257
|
+
} );
|
|
1258
|
+
|
|
1259
|
+
staticGeometryGenerator.attributes = attributes;
|
|
1260
|
+
staticGeometryGenerator.generate( geometry );
|
|
1261
|
+
|
|
1262
|
+
const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, materials, materials );
|
|
1263
|
+
geometry.setAttribute( 'materialIndex', materialIndexAttribute );
|
|
1264
|
+
geometry.clearGroups();
|
|
1265
|
+
|
|
1266
|
+
this.materials = materials;
|
|
1267
|
+
this.textures = Array.from( textureSet );
|
|
1268
|
+
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
generate() {
|
|
1400
1272
|
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
geometry.clearGroups();
|
|
1273
|
+
const { objects, staticGeometryGenerator, geometry, bvhOptions } = this;
|
|
1274
|
+
if ( this.bvh === null ) {
|
|
1404
1275
|
|
|
1405
|
-
this.
|
|
1406
|
-
this.
|
|
1407
|
-
this.textures = Array.from( textureSet );
|
|
1276
|
+
this.prepScene();
|
|
1277
|
+
this.bvh = new MeshBVH( geometry, { strategy: SAH, maxLeafTris: 1, ...bvhOptions } );
|
|
1408
1278
|
|
|
1409
1279
|
return {
|
|
1410
1280
|
lights: this.lights,
|
|
@@ -1434,6 +1304,30 @@ class DynamicPathTracingSceneGenerator {
|
|
|
1434
1304
|
|
|
1435
1305
|
}
|
|
1436
1306
|
|
|
1307
|
+
class PathTracingSceneGenerator {
|
|
1308
|
+
|
|
1309
|
+
generate( scene, options = {} ) {
|
|
1310
|
+
|
|
1311
|
+
// ensure scene transforms are up to date
|
|
1312
|
+
// TODO: remove this?
|
|
1313
|
+
if ( Array.isArray( scene ) ) {
|
|
1314
|
+
|
|
1315
|
+
scene.forEach( s => s.updateMatrixWorld( true ) );
|
|
1316
|
+
|
|
1317
|
+
} else {
|
|
1318
|
+
|
|
1319
|
+
scene.updateMatrixWorld( true );
|
|
1320
|
+
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
const generator = new DynamicPathTracingSceneGenerator( scene );
|
|
1324
|
+
generator.bvhOptions = options;
|
|
1325
|
+
return generator.generate();
|
|
1326
|
+
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1437
1331
|
// https://github.com/gkjohnson/webxr-sandbox/blob/main/skinned-mesh-batching/src/MaterialReducer.js
|
|
1438
1332
|
|
|
1439
1333
|
function isTypedArray( arr ) {
|
|
@@ -1798,8 +1692,8 @@ class ProceduralEquirectTexture extends DataTexture {
|
|
|
1798
1692
|
constructor( width = 512, height = 512 ) {
|
|
1799
1693
|
|
|
1800
1694
|
super(
|
|
1801
|
-
new
|
|
1802
|
-
width, height, RGBAFormat,
|
|
1695
|
+
new Float32Array( width * height * 4 ),
|
|
1696
|
+
width, height, RGBAFormat, FloatType, EquirectangularReflectionMapping,
|
|
1803
1697
|
RepeatWrapping, ClampToEdgeWrapping, LinearFilter, LinearFilter,
|
|
1804
1698
|
);
|
|
1805
1699
|
|
|
@@ -1831,10 +1725,10 @@ class ProceduralEquirectTexture extends DataTexture {
|
|
|
1831
1725
|
|
|
1832
1726
|
const i = y * width + x;
|
|
1833
1727
|
const i4 = 4 * i;
|
|
1834
|
-
data[ i4 + 0 ] =
|
|
1835
|
-
data[ i4 + 1 ] =
|
|
1836
|
-
data[ i4 + 2 ] =
|
|
1837
|
-
data[ i4 + 3 ] =
|
|
1728
|
+
data[ i4 + 0 ] = ( _color.r );
|
|
1729
|
+
data[ i4 + 1 ] = ( _color.g );
|
|
1730
|
+
data[ i4 + 2 ] = ( _color.b );
|
|
1731
|
+
data[ i4 + 3 ] = ( 1.0 );
|
|
1838
1732
|
|
|
1839
1733
|
}
|
|
1840
1734
|
|
|
@@ -1889,7 +1783,7 @@ class GradientEquirectTexture extends ProceduralEquirectTexture {
|
|
|
1889
1783
|
// when rendering each texture to the texture array they must have a consistent color space.
|
|
1890
1784
|
function getTextureHash( t ) {
|
|
1891
1785
|
|
|
1892
|
-
return `${ t.source.uuid }:${ t.
|
|
1786
|
+
return `${ t.source.uuid }:${ t.colorSpace }`;
|
|
1893
1787
|
|
|
1894
1788
|
}
|
|
1895
1789
|
|
|
@@ -2101,7 +1995,7 @@ class MaterialsTexture extends DataTexture {
|
|
|
2101
1995
|
|
|
2102
1996
|
let index = 0;
|
|
2103
1997
|
const pixelCount = materials.length * MATERIAL_PIXELS;
|
|
2104
|
-
const dimension = Math.ceil( Math.sqrt( pixelCount ) );
|
|
1998
|
+
const dimension = Math.ceil( Math.sqrt( pixelCount ) ) || 1;
|
|
2105
1999
|
const { threeCompatibilityTransforms, image, features } = this;
|
|
2106
2000
|
|
|
2107
2001
|
// get the list of textures with unique sources
|
|
@@ -2439,7 +2333,7 @@ class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
2439
2333
|
|
|
2440
2334
|
};
|
|
2441
2335
|
|
|
2442
|
-
const fsQuad = new FullScreenQuad( new
|
|
2336
|
+
const fsQuad = new FullScreenQuad( new CopyMaterial() );
|
|
2443
2337
|
this.fsQuad = fsQuad;
|
|
2444
2338
|
|
|
2445
2339
|
}
|
|
@@ -2474,7 +2368,6 @@ class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
2474
2368
|
texture.matrix.identity();
|
|
2475
2369
|
|
|
2476
2370
|
fsQuad.material.map = texture;
|
|
2477
|
-
fsQuad.material.transparent = true;
|
|
2478
2371
|
|
|
2479
2372
|
renderer.setRenderTarget( this, i );
|
|
2480
2373
|
fsQuad.render( renderer );
|
|
@@ -2504,6 +2397,54 @@ class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
2504
2397
|
|
|
2505
2398
|
}
|
|
2506
2399
|
|
|
2400
|
+
class CopyMaterial extends ShaderMaterial {
|
|
2401
|
+
|
|
2402
|
+
get map() {
|
|
2403
|
+
|
|
2404
|
+
return this.uniforms.map.value;
|
|
2405
|
+
|
|
2406
|
+
}
|
|
2407
|
+
set map( v ) {
|
|
2408
|
+
|
|
2409
|
+
this.uniforms.map.value = v;
|
|
2410
|
+
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
constructor() {
|
|
2414
|
+
|
|
2415
|
+
super( {
|
|
2416
|
+
uniforms: {
|
|
2417
|
+
|
|
2418
|
+
map: { value: null },
|
|
2419
|
+
|
|
2420
|
+
},
|
|
2421
|
+
|
|
2422
|
+
vertexShader: /* glsl */`
|
|
2423
|
+
varying vec2 vUv;
|
|
2424
|
+
void main() {
|
|
2425
|
+
|
|
2426
|
+
vUv = uv;
|
|
2427
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
2428
|
+
|
|
2429
|
+
}
|
|
2430
|
+
`,
|
|
2431
|
+
|
|
2432
|
+
fragmentShader: /* glsl */`
|
|
2433
|
+
uniform sampler2D map;
|
|
2434
|
+
varying vec2 vUv;
|
|
2435
|
+
void main() {
|
|
2436
|
+
|
|
2437
|
+
gl_FragColor = texture2D( map, vUv );
|
|
2438
|
+
|
|
2439
|
+
}
|
|
2440
|
+
`
|
|
2441
|
+
} );
|
|
2442
|
+
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2507
2448
|
function toHalfFloatArray( f32Array ) {
|
|
2508
2449
|
|
|
2509
2450
|
const f16Array = new Uint16Array( f32Array.length );
|
|
@@ -2555,7 +2496,7 @@ function colorToLuminance( r, g, b ) {
|
|
|
2555
2496
|
}
|
|
2556
2497
|
|
|
2557
2498
|
// ensures the data is all floating point values and flipY is false
|
|
2558
|
-
function preprocessEnvMap( envMap ) {
|
|
2499
|
+
function preprocessEnvMap( envMap, targetType = HalfFloatType ) {
|
|
2559
2500
|
|
|
2560
2501
|
const map = envMap.clone();
|
|
2561
2502
|
map.source = new Source( { ...map.image } );
|
|
@@ -2564,17 +2505,54 @@ function preprocessEnvMap( envMap ) {
|
|
|
2564
2505
|
// TODO: is there a simple way to avoid cloning and adjusting the env map data here?
|
|
2565
2506
|
// convert the data from half float uint 16 arrays to float arrays for cdf computation
|
|
2566
2507
|
let newData = data;
|
|
2567
|
-
if ( map.type
|
|
2508
|
+
if ( map.type !== targetType ) {
|
|
2509
|
+
|
|
2510
|
+
if ( targetType === HalfFloatType ) {
|
|
2511
|
+
|
|
2512
|
+
newData = new Uint16Array( data.length );
|
|
2513
|
+
|
|
2514
|
+
} else {
|
|
2515
|
+
|
|
2516
|
+
newData = new Float32Array( data.length );
|
|
2517
|
+
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
let maxIntValue;
|
|
2521
|
+
if ( data instanceof Int8Array || data instanceof Int16Array || data instanceof Int32Array ) {
|
|
2522
|
+
|
|
2523
|
+
maxIntValue = 2 ** ( 8 * data.BYTES_PER_ELEMENT - 1 ) - 1;
|
|
2568
2524
|
|
|
2569
|
-
|
|
2570
|
-
|
|
2525
|
+
} else {
|
|
2526
|
+
|
|
2527
|
+
maxIntValue = 2 ** ( 8 * data.BYTES_PER_ELEMENT ) - 1;
|
|
2528
|
+
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
for ( let i = 0, l = data.length; i < l; i ++ ) {
|
|
2532
|
+
|
|
2533
|
+
let v = data[ i ];
|
|
2534
|
+
if ( map.type === HalfFloatType ) {
|
|
2535
|
+
|
|
2536
|
+
v = DataUtils.fromHalfFloat( data[ i ] );
|
|
2537
|
+
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
if ( map.type !== FloatType && map.type !== HalfFloatType ) {
|
|
2541
|
+
|
|
2542
|
+
v /= maxIntValue;
|
|
2543
|
+
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
if ( targetType === HalfFloatType ) {
|
|
2571
2547
|
|
|
2572
|
-
|
|
2548
|
+
newData[ i ] = DataUtils.toHalfFloat( v );
|
|
2549
|
+
|
|
2550
|
+
}
|
|
2573
2551
|
|
|
2574
2552
|
}
|
|
2575
2553
|
|
|
2576
2554
|
map.image.data = newData;
|
|
2577
|
-
map.type =
|
|
2555
|
+
map.type = targetType;
|
|
2578
2556
|
|
|
2579
2557
|
}
|
|
2580
2558
|
|
|
@@ -2666,7 +2644,7 @@ class EquirectHdrInfoUniform {
|
|
|
2666
2644
|
// https://pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Sampling_Light_Sources#InfiniteAreaLights
|
|
2667
2645
|
const map = preprocessEnvMap( hdr );
|
|
2668
2646
|
map.wrapS = RepeatWrapping;
|
|
2669
|
-
map.wrapT =
|
|
2647
|
+
map.wrapT = ClampToEdgeWrapping;
|
|
2670
2648
|
|
|
2671
2649
|
const { width, height, data } = map.image;
|
|
2672
2650
|
|
|
@@ -2878,6 +2856,13 @@ class LightsInfoUniformStruct {
|
|
|
2878
2856
|
const baseIndex = i * LIGHT_PIXELS * 4;
|
|
2879
2857
|
let index = 0;
|
|
2880
2858
|
|
|
2859
|
+
// initialize to 0
|
|
2860
|
+
for ( let p = 0; p < LIGHT_PIXELS * 4; p ++ ) {
|
|
2861
|
+
|
|
2862
|
+
floatArray[ baseIndex + p ] = 0;
|
|
2863
|
+
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2881
2866
|
// sample 1
|
|
2882
2867
|
// position
|
|
2883
2868
|
l.getWorldPosition( v );
|
|
@@ -2942,7 +2927,7 @@ class LightsInfoUniformStruct {
|
|
|
2942
2927
|
|
|
2943
2928
|
} else if ( l.isSpotLight ) {
|
|
2944
2929
|
|
|
2945
|
-
const radius = l.radius;
|
|
2930
|
+
const radius = l.radius || 0;
|
|
2946
2931
|
eye.setFromMatrixPosition( l.matrixWorld );
|
|
2947
2932
|
target.setFromMatrixPosition( l.target.matrixWorld );
|
|
2948
2933
|
m.lookAt( eye, target, up );
|
|
@@ -2972,24 +2957,21 @@ class LightsInfoUniformStruct {
|
|
|
2972
2957
|
// radius
|
|
2973
2958
|
floatArray[ baseIndex + ( index ++ ) ] = radius;
|
|
2974
2959
|
|
|
2975
|
-
// near
|
|
2976
|
-
floatArray[ baseIndex + ( index ++ ) ] = l.shadow.camera.near;
|
|
2977
|
-
|
|
2978
2960
|
// decay
|
|
2979
2961
|
floatArray[ baseIndex + ( index ++ ) ] = l.decay;
|
|
2980
2962
|
|
|
2981
2963
|
// distance
|
|
2982
2964
|
floatArray[ baseIndex + ( index ++ ) ] = l.distance;
|
|
2983
2965
|
|
|
2984
|
-
// sample 6
|
|
2985
2966
|
// coneCos
|
|
2986
2967
|
floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle );
|
|
2987
2968
|
|
|
2969
|
+
// sample 6
|
|
2988
2970
|
// penumbraCos
|
|
2989
2971
|
floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle * ( 1 - l.penumbra ) );
|
|
2990
2972
|
|
|
2991
2973
|
// iesProfile
|
|
2992
|
-
floatArray[ baseIndex + ( index ++ ) ] = iesTextures.indexOf( l.iesTexture );
|
|
2974
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.iesTexture ? iesTextures.indexOf( l.iesTexture ) : - 1;
|
|
2993
2975
|
|
|
2994
2976
|
} else if ( l.isPointLight ) {
|
|
2995
2977
|
|
|
@@ -3006,7 +2988,7 @@ class LightsInfoUniformStruct {
|
|
|
3006
2988
|
index += 4;
|
|
3007
2989
|
|
|
3008
2990
|
// sample 5
|
|
3009
|
-
index +=
|
|
2991
|
+
index += 1;
|
|
3010
2992
|
|
|
3011
2993
|
floatArray[ baseIndex + ( index ++ ) ] = l.decay;
|
|
3012
2994
|
floatArray[ baseIndex + ( index ++ ) ] = l.distance;
|
|
@@ -4420,18 +4402,16 @@ const lightsStructGLSL = /* glsl */`
|
|
|
4420
4402
|
vec4 s4 = texelFetch1D( tex, i + 4u );
|
|
4421
4403
|
vec4 s5 = texelFetch1D( tex, i + 5u );
|
|
4422
4404
|
l.radius = s4.r;
|
|
4423
|
-
l.
|
|
4424
|
-
l.
|
|
4425
|
-
l.
|
|
4405
|
+
l.decay = s4.g;
|
|
4406
|
+
l.distance = s4.b;
|
|
4407
|
+
l.coneCos = s4.a;
|
|
4426
4408
|
|
|
4427
|
-
l.
|
|
4428
|
-
l.
|
|
4429
|
-
l.iesProfile = int( round( s5.b ) );
|
|
4409
|
+
l.penumbraCos = s5.r;
|
|
4410
|
+
l.iesProfile = int( round( s5.g ) );
|
|
4430
4411
|
|
|
4431
4412
|
} else {
|
|
4432
4413
|
|
|
4433
4414
|
l.radius = 0.0;
|
|
4434
|
-
l.near = 0.0;
|
|
4435
4415
|
l.decay = 0.0;
|
|
4436
4416
|
l.distance = 0.0;
|
|
4437
4417
|
|
|
@@ -5149,15 +5129,17 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5149
5129
|
|
|
5150
5130
|
// TODO: subsurface approx?
|
|
5151
5131
|
|
|
5152
|
-
float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
|
|
5132
|
+
// float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
|
|
5133
|
+
float F = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
|
|
5153
5134
|
color = ( 1.0 - F ) * transFactor * metalFactor * wi.z * surf.color * ( retro + lambert ) / PI;
|
|
5135
|
+
|
|
5154
5136
|
return wi.z / PI;
|
|
5155
5137
|
|
|
5156
5138
|
}
|
|
5157
5139
|
|
|
5158
5140
|
vec3 diffuseDirection( vec3 wo, SurfaceRecord surf ) {
|
|
5159
5141
|
|
|
5160
|
-
vec3 lightDirection = sampleSphere(
|
|
5142
|
+
vec3 lightDirection = sampleSphere( rand2( 11 ) );
|
|
5161
5143
|
lightDirection.z += 1.0;
|
|
5162
5144
|
lightDirection = normalize( lightDirection );
|
|
5163
5145
|
|
|
@@ -5202,7 +5184,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5202
5184
|
vec3 halfVector = ggxDirection(
|
|
5203
5185
|
wo,
|
|
5204
5186
|
vec2( roughness ),
|
|
5205
|
-
|
|
5187
|
+
rand2( 12 )
|
|
5206
5188
|
);
|
|
5207
5189
|
|
|
5208
5190
|
// apply to new ray by reflecting off the new normal
|
|
@@ -5239,7 +5221,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5239
5221
|
vec3 halfVector = ggxDirection(
|
|
5240
5222
|
wo,
|
|
5241
5223
|
vec2( filteredRoughness ),
|
|
5242
|
-
|
|
5224
|
+
rand2( 13 )
|
|
5243
5225
|
);
|
|
5244
5226
|
|
|
5245
5227
|
vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
|
|
@@ -5261,7 +5243,8 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5261
5243
|
color = surf.transmission * surf.color;
|
|
5262
5244
|
|
|
5263
5245
|
// PDF
|
|
5264
|
-
float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
|
|
5246
|
+
// float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
|
|
5247
|
+
float F = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
|
|
5265
5248
|
if ( F >= 1.0 ) {
|
|
5266
5249
|
|
|
5267
5250
|
return 0.0;
|
|
@@ -5276,7 +5259,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5276
5259
|
|
|
5277
5260
|
float roughness = surf.filteredRoughness;
|
|
5278
5261
|
float eta = surf.eta;
|
|
5279
|
-
vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere(
|
|
5262
|
+
vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere( rand2( 13 ) ) * roughness );
|
|
5280
5263
|
vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
|
|
5281
5264
|
|
|
5282
5265
|
if ( surf.thinFilm ) {
|
|
@@ -5317,7 +5300,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5317
5300
|
vec3 halfVector = ggxDirection(
|
|
5318
5301
|
wo,
|
|
5319
5302
|
vec2( roughness ),
|
|
5320
|
-
|
|
5303
|
+
rand2( 14 )
|
|
5321
5304
|
);
|
|
5322
5305
|
|
|
5323
5306
|
// apply to new ray by reflecting off the new normal
|
|
@@ -5352,7 +5335,8 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5352
5335
|
|
|
5353
5336
|
float metalness = surf.metalness;
|
|
5354
5337
|
float transmission = surf.transmission;
|
|
5355
|
-
float fEstimate = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
|
|
5338
|
+
// float fEstimate = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
|
|
5339
|
+
float fEstimate = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
|
|
5356
5340
|
|
|
5357
5341
|
float transSpecularProb = mix( max( 0.25, fEstimate ), 1.0, metalness );
|
|
5358
5342
|
float diffSpecularProb = 0.5 + 0.5 * metalness;
|
|
@@ -5468,7 +5452,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5468
5452
|
ScatterRecord sampleRec;
|
|
5469
5453
|
sampleRec.specularPdf = 0.0;
|
|
5470
5454
|
sampleRec.pdf = 1.0 / ( 4.0 * PI );
|
|
5471
|
-
sampleRec.direction = sampleSphere(
|
|
5455
|
+
sampleRec.direction = sampleSphere( rand2( 16 ) );
|
|
5472
5456
|
sampleRec.color = surf.color / ( 4.0 * PI );
|
|
5473
5457
|
return sampleRec;
|
|
5474
5458
|
|
|
@@ -5520,7 +5504,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5520
5504
|
vec3 wi;
|
|
5521
5505
|
vec3 clearcoatWi;
|
|
5522
5506
|
|
|
5523
|
-
float r =
|
|
5507
|
+
float r = rand( 15 );
|
|
5524
5508
|
if ( r <= cdf[0] ) { // diffuse
|
|
5525
5509
|
|
|
5526
5510
|
wi = diffuseDirection( wo, surf );
|
|
@@ -5604,10 +5588,17 @@ const equirectSamplingGLSL = /* glsl */`
|
|
|
5604
5588
|
// samples the color given env map with CDF and returns the pdf of the direction
|
|
5605
5589
|
float sampleEquirect( vec3 direction, inout vec3 color ) {
|
|
5606
5590
|
|
|
5591
|
+
float totalSum = envMapInfo.totalSum;
|
|
5592
|
+
if ( totalSum == 0.0 ) {
|
|
5593
|
+
|
|
5594
|
+
color = vec3( 0.0 );
|
|
5595
|
+
return 1.0;
|
|
5596
|
+
|
|
5597
|
+
}
|
|
5598
|
+
|
|
5607
5599
|
vec2 uv = equirectDirectionToUv( direction );
|
|
5608
5600
|
color = texture2D( envMapInfo.map, uv ).rgb;
|
|
5609
5601
|
|
|
5610
|
-
float totalSum = envMapInfo.totalSum;
|
|
5611
5602
|
float lum = luminance( color );
|
|
5612
5603
|
ivec2 resolution = textureSize( envMapInfo.map, 0 );
|
|
5613
5604
|
float pdf = lum / totalSum;
|
|
@@ -6159,32 +6150,38 @@ const fresnelGLSL = /* glsl */`
|
|
|
6159
6150
|
|
|
6160
6151
|
}
|
|
6161
6152
|
|
|
6162
|
-
|
|
6153
|
+
// TODO: disney fresnel was removed and replaced with this fresnel function to better align with
|
|
6154
|
+
// the glTF but is causing blown out pixels. Should be revisited
|
|
6155
|
+
// float evaluateFresnelWeight( float cosTheta, float eta, float f0 ) {
|
|
6163
6156
|
|
|
6164
|
-
|
|
6157
|
+
// if ( totalInternalReflection( cosTheta, eta ) ) {
|
|
6165
6158
|
|
|
6166
|
-
|
|
6159
|
+
// return 1.0;
|
|
6167
6160
|
|
|
6168
|
-
|
|
6161
|
+
// }
|
|
6169
6162
|
|
|
6170
|
-
|
|
6163
|
+
// return schlickFresnel( cosTheta, f0 );
|
|
6171
6164
|
|
|
6172
|
-
}
|
|
6165
|
+
// }
|
|
6173
6166
|
|
|
6174
|
-
/*
|
|
6175
6167
|
// https://schuttejoe.github.io/post/disneybsdf/
|
|
6176
6168
|
float disneyFresnel( vec3 wo, vec3 wi, vec3 wh, float f0, float eta, float metalness ) {
|
|
6177
6169
|
|
|
6178
6170
|
float dotHV = dot( wo, wh );
|
|
6179
|
-
|
|
6171
|
+
if ( totalInternalReflection( dotHV, eta ) ) {
|
|
6172
|
+
|
|
6173
|
+
return 1.0;
|
|
6174
|
+
|
|
6175
|
+
}
|
|
6180
6176
|
|
|
6177
|
+
float dotHL = dot( wi, wh );
|
|
6181
6178
|
float dielectricFresnel = dielectricFresnel( abs( dotHV ), eta );
|
|
6182
6179
|
float metallicFresnel = schlickFresnel( dotHL, f0 );
|
|
6183
6180
|
|
|
6184
6181
|
return mix( dielectricFresnel, metallicFresnel, metalness );
|
|
6185
6182
|
|
|
6186
6183
|
}
|
|
6187
|
-
|
|
6184
|
+
|
|
6188
6185
|
`;
|
|
6189
6186
|
|
|
6190
6187
|
const arraySamplerTexelFetchGLSL = /*glsl */`
|
|
@@ -6241,28 +6238,28 @@ const pcgGLSL = /* glsl */`
|
|
|
6241
6238
|
}
|
|
6242
6239
|
|
|
6243
6240
|
// returns [ 0, 1 ]
|
|
6244
|
-
float
|
|
6241
|
+
float pcgRand() {
|
|
6245
6242
|
|
|
6246
6243
|
pcg4d( WHITE_NOISE_SEED );
|
|
6247
6244
|
return float( WHITE_NOISE_SEED.x ) / float( 0xffffffffu );
|
|
6248
6245
|
|
|
6249
6246
|
}
|
|
6250
6247
|
|
|
6251
|
-
vec2
|
|
6248
|
+
vec2 pcgRand2() {
|
|
6252
6249
|
|
|
6253
6250
|
pcg4d( WHITE_NOISE_SEED );
|
|
6254
6251
|
return vec2( WHITE_NOISE_SEED.xy ) / float(0xffffffffu);
|
|
6255
6252
|
|
|
6256
6253
|
}
|
|
6257
6254
|
|
|
6258
|
-
vec3
|
|
6255
|
+
vec3 pcgRand3() {
|
|
6259
6256
|
|
|
6260
6257
|
pcg4d( WHITE_NOISE_SEED );
|
|
6261
6258
|
return vec3( WHITE_NOISE_SEED.xyz ) / float( 0xffffffffu );
|
|
6262
6259
|
|
|
6263
6260
|
}
|
|
6264
6261
|
|
|
6265
|
-
vec4
|
|
6262
|
+
vec4 pcgRand4() {
|
|
6266
6263
|
|
|
6267
6264
|
pcg4d( WHITE_NOISE_SEED );
|
|
6268
6265
|
return vec4( WHITE_NOISE_SEED ) / float( 0xffffffffu );
|
|
@@ -6335,7 +6332,7 @@ const cameraUtilsGLSL = /* glsl */`
|
|
|
6335
6332
|
|
|
6336
6333
|
// Jitter the camera ray by finding a uv coordinate at a random sample
|
|
6337
6334
|
// around this pixel's UV coordinate for AA
|
|
6338
|
-
vec2 ruv =
|
|
6335
|
+
vec2 ruv = rand2( 0 );
|
|
6339
6336
|
vec2 jitteredUv = vUv + vec2( tentFilter( ruv.x ) * ssd.x, tentFilter( ruv.y ) * ssd.y );
|
|
6340
6337
|
Ray ray;
|
|
6341
6338
|
|
|
@@ -6380,7 +6377,7 @@ const cameraUtilsGLSL = /* glsl */`
|
|
|
6380
6377
|
|
|
6381
6378
|
// get the aperture sample
|
|
6382
6379
|
// if blades === 0 then we assume a circle
|
|
6383
|
-
vec3 shapeUVW=
|
|
6380
|
+
vec3 shapeUVW= rand3( 1 );
|
|
6384
6381
|
int blades = physicalCamera.apertureBlades;
|
|
6385
6382
|
float anamorphicRatio = physicalCamera.anamorphicRatio;
|
|
6386
6383
|
vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularPolygon( blades, shapeUVW );
|
|
@@ -6416,6 +6413,9 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6416
6413
|
out vec3 color
|
|
6417
6414
|
) {
|
|
6418
6415
|
|
|
6416
|
+
// store the original bounce index so we can reset it after
|
|
6417
|
+
uint originalBounceIndex = sobolBounceIndex;
|
|
6418
|
+
|
|
6419
6419
|
int traversals = state.traversals;
|
|
6420
6420
|
int transmissiveTraversals = state.transmissiveTraversals;
|
|
6421
6421
|
bool isShadowRay = state.isShadowRay;
|
|
@@ -6428,22 +6428,25 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6428
6428
|
|
|
6429
6429
|
color = vec3( 1.0 );
|
|
6430
6430
|
|
|
6431
|
-
|
|
6432
|
-
// and then reset.
|
|
6431
|
+
bool result = true;
|
|
6433
6432
|
for ( int i = 0; i < traversals; i ++ ) {
|
|
6434
6433
|
|
|
6434
|
+
sobolBounceIndex ++;
|
|
6435
|
+
|
|
6435
6436
|
int hitType = traceScene( ray, fogMaterial, surfaceHit );
|
|
6436
6437
|
|
|
6437
6438
|
if ( hitType == FOG_HIT ) {
|
|
6438
6439
|
|
|
6439
|
-
|
|
6440
|
+
result = true;
|
|
6441
|
+
break;
|
|
6440
6442
|
|
|
6441
6443
|
} else if ( hitType == SURFACE_HIT ) {
|
|
6442
6444
|
|
|
6443
6445
|
float totalDist = distance( startPoint, ray.origin + ray.direction * surfaceHit.dist );
|
|
6444
6446
|
if ( totalDist > rayDist ) {
|
|
6445
6447
|
|
|
6446
|
-
|
|
6448
|
+
result = false;
|
|
6449
|
+
break;
|
|
6447
6450
|
|
|
6448
6451
|
}
|
|
6449
6452
|
|
|
@@ -6525,7 +6528,7 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6525
6528
|
bool useAlphaTest = alphaTest != 0.0;
|
|
6526
6529
|
float transmissionFactor = ( 1.0 - metalness ) * transmission;
|
|
6527
6530
|
if (
|
|
6528
|
-
transmissionFactor < rand() && ! (
|
|
6531
|
+
transmissionFactor < rand( 9 ) && ! (
|
|
6529
6532
|
// material sidedness
|
|
6530
6533
|
material.side != 0.0 && surfaceHit.side == material.side
|
|
6531
6534
|
|
|
@@ -6533,11 +6536,12 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6533
6536
|
|| useAlphaTest && albedo.a < alphaTest
|
|
6534
6537
|
|
|
6535
6538
|
// opacity
|
|
6536
|
-
|| material.transparent && ! useAlphaTest && albedo.a < rand()
|
|
6539
|
+
|| material.transparent && ! useAlphaTest && albedo.a < rand( 10 )
|
|
6537
6540
|
)
|
|
6538
6541
|
) {
|
|
6539
6542
|
|
|
6540
|
-
|
|
6543
|
+
result = true;
|
|
6544
|
+
break;
|
|
6541
6545
|
|
|
6542
6546
|
}
|
|
6543
6547
|
|
|
@@ -6563,13 +6567,16 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6563
6567
|
|
|
6564
6568
|
} else {
|
|
6565
6569
|
|
|
6566
|
-
|
|
6570
|
+
result = false;
|
|
6571
|
+
break;
|
|
6567
6572
|
|
|
6568
6573
|
}
|
|
6569
6574
|
|
|
6570
6575
|
}
|
|
6571
6576
|
|
|
6572
|
-
|
|
6577
|
+
// reset the bounce index
|
|
6578
|
+
sobolBounceIndex = originalBounceIndex;
|
|
6579
|
+
return result;
|
|
6573
6580
|
|
|
6574
6581
|
}
|
|
6575
6582
|
|
|
@@ -6600,7 +6607,7 @@ const traceSceneGLSL = /* glsl */`
|
|
|
6600
6607
|
|
|
6601
6608
|
// offset the distance so we don't run into issues with particles on the same surface
|
|
6602
6609
|
// as other objects
|
|
6603
|
-
float particleDist = intersectFogVolume( fogMaterial,
|
|
6610
|
+
float particleDist = intersectFogVolume( fogMaterial, rand( 1 ) );
|
|
6604
6611
|
if ( particleDist + RAY_OFFSET < surfaceHit.dist ) {
|
|
6605
6612
|
|
|
6606
6613
|
surfaceHit.side = 1.0;
|
|
@@ -6663,6 +6670,7 @@ const getSurfaceRecordGLSL = /* glsl */`
|
|
|
6663
6670
|
|
|
6664
6671
|
vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
|
|
6665
6672
|
albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
|
|
6673
|
+
|
|
6666
6674
|
}
|
|
6667
6675
|
|
|
6668
6676
|
if ( material.vertexColors ) {
|
|
@@ -6694,7 +6702,7 @@ const getSurfaceRecordGLSL = /* glsl */`
|
|
|
6694
6702
|
|| useAlphaTest && albedo.a < alphaTest
|
|
6695
6703
|
|
|
6696
6704
|
// opacity
|
|
6697
|
-
|| material.transparent && ! useAlphaTest && albedo.a <
|
|
6705
|
+
|| material.transparent && ! useAlphaTest && albedo.a < rand( 3 )
|
|
6698
6706
|
) {
|
|
6699
6707
|
|
|
6700
6708
|
return SKIP_SURFACE;
|
|
@@ -6956,10 +6964,10 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6956
6964
|
vec3 result = vec3( 0.0 );
|
|
6957
6965
|
|
|
6958
6966
|
// uniformly pick a light or environment map
|
|
6959
|
-
if( lightsDenom != 0.0 &&
|
|
6967
|
+
if( lightsDenom != 0.0 && rand( 5 ) < float( lights.count ) / lightsDenom ) {
|
|
6960
6968
|
|
|
6961
6969
|
// sample a light or environment
|
|
6962
|
-
LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin,
|
|
6970
|
+
LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, rand3( 6 ) );
|
|
6963
6971
|
|
|
6964
6972
|
bool isSampleBelowSurface = ! surf.volumeParticle && dot( surf.faceNormal, lightRec.direction ) < 0.0;
|
|
6965
6973
|
if ( isSampleBelowSurface ) {
|
|
@@ -6994,11 +7002,11 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6994
7002
|
|
|
6995
7003
|
}
|
|
6996
7004
|
|
|
6997
|
-
} else {
|
|
7005
|
+
} else if ( envMapInfo.totalSum != 0.0 && environmentIntensity != 0.0 ) {
|
|
6998
7006
|
|
|
6999
7007
|
// find a sample in the environment map to include in the contribution
|
|
7000
7008
|
vec3 envColor, envDirection;
|
|
7001
|
-
float envPdf = sampleEquirectProbability(
|
|
7009
|
+
float envPdf = sampleEquirectProbability( rand2( 7 ), envColor, envDirection );
|
|
7002
7010
|
envDirection = invEnvRotation3x3 * envDirection;
|
|
7003
7011
|
|
|
7004
7012
|
// this env sampling is not set up for transmissive sampling and yields overly bright
|
|
@@ -7047,6 +7055,667 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
7047
7055
|
|
|
7048
7056
|
`;
|
|
7049
7057
|
|
|
7058
|
+
const stratifiedTextureGLSL = /* glsl */`
|
|
7059
|
+
|
|
7060
|
+
uniform sampler2D stratifiedTexture;
|
|
7061
|
+
uniform sampler2D stratifiedOffsetTexture;
|
|
7062
|
+
|
|
7063
|
+
uint sobolPixelIndex = 0u;
|
|
7064
|
+
uint sobolPathIndex = 0u;
|
|
7065
|
+
uint sobolBounceIndex = 0u;
|
|
7066
|
+
vec4 pixelSeed = vec4( 0 );
|
|
7067
|
+
|
|
7068
|
+
vec4 rand4( int v ) {
|
|
7069
|
+
|
|
7070
|
+
ivec2 uv = ivec2( v, sobolBounceIndex );
|
|
7071
|
+
vec4 stratifiedSample = texelFetch( stratifiedTexture, uv, 0 );
|
|
7072
|
+
return fract( stratifiedSample + pixelSeed.r ); // blue noise + stratified samples
|
|
7073
|
+
|
|
7074
|
+
}
|
|
7075
|
+
|
|
7076
|
+
vec3 rand3( int v ) {
|
|
7077
|
+
|
|
7078
|
+
return rand4( v ).xyz;
|
|
7079
|
+
|
|
7080
|
+
}
|
|
7081
|
+
|
|
7082
|
+
vec2 rand2( int v ) {
|
|
7083
|
+
|
|
7084
|
+
return rand4( v ).xy;
|
|
7085
|
+
|
|
7086
|
+
}
|
|
7087
|
+
|
|
7088
|
+
float rand( int v ) {
|
|
7089
|
+
|
|
7090
|
+
return rand4( v ).x;
|
|
7091
|
+
|
|
7092
|
+
}
|
|
7093
|
+
|
|
7094
|
+
void rng_initialize( vec2 screenCoord, int frame ) {
|
|
7095
|
+
|
|
7096
|
+
// tile the small noise texture across the entire screen
|
|
7097
|
+
ivec2 noiseSize = ivec2( textureSize( stratifiedOffsetTexture, 0 ) );
|
|
7098
|
+
pixelSeed = texelFetch( stratifiedOffsetTexture, ivec2( screenCoord.xy ) % noiseSize, 0 );
|
|
7099
|
+
|
|
7100
|
+
}
|
|
7101
|
+
|
|
7102
|
+
`;
|
|
7103
|
+
|
|
7104
|
+
// Stratified Sampling based on implementation from hoverinc pathtracer
|
|
7105
|
+
// - https://github.com/hoverinc/ray-tracing-renderer
|
|
7106
|
+
// - http://www.pbr-book.org/3ed-2018/Sampling_and_Reconstruction/Stratified_Sampling.html
|
|
7107
|
+
|
|
7108
|
+
function shuffle( arr ) {
|
|
7109
|
+
|
|
7110
|
+
for ( let i = arr.length - 1; i > 0; i -- ) {
|
|
7111
|
+
|
|
7112
|
+
const j = Math.floor( Math.random() * ( i + 1 ) );
|
|
7113
|
+
const x = arr[ i ];
|
|
7114
|
+
arr[ i ] = arr[ j ];
|
|
7115
|
+
arr[ j ] = x;
|
|
7116
|
+
|
|
7117
|
+
}
|
|
7118
|
+
|
|
7119
|
+
return arr;
|
|
7120
|
+
|
|
7121
|
+
}
|
|
7122
|
+
|
|
7123
|
+
// strataCount : The number of bins per dimension
|
|
7124
|
+
// dimensions : The number of dimensions to generate stratified values for
|
|
7125
|
+
class StratifiedSampler {
|
|
7126
|
+
|
|
7127
|
+
constructor( strataCount, dimensions ) {
|
|
7128
|
+
|
|
7129
|
+
const l = strataCount ** dimensions;
|
|
7130
|
+
const strata = new Uint16Array( l );
|
|
7131
|
+
let index = l;
|
|
7132
|
+
|
|
7133
|
+
// each integer represents a statum bin
|
|
7134
|
+
for ( let i = 0; i < l; i ++ ) {
|
|
7135
|
+
|
|
7136
|
+
strata[ i ] = i;
|
|
7137
|
+
|
|
7138
|
+
}
|
|
7139
|
+
|
|
7140
|
+
this.samples = new Float32Array( dimensions );
|
|
7141
|
+
|
|
7142
|
+
this.strataCount = strataCount;
|
|
7143
|
+
|
|
7144
|
+
this.restart = function () {
|
|
7145
|
+
|
|
7146
|
+
index = 0;
|
|
7147
|
+
|
|
7148
|
+
};
|
|
7149
|
+
|
|
7150
|
+
this.next = function () {
|
|
7151
|
+
|
|
7152
|
+
const { samples } = this;
|
|
7153
|
+
|
|
7154
|
+
if ( index >= strata.length ) {
|
|
7155
|
+
|
|
7156
|
+
shuffle( strata );
|
|
7157
|
+
this.restart();
|
|
7158
|
+
|
|
7159
|
+
}
|
|
7160
|
+
|
|
7161
|
+
let stratum = strata[ index ++ ];
|
|
7162
|
+
|
|
7163
|
+
for ( let i = 0; i < dimensions; i ++ ) {
|
|
7164
|
+
|
|
7165
|
+
samples[ i ] = ( stratum % strataCount + Math.random() ) / strataCount;
|
|
7166
|
+
stratum = Math.floor( stratum / strataCount );
|
|
7167
|
+
|
|
7168
|
+
}
|
|
7169
|
+
|
|
7170
|
+
return samples;
|
|
7171
|
+
|
|
7172
|
+
};
|
|
7173
|
+
|
|
7174
|
+
}
|
|
7175
|
+
|
|
7176
|
+
}
|
|
7177
|
+
|
|
7178
|
+
// Stratified Sampling based on implementation from hoverinc pathtracer
|
|
7179
|
+
|
|
7180
|
+
// Stratified set of data with each tuple stratified separately and combined
|
|
7181
|
+
class StratifiedSamplerCombined {
|
|
7182
|
+
|
|
7183
|
+
constructor( strataCount, listOfDimensions ) {
|
|
7184
|
+
|
|
7185
|
+
let totalDim = 0;
|
|
7186
|
+
for ( const dim of listOfDimensions ) {
|
|
7187
|
+
|
|
7188
|
+
totalDim += dim;
|
|
7189
|
+
|
|
7190
|
+
}
|
|
7191
|
+
|
|
7192
|
+
const combined = new Float32Array( totalDim );
|
|
7193
|
+
const strataObjs = [];
|
|
7194
|
+
let offset = 0;
|
|
7195
|
+
for ( const dim of listOfDimensions ) {
|
|
7196
|
+
|
|
7197
|
+
const sampler = new StratifiedSampler( strataCount, dim );
|
|
7198
|
+
sampler.samples = new Float32Array( combined.buffer, offset, sampler.samples.length );
|
|
7199
|
+
offset += sampler.samples.length * 4;
|
|
7200
|
+
strataObjs.push( sampler );
|
|
7201
|
+
|
|
7202
|
+
}
|
|
7203
|
+
|
|
7204
|
+
this.samples = combined;
|
|
7205
|
+
|
|
7206
|
+
this.strataCount = strataCount;
|
|
7207
|
+
|
|
7208
|
+
this.next = function () {
|
|
7209
|
+
|
|
7210
|
+
for ( const strata of strataObjs ) {
|
|
7211
|
+
|
|
7212
|
+
strata.next();
|
|
7213
|
+
|
|
7214
|
+
}
|
|
7215
|
+
|
|
7216
|
+
return combined;
|
|
7217
|
+
|
|
7218
|
+
};
|
|
7219
|
+
|
|
7220
|
+
this.restart = function () {
|
|
7221
|
+
|
|
7222
|
+
for ( const strata of strataObjs ) {
|
|
7223
|
+
|
|
7224
|
+
strata.restart();
|
|
7225
|
+
|
|
7226
|
+
}
|
|
7227
|
+
|
|
7228
|
+
};
|
|
7229
|
+
|
|
7230
|
+
}
|
|
7231
|
+
|
|
7232
|
+
}
|
|
7233
|
+
|
|
7234
|
+
class StratifiedSamplesTexture extends DataTexture {
|
|
7235
|
+
|
|
7236
|
+
constructor( count = 1, depth = 1, strata = 8 ) {
|
|
7237
|
+
|
|
7238
|
+
super( new Float32Array( 1 ), 1, 1, RGBAFormat, FloatType );
|
|
7239
|
+
this.minFilter = NearestFilter;
|
|
7240
|
+
this.magFilter = NearestFilter;
|
|
7241
|
+
|
|
7242
|
+
this.strata = strata;
|
|
7243
|
+
this.sampler = null;
|
|
7244
|
+
|
|
7245
|
+
this.init( count, depth, strata );
|
|
7246
|
+
|
|
7247
|
+
}
|
|
7248
|
+
|
|
7249
|
+
init( count, depth, strata = this.strata ) {
|
|
7250
|
+
|
|
7251
|
+
const { image } = this;
|
|
7252
|
+
if ( image.width === depth && image.height === count ) {
|
|
7253
|
+
|
|
7254
|
+
return;
|
|
7255
|
+
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7258
|
+
const dimensions = new Array( count * depth ).fill( 4 );
|
|
7259
|
+
const sampler = new StratifiedSamplerCombined( strata, dimensions );
|
|
7260
|
+
|
|
7261
|
+
image.width = depth;
|
|
7262
|
+
image.height = count;
|
|
7263
|
+
image.data = sampler.samples;
|
|
7264
|
+
|
|
7265
|
+
this.sampler = sampler;
|
|
7266
|
+
|
|
7267
|
+
this.dispose();
|
|
7268
|
+
this.next();
|
|
7269
|
+
|
|
7270
|
+
}
|
|
7271
|
+
|
|
7272
|
+
next() {
|
|
7273
|
+
|
|
7274
|
+
this.sampler.next();
|
|
7275
|
+
this.needsUpdate = true;
|
|
7276
|
+
|
|
7277
|
+
}
|
|
7278
|
+
|
|
7279
|
+
}
|
|
7280
|
+
|
|
7281
|
+
function shuffleArray( array, random = Math.random ) {
|
|
7282
|
+
|
|
7283
|
+
for ( let i = array.length - 1; i > 0; i -- ) {
|
|
7284
|
+
|
|
7285
|
+
const replaceIndex = ~ ~ ( ( random() - 1e-6 ) * i );
|
|
7286
|
+
const tmp = array[ i ];
|
|
7287
|
+
array[ i ] = array[ replaceIndex ];
|
|
7288
|
+
array[ replaceIndex ] = tmp;
|
|
7289
|
+
|
|
7290
|
+
}
|
|
7291
|
+
|
|
7292
|
+
}
|
|
7293
|
+
|
|
7294
|
+
function fillWithOnes( array, count ) {
|
|
7295
|
+
|
|
7296
|
+
array.fill( 0 );
|
|
7297
|
+
|
|
7298
|
+
for ( let i = 0; i < count; i ++ ) {
|
|
7299
|
+
|
|
7300
|
+
array[ i ] = 1;
|
|
7301
|
+
|
|
7302
|
+
}
|
|
7303
|
+
|
|
7304
|
+
}
|
|
7305
|
+
|
|
7306
|
+
class BlueNoiseSamples {
|
|
7307
|
+
|
|
7308
|
+
constructor( size ) {
|
|
7309
|
+
|
|
7310
|
+
this.count = 0;
|
|
7311
|
+
this.size = - 1;
|
|
7312
|
+
this.sigma = - 1;
|
|
7313
|
+
this.radius = - 1;
|
|
7314
|
+
this.lookupTable = null;
|
|
7315
|
+
this.score = null;
|
|
7316
|
+
this.binaryPattern = null;
|
|
7317
|
+
|
|
7318
|
+
this.resize( size );
|
|
7319
|
+
this.setSigma( 1.5 );
|
|
7320
|
+
|
|
7321
|
+
}
|
|
7322
|
+
|
|
7323
|
+
findVoid() {
|
|
7324
|
+
|
|
7325
|
+
const { score, binaryPattern } = this;
|
|
7326
|
+
|
|
7327
|
+
let currValue = Infinity;
|
|
7328
|
+
let currIndex = - 1;
|
|
7329
|
+
for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
|
|
7330
|
+
|
|
7331
|
+
if ( binaryPattern[ i ] !== 0 ) {
|
|
7332
|
+
|
|
7333
|
+
continue;
|
|
7334
|
+
|
|
7335
|
+
}
|
|
7336
|
+
|
|
7337
|
+
const pScore = score[ i ];
|
|
7338
|
+
if ( pScore < currValue ) {
|
|
7339
|
+
|
|
7340
|
+
currValue = pScore;
|
|
7341
|
+
currIndex = i;
|
|
7342
|
+
|
|
7343
|
+
}
|
|
7344
|
+
|
|
7345
|
+
}
|
|
7346
|
+
|
|
7347
|
+
return currIndex;
|
|
7348
|
+
|
|
7349
|
+
}
|
|
7350
|
+
|
|
7351
|
+
findCluster() {
|
|
7352
|
+
|
|
7353
|
+
const { score, binaryPattern } = this;
|
|
7354
|
+
|
|
7355
|
+
let currValue = - Infinity;
|
|
7356
|
+
let currIndex = - 1;
|
|
7357
|
+
for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
|
|
7358
|
+
|
|
7359
|
+
if ( binaryPattern[ i ] !== 1 ) {
|
|
7360
|
+
|
|
7361
|
+
continue;
|
|
7362
|
+
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7365
|
+
const pScore = score[ i ];
|
|
7366
|
+
if ( pScore > currValue ) {
|
|
7367
|
+
|
|
7368
|
+
currValue = pScore;
|
|
7369
|
+
currIndex = i;
|
|
7370
|
+
|
|
7371
|
+
}
|
|
7372
|
+
|
|
7373
|
+
}
|
|
7374
|
+
|
|
7375
|
+
return currIndex;
|
|
7376
|
+
|
|
7377
|
+
}
|
|
7378
|
+
|
|
7379
|
+
setSigma( sigma ) {
|
|
7380
|
+
|
|
7381
|
+
if ( sigma === this.sigma ) {
|
|
7382
|
+
|
|
7383
|
+
return;
|
|
7384
|
+
|
|
7385
|
+
}
|
|
7386
|
+
|
|
7387
|
+
// generate a radius in which the score will be updated under the
|
|
7388
|
+
// assumption that e^-10 is insignificant enough to be the border at
|
|
7389
|
+
// which we drop off.
|
|
7390
|
+
const radius = ~ ~ ( Math.sqrt( 10 * 2 * ( sigma ** 2 ) ) + 1 );
|
|
7391
|
+
const lookupWidth = 2 * radius + 1;
|
|
7392
|
+
const lookupTable = new Float32Array( lookupWidth * lookupWidth );
|
|
7393
|
+
const sigma2 = sigma * sigma;
|
|
7394
|
+
for ( let x = - radius; x <= radius; x ++ ) {
|
|
7395
|
+
|
|
7396
|
+
for ( let y = - radius; y <= radius; y ++ ) {
|
|
7397
|
+
|
|
7398
|
+
const index = ( radius + y ) * lookupWidth + x + radius;
|
|
7399
|
+
const dist2 = x * x + y * y;
|
|
7400
|
+
lookupTable[ index ] = Math.E ** ( - dist2 / ( 2 * sigma2 ) );
|
|
7401
|
+
|
|
7402
|
+
}
|
|
7403
|
+
|
|
7404
|
+
}
|
|
7405
|
+
|
|
7406
|
+
this.lookupTable = lookupTable;
|
|
7407
|
+
this.sigma = sigma;
|
|
7408
|
+
this.radius = radius;
|
|
7409
|
+
|
|
7410
|
+
}
|
|
7411
|
+
|
|
7412
|
+
resize( size ) {
|
|
7413
|
+
|
|
7414
|
+
if ( this.size !== size ) {
|
|
7415
|
+
|
|
7416
|
+
this.size = size;
|
|
7417
|
+
this.score = new Float32Array( size * size );
|
|
7418
|
+
this.binaryPattern = new Uint8Array( size * size );
|
|
7419
|
+
|
|
7420
|
+
}
|
|
7421
|
+
|
|
7422
|
+
|
|
7423
|
+
}
|
|
7424
|
+
|
|
7425
|
+
invert() {
|
|
7426
|
+
|
|
7427
|
+
const { binaryPattern, score, size } = this;
|
|
7428
|
+
|
|
7429
|
+
score.fill( 0 );
|
|
7430
|
+
|
|
7431
|
+
for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
|
|
7432
|
+
|
|
7433
|
+
if ( binaryPattern[ i ] === 0 ) {
|
|
7434
|
+
|
|
7435
|
+
const y = ~ ~ ( i / size );
|
|
7436
|
+
const x = i - y * size;
|
|
7437
|
+
this.updateScore( x, y, 1 );
|
|
7438
|
+
binaryPattern[ i ] = 1;
|
|
7439
|
+
|
|
7440
|
+
} else {
|
|
7441
|
+
|
|
7442
|
+
binaryPattern[ i ] = 0;
|
|
7443
|
+
|
|
7444
|
+
}
|
|
7445
|
+
|
|
7446
|
+
}
|
|
7447
|
+
|
|
7448
|
+
}
|
|
7449
|
+
|
|
7450
|
+
updateScore( x, y, multiplier ) {
|
|
7451
|
+
|
|
7452
|
+
// TODO: Is there a way to keep track of the highest and lowest scores here to avoid have to search over
|
|
7453
|
+
// everything in the buffer?
|
|
7454
|
+
const { size, score, lookupTable } = this;
|
|
7455
|
+
|
|
7456
|
+
// const sigma2 = sigma * sigma;
|
|
7457
|
+
// const radius = Math.floor( size / 2 );
|
|
7458
|
+
const radius = this.radius;
|
|
7459
|
+
const lookupWidth = 2 * radius + 1;
|
|
7460
|
+
for ( let px = - radius; px <= radius; px ++ ) {
|
|
7461
|
+
|
|
7462
|
+
for ( let py = - radius; py <= radius; py ++ ) {
|
|
7463
|
+
|
|
7464
|
+
// const dist2 = px * px + py * py;
|
|
7465
|
+
// const value = Math.E ** ( - dist2 / ( 2 * sigma2 ) );
|
|
7466
|
+
|
|
7467
|
+
const lookupIndex = ( radius + py ) * lookupWidth + px + radius;
|
|
7468
|
+
const value = lookupTable[ lookupIndex ];
|
|
7469
|
+
|
|
7470
|
+
let sx = ( x + px );
|
|
7471
|
+
sx = sx < 0 ? size + sx : sx % size;
|
|
7472
|
+
|
|
7473
|
+
let sy = ( y + py );
|
|
7474
|
+
sy = sy < 0 ? size + sy : sy % size;
|
|
7475
|
+
|
|
7476
|
+
const sindex = sy * size + sx;
|
|
7477
|
+
score[ sindex ] += multiplier * value;
|
|
7478
|
+
|
|
7479
|
+
}
|
|
7480
|
+
|
|
7481
|
+
}
|
|
7482
|
+
|
|
7483
|
+
}
|
|
7484
|
+
|
|
7485
|
+
addPointIndex( index ) {
|
|
7486
|
+
|
|
7487
|
+
this.binaryPattern[ index ] = 1;
|
|
7488
|
+
|
|
7489
|
+
const size = this.size;
|
|
7490
|
+
const y = ~ ~ ( index / size );
|
|
7491
|
+
const x = index - y * size;
|
|
7492
|
+
this.updateScore( x, y, 1 );
|
|
7493
|
+
this.count ++;
|
|
7494
|
+
|
|
7495
|
+
}
|
|
7496
|
+
|
|
7497
|
+
removePointIndex( index ) {
|
|
7498
|
+
|
|
7499
|
+
this.binaryPattern[ index ] = 0;
|
|
7500
|
+
|
|
7501
|
+
const size = this.size;
|
|
7502
|
+
const y = ~ ~ ( index / size );
|
|
7503
|
+
const x = index - y * size;
|
|
7504
|
+
this.updateScore( x, y, - 1 );
|
|
7505
|
+
this.count --;
|
|
7506
|
+
|
|
7507
|
+
}
|
|
7508
|
+
|
|
7509
|
+
copy( source ) {
|
|
7510
|
+
|
|
7511
|
+
this.resize( source.size );
|
|
7512
|
+
this.score.set( source.score );
|
|
7513
|
+
this.binaryPattern.set( source.binaryPattern );
|
|
7514
|
+
this.setSigma( source.sigma );
|
|
7515
|
+
this.count = source.count;
|
|
7516
|
+
|
|
7517
|
+
}
|
|
7518
|
+
|
|
7519
|
+
}
|
|
7520
|
+
|
|
7521
|
+
class BlueNoiseGenerator {
|
|
7522
|
+
|
|
7523
|
+
constructor() {
|
|
7524
|
+
|
|
7525
|
+
this.random = Math.random;
|
|
7526
|
+
this.sigma = 1.5;
|
|
7527
|
+
this.size = 64;
|
|
7528
|
+
this.majorityPointsRatio = 0.1;
|
|
7529
|
+
|
|
7530
|
+
this.samples = new BlueNoiseSamples( 1 );
|
|
7531
|
+
this.savedSamples = new BlueNoiseSamples( 1 );
|
|
7532
|
+
|
|
7533
|
+
}
|
|
7534
|
+
|
|
7535
|
+
generate() {
|
|
7536
|
+
|
|
7537
|
+
// http://cv.ulichney.com/papers/1993-void-cluster.pdf
|
|
7538
|
+
|
|
7539
|
+
const {
|
|
7540
|
+
samples,
|
|
7541
|
+
savedSamples,
|
|
7542
|
+
sigma,
|
|
7543
|
+
majorityPointsRatio,
|
|
7544
|
+
size,
|
|
7545
|
+
} = this;
|
|
7546
|
+
|
|
7547
|
+
samples.resize( size );
|
|
7548
|
+
samples.setSigma( sigma );
|
|
7549
|
+
|
|
7550
|
+
// 1. Randomly place the minority points.
|
|
7551
|
+
const pointCount = Math.floor( size * size * majorityPointsRatio );
|
|
7552
|
+
const initialSamples = samples.binaryPattern;
|
|
7553
|
+
|
|
7554
|
+
fillWithOnes( initialSamples, pointCount );
|
|
7555
|
+
shuffleArray( initialSamples, this.random );
|
|
7556
|
+
|
|
7557
|
+
for ( let i = 0, l = initialSamples.length; i < l; i ++ ) {
|
|
7558
|
+
|
|
7559
|
+
if ( initialSamples[ i ] === 1 ) {
|
|
7560
|
+
|
|
7561
|
+
samples.addPointIndex( i );
|
|
7562
|
+
|
|
7563
|
+
}
|
|
7564
|
+
|
|
7565
|
+
}
|
|
7566
|
+
|
|
7567
|
+
// 2. Remove minority point that is in densest cluster and place it in the largest void.
|
|
7568
|
+
while ( true ) {
|
|
7569
|
+
|
|
7570
|
+
const clusterIndex = samples.findCluster();
|
|
7571
|
+
samples.removePointIndex( clusterIndex );
|
|
7572
|
+
|
|
7573
|
+
const voidIndex = samples.findVoid();
|
|
7574
|
+
if ( clusterIndex === voidIndex ) {
|
|
7575
|
+
|
|
7576
|
+
samples.addPointIndex( clusterIndex );
|
|
7577
|
+
break;
|
|
7578
|
+
|
|
7579
|
+
}
|
|
7580
|
+
|
|
7581
|
+
samples.addPointIndex( voidIndex );
|
|
7582
|
+
|
|
7583
|
+
}
|
|
7584
|
+
|
|
7585
|
+
// 3. PHASE I: Assign a rank to each progressively less dense cluster point and put it
|
|
7586
|
+
// in the dither array.
|
|
7587
|
+
const ditherArray = new Uint32Array( size * size );
|
|
7588
|
+
savedSamples.copy( samples );
|
|
7589
|
+
|
|
7590
|
+
let rank;
|
|
7591
|
+
rank = samples.count - 1;
|
|
7592
|
+
while ( rank >= 0 ) {
|
|
7593
|
+
|
|
7594
|
+
const clusterIndex = samples.findCluster();
|
|
7595
|
+
samples.removePointIndex( clusterIndex );
|
|
7596
|
+
|
|
7597
|
+
ditherArray[ clusterIndex ] = rank;
|
|
7598
|
+
rank --;
|
|
7599
|
+
|
|
7600
|
+
}
|
|
7601
|
+
|
|
7602
|
+
// 4. PHASE II: Do the same thing for the largest voids up to half of the total pixels using
|
|
7603
|
+
// the initial binary pattern.
|
|
7604
|
+
const totalSize = size * size;
|
|
7605
|
+
rank = savedSamples.count;
|
|
7606
|
+
while ( rank < totalSize / 2 ) {
|
|
7607
|
+
|
|
7608
|
+
const voidIndex = savedSamples.findVoid();
|
|
7609
|
+
savedSamples.addPointIndex( voidIndex );
|
|
7610
|
+
ditherArray[ voidIndex ] = rank;
|
|
7611
|
+
rank ++;
|
|
7612
|
+
|
|
7613
|
+
}
|
|
7614
|
+
|
|
7615
|
+
// 5. PHASE III: Invert the pattern and finish out by assigning a rank to the remaining
|
|
7616
|
+
// and iteratively removing them.
|
|
7617
|
+
savedSamples.invert();
|
|
7618
|
+
|
|
7619
|
+
while ( rank < totalSize ) {
|
|
7620
|
+
|
|
7621
|
+
const clusterIndex = savedSamples.findCluster();
|
|
7622
|
+
savedSamples.removePointIndex( clusterIndex );
|
|
7623
|
+
ditherArray[ clusterIndex ] = rank;
|
|
7624
|
+
rank ++;
|
|
7625
|
+
|
|
7626
|
+
}
|
|
7627
|
+
|
|
7628
|
+
return { data: ditherArray, maxValue: totalSize };
|
|
7629
|
+
|
|
7630
|
+
}
|
|
7631
|
+
|
|
7632
|
+
}
|
|
7633
|
+
|
|
7634
|
+
function getStride( channels ) {
|
|
7635
|
+
|
|
7636
|
+
if ( channels >= 3 ) {
|
|
7637
|
+
|
|
7638
|
+
return 4;
|
|
7639
|
+
|
|
7640
|
+
} else {
|
|
7641
|
+
|
|
7642
|
+
return channels;
|
|
7643
|
+
|
|
7644
|
+
}
|
|
7645
|
+
|
|
7646
|
+
}
|
|
7647
|
+
|
|
7648
|
+
function getFormat( channels ) {
|
|
7649
|
+
|
|
7650
|
+
switch ( channels ) {
|
|
7651
|
+
|
|
7652
|
+
case 1:
|
|
7653
|
+
return RedFormat;
|
|
7654
|
+
case 2:
|
|
7655
|
+
return RGFormat;
|
|
7656
|
+
default:
|
|
7657
|
+
return RGBAFormat;
|
|
7658
|
+
|
|
7659
|
+
}
|
|
7660
|
+
|
|
7661
|
+
}
|
|
7662
|
+
|
|
7663
|
+
class BlueNoiseTexture extends DataTexture {
|
|
7664
|
+
|
|
7665
|
+
constructor( size = 64, channels = 1 ) {
|
|
7666
|
+
|
|
7667
|
+
super( new Float32Array( 4 ), 1, 1, RGBAFormat, FloatType );
|
|
7668
|
+
this.minFilter = NearestFilter;
|
|
7669
|
+
this.magFilter = NearestFilter;
|
|
7670
|
+
|
|
7671
|
+
this.size = size;
|
|
7672
|
+
this.channels = channels;
|
|
7673
|
+
this.update();
|
|
7674
|
+
|
|
7675
|
+
}
|
|
7676
|
+
|
|
7677
|
+
update() {
|
|
7678
|
+
|
|
7679
|
+
const channels = this.channels;
|
|
7680
|
+
const size = this.size;
|
|
7681
|
+
const generator = new BlueNoiseGenerator();
|
|
7682
|
+
generator.channels = channels;
|
|
7683
|
+
generator.size = size;
|
|
7684
|
+
|
|
7685
|
+
const stride = getStride( channels );
|
|
7686
|
+
const format = getFormat( stride );
|
|
7687
|
+
if ( this.image.width !== size || format !== this.format ) {
|
|
7688
|
+
|
|
7689
|
+
this.image.width = size;
|
|
7690
|
+
this.image.height = size;
|
|
7691
|
+
this.image.data = new Float32Array( ( size ** 2 ) * stride );
|
|
7692
|
+
this.format = format;
|
|
7693
|
+
this.dispose();
|
|
7694
|
+
|
|
7695
|
+
}
|
|
7696
|
+
|
|
7697
|
+
const data = this.image.data;
|
|
7698
|
+
for ( let i = 0, l = channels; i < l; i ++ ) {
|
|
7699
|
+
|
|
7700
|
+
const result = generator.generate();
|
|
7701
|
+
const bin = result.data;
|
|
7702
|
+
const maxValue = result.maxValue;
|
|
7703
|
+
|
|
7704
|
+
for ( let j = 0, l2 = bin.length; j < l2; j ++ ) {
|
|
7705
|
+
|
|
7706
|
+
const value = bin[ j ] / maxValue;
|
|
7707
|
+
data[ j * stride + i ] = value;
|
|
7708
|
+
|
|
7709
|
+
}
|
|
7710
|
+
|
|
7711
|
+
}
|
|
7712
|
+
|
|
7713
|
+
this.needsUpdate = true;
|
|
7714
|
+
|
|
7715
|
+
}
|
|
7716
|
+
|
|
7717
|
+
}
|
|
7718
|
+
|
|
7050
7719
|
class PhysicalPathTracingMaterial extends MaterialBase {
|
|
7051
7720
|
|
|
7052
7721
|
onBeforeRender() {
|
|
@@ -7070,7 +7739,12 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7070
7739
|
FEATURE_DOF: 1,
|
|
7071
7740
|
FEATURE_BACKGROUND_MAP: 0,
|
|
7072
7741
|
FEATURE_FOG: 1,
|
|
7073
|
-
|
|
7742
|
+
|
|
7743
|
+
// 0 = PCG
|
|
7744
|
+
// 1 = Sobol
|
|
7745
|
+
// 2 = Stratified List
|
|
7746
|
+
RANDOM_TYPE: 2,
|
|
7747
|
+
|
|
7074
7748
|
// 0 = Perspective
|
|
7075
7749
|
// 1 = Orthographic
|
|
7076
7750
|
// 2 = Equirectangular
|
|
@@ -7112,6 +7786,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7112
7786
|
|
|
7113
7787
|
backgroundAlpha: { value: 1.0 },
|
|
7114
7788
|
sobolTexture: { value: null },
|
|
7789
|
+
stratifiedTexture: { value: new StratifiedSamplesTexture() },
|
|
7790
|
+
stratifiedOffsetTexture: { value: new BlueNoiseTexture( 64, 1 ) },
|
|
7115
7791
|
},
|
|
7116
7792
|
|
|
7117
7793
|
vertexShader: /* glsl */`
|
|
@@ -7151,23 +7827,35 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7151
7827
|
${ materialStructGLSL }
|
|
7152
7828
|
|
|
7153
7829
|
// random
|
|
7154
|
-
|
|
7155
|
-
#if FEATURE_SOBOL
|
|
7830
|
+
#if RANDOM_TYPE == 2 // Stratified List
|
|
7156
7831
|
|
|
7832
|
+
${ stratifiedTextureGLSL }
|
|
7833
|
+
|
|
7834
|
+
#elif RANDOM_TYPE == 1 // Sobol
|
|
7835
|
+
|
|
7836
|
+
${ pcgGLSL }
|
|
7157
7837
|
${ sobolCommonGLSL }
|
|
7158
7838
|
${ sobolSamplingGLSL }
|
|
7159
7839
|
|
|
7160
|
-
|
|
7840
|
+
#define rand(v) sobol(v)
|
|
7841
|
+
#define rand2(v) sobol2(v)
|
|
7842
|
+
#define rand3(v) sobol3(v)
|
|
7843
|
+
#define rand4(v) sobol4(v)
|
|
7844
|
+
|
|
7845
|
+
#else // PCG
|
|
7846
|
+
|
|
7847
|
+
${ pcgGLSL }
|
|
7161
7848
|
|
|
7162
7849
|
// Using the sobol functions seems to break the the compiler on MacOS
|
|
7163
7850
|
// - specifically the "sobolReverseBits" function.
|
|
7164
7851
|
uint sobolPixelIndex = 0u;
|
|
7165
7852
|
uint sobolPathIndex = 0u;
|
|
7166
7853
|
uint sobolBounceIndex = 0u;
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7854
|
+
|
|
7855
|
+
#define rand(v) pcgRand()
|
|
7856
|
+
#define rand2(v) pcgRand2()
|
|
7857
|
+
#define rand3(v) pcgRand3()
|
|
7858
|
+
#define rand4(v) pcgRand4()
|
|
7171
7859
|
|
|
7172
7860
|
#endif
|
|
7173
7861
|
|
|
@@ -7285,7 +7973,10 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7285
7973
|
// inverse environment rotation
|
|
7286
7974
|
envRotation3x3 = mat3( environmentRotation );
|
|
7287
7975
|
invEnvRotation3x3 = inverse( envRotation3x3 );
|
|
7288
|
-
lightsDenom =
|
|
7976
|
+
lightsDenom =
|
|
7977
|
+
( environmentIntensity == 0.0 || envMapInfo.totalSum == 0.0 ) && lights.count != 0u ?
|
|
7978
|
+
float( lights.count ) :
|
|
7979
|
+
float( lights.count + 1u );
|
|
7289
7980
|
|
|
7290
7981
|
// final color
|
|
7291
7982
|
gl_FragColor = vec4( 0, 0, 0, 1 );
|
|
@@ -7354,7 +8045,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7354
8045
|
|
|
7355
8046
|
if ( state.firstRay || state.transmissiveRay ) {
|
|
7356
8047
|
|
|
7357
|
-
gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction,
|
|
8048
|
+
gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction, rand2( 2 ) ) * state.throughputColor;
|
|
7358
8049
|
gl_FragColor.a = backgroundAlpha;
|
|
7359
8050
|
|
|
7360
8051
|
} else {
|
|
@@ -7445,7 +8136,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7445
8136
|
}
|
|
7446
8137
|
|
|
7447
8138
|
scatterRec = bsdfSample( - ray.direction, surf );
|
|
7448
|
-
state.isShadowRay = scatterRec.specularPdf <
|
|
8139
|
+
state.isShadowRay = scatterRec.specularPdf < rand( 4 );
|
|
7449
8140
|
|
|
7450
8141
|
bool isBelowSurface = ! surf.volumeParticle && dot( scatterRec.direction, surf.faceNormal ) < 0.0;
|
|
7451
8142
|
vec3 hitPoint = stepRayOrigin( ray.origin, ray.direction, isBelowSurface ? - surf.faceNormal : surf.faceNormal, surfaceHit.dist );
|
|
@@ -7515,7 +8206,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7515
8206
|
rrProb = sqrt( rrProb );
|
|
7516
8207
|
rrProb = max( rrProb, depthProb );
|
|
7517
8208
|
rrProb = min( rrProb, 1.0 );
|
|
7518
|
-
if (
|
|
8209
|
+
if ( rand( 8 ) > rrProb ) {
|
|
7519
8210
|
|
|
7520
8211
|
break;
|
|
7521
8212
|
|
|
@@ -7920,5 +8611,5 @@ class CompatibilityDetector {
|
|
|
7920
8611
|
|
|
7921
8612
|
// core
|
|
7922
8613
|
|
|
7923
|
-
export { BlurredEnvMapGenerator, CompatibilityDetector, DenoiseMaterial, DynamicPathTracingSceneGenerator, EquirectCamera, EquirectHdrInfoUniform, FogVolumeMaterial, GradientEquirectTexture, GradientMapMaterial, GraphMaterial, IESLoader, IESProfilesTexture, LightsInfoUniformStruct, MaterialBase, MaterialReducer, MaterialsTexture, PathTracingRenderer, PathTracingSceneGenerator, PhysicalCamera, PhysicalCameraUniform, PhysicalPathTracingMaterial, PhysicalSpotLight, ProceduralEquirectTexture, QuiltPathTracingRenderer, RenderTarget2DArray, ShapedAreaLight,
|
|
8614
|
+
export { BlurredEnvMapGenerator, CompatibilityDetector, DenoiseMaterial, DynamicPathTracingSceneGenerator, EquirectCamera, EquirectHdrInfoUniform, FogVolumeMaterial, GradientEquirectTexture, GradientMapMaterial, GraphMaterial, IESLoader, IESProfilesTexture, LightsInfoUniformStruct, MaterialBase, MaterialReducer, MaterialsTexture, PathTracingRenderer, PathTracingSceneGenerator, PhysicalCamera, PhysicalCameraUniform, PhysicalPathTracingMaterial, PhysicalSpotLight, ProceduralEquirectTexture, QuiltPathTracingRenderer, RenderTarget2DArray, ShapedAreaLight, getDummyMesh, getGroupMaterialIndicesAttribute, setCommonAttributes };
|
|
7924
8615
|
//# sourceMappingURL=index.module.js.map
|