three-gpu-pathtracer 0.0.16 → 0.0.18
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/README.md +3 -1
- package/build/index.module.js +1202 -471
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +1200 -470
- package/build/index.umd.cjs.map +1 -1
- package/package.json +5 -6
- package/src/core/DynamicPathTracingSceneGenerator.js +80 -35
- package/src/core/PathTracingRenderer.js +36 -38
- package/src/core/PathTracingSceneGenerator.js +11 -55
- package/src/materials/debug/GraphMaterial.js +1 -1
- package/src/materials/fullscreen/AlphaDisplayMaterial.js +1 -1
- package/src/materials/fullscreen/DenoiseMaterial.js +1 -1
- package/src/materials/fullscreen/GradientMapMaterial.js +1 -1
- package/src/materials/pathtracing/LambertPathTracingMaterial.js +5 -4
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +87 -50
- package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +21 -20
- package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
- package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +12 -8
- package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +3 -2
- package/src/materials/pathtracing/glsl/traceScene.glsl.js +11 -13
- package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
- package/src/shader/bsdf/bsdfSampling.glsl.js +21 -17
- package/src/shader/common/bvhAnyHit.glsl.js +2 -2
- package/src/shader/common/fresnel.glsl.js +15 -9
- package/src/shader/common/intersectShapes.glsl.js +2 -2
- package/src/shader/rand/pcg.glsl.js +4 -4
- package/src/shader/rand/sobol.glsl.js +3 -3
- package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
- package/src/shader/sampling/equirectSampling.glsl.js +10 -10
- package/src/shader/sampling/lightSampling.glsl.js +30 -37
- package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
- package/src/shader/structs/lightsStruct.glsl.js +15 -6
- package/src/shader/structs/materialStruct.glsl.js +16 -15
- package/src/textures/BlueNoiseTexture.js +87 -0
- package/src/textures/ProceduralEquirectTexture.js +6 -6
- 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 +59 -21
- package/src/uniforms/IESProfilesTexture.js +2 -2
- package/src/uniforms/LightsInfoUniformStruct.js +15 -9
- package/src/uniforms/MaterialsTexture.js +3 -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/BlurredEnvMapGenerator.js +4 -4
- package/src/utils/GeometryPreparationUtils.js +8 -95
- package/src/utils/IESLoader.js +7 -5
- package/src/utils/TextureUtils.js +15 -0
- 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, 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
|
|
|
@@ -339,9 +339,9 @@ const sobolSamplingGLSL = /* glsl */`
|
|
|
339
339
|
|
|
340
340
|
// Seeds
|
|
341
341
|
uniform sampler2D sobolTexture;
|
|
342
|
-
uint sobolPixelIndex;
|
|
343
|
-
uint sobolPathIndex;
|
|
344
|
-
uint sobolBounceIndex;
|
|
342
|
+
uint sobolPixelIndex = 0u;
|
|
343
|
+
uint sobolPathIndex = 0u;
|
|
344
|
+
uint sobolBounceIndex = 0u;
|
|
345
345
|
|
|
346
346
|
uint sobolGetSeed( uint bounce, uint effect ) {
|
|
347
347
|
|
|
@@ -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
|
|
|
@@ -703,18 +697,22 @@ class PathTracingRenderer {
|
|
|
703
697
|
this._currentTile = 0;
|
|
704
698
|
|
|
705
699
|
this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
|
|
700
|
+
|
|
701
|
+
// will be null if extension not supported
|
|
702
|
+
const floatLinearExtensionSupported = renderer.extensions.get( 'OES_texture_float_linear' );
|
|
703
|
+
|
|
706
704
|
this._primaryTarget = new WebGLRenderTarget( 1, 1, {
|
|
707
705
|
format: RGBAFormat,
|
|
708
|
-
type: FloatType,
|
|
706
|
+
type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
|
|
709
707
|
} );
|
|
710
708
|
this._blendTargets = [
|
|
711
709
|
new WebGLRenderTarget( 1, 1, {
|
|
712
710
|
format: RGBAFormat,
|
|
713
|
-
type: FloatType,
|
|
711
|
+
type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
|
|
714
712
|
} ),
|
|
715
713
|
new WebGLRenderTarget( 1, 1, {
|
|
716
714
|
format: RGBAFormat,
|
|
717
|
-
type: FloatType,
|
|
715
|
+
type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
|
|
718
716
|
} ),
|
|
719
717
|
];
|
|
720
718
|
|
|
@@ -1073,25 +1071,6 @@ function getGroupMaterialIndicesAttribute( geometry, materials, allMaterials ) {
|
|
|
1073
1071
|
|
|
1074
1072
|
}
|
|
1075
1073
|
|
|
1076
|
-
function trimToAttributes( geometry, attributes ) {
|
|
1077
|
-
|
|
1078
|
-
// trim any unneeded attributes
|
|
1079
|
-
if ( attributes ) {
|
|
1080
|
-
|
|
1081
|
-
for ( const key in geometry.attributes ) {
|
|
1082
|
-
|
|
1083
|
-
if ( ! attributes.includes( key ) ) {
|
|
1084
|
-
|
|
1085
|
-
geometry.deleteAttribute( key );
|
|
1086
|
-
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
1074
|
function setCommonAttributes( geometry, options ) {
|
|
1096
1075
|
|
|
1097
1076
|
const { attributes = [], normalMapRequired = false } = options;
|
|
@@ -1109,6 +1088,13 @@ function setCommonAttributes( geometry, options ) {
|
|
|
1109
1088
|
|
|
1110
1089
|
}
|
|
1111
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
|
+
|
|
1112
1098
|
if ( ! geometry.attributes.tangent && ( attributes && attributes.includes( 'tangent' ) ) ) {
|
|
1113
1099
|
|
|
1114
1100
|
if ( normalMapRequired ) {
|
|
@@ -1157,158 +1143,46 @@ function setCommonAttributes( geometry, options ) {
|
|
|
1157
1143
|
|
|
1158
1144
|
}
|
|
1159
1145
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
options = { attributes: null, cloneGeometry: true, ...options };
|
|
1163
|
-
|
|
1164
|
-
const transformedGeometry = [];
|
|
1165
|
-
const materialSet = new Set();
|
|
1166
|
-
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
|
1167
|
-
|
|
1168
|
-
// save any materials
|
|
1169
|
-
const mesh = meshes[ i ];
|
|
1170
|
-
if ( mesh.visible === false ) continue;
|
|
1171
|
-
|
|
1172
|
-
if ( Array.isArray( mesh.material ) ) {
|
|
1173
|
-
|
|
1174
|
-
mesh.material.forEach( m => materialSet.add( m ) );
|
|
1175
|
-
|
|
1176
|
-
} else {
|
|
1177
|
-
|
|
1178
|
-
materialSet.add( mesh.material );
|
|
1179
|
-
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
const materials = Array.from( materialSet );
|
|
1185
|
-
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
|
1186
|
-
|
|
1187
|
-
// ensure the matrix world is up to date
|
|
1188
|
-
const mesh = meshes[ i ];
|
|
1189
|
-
if ( mesh.visible === false ) continue;
|
|
1146
|
+
const dummyMaterial = new MeshBasicMaterial();
|
|
1147
|
+
function getDummyMesh() {
|
|
1190
1148
|
|
|
1191
|
-
|
|
1149
|
+
const emptyGeometry = new BufferGeometry();
|
|
1150
|
+
emptyGeometry.setAttribute( 'position', new BufferAttribute( new Float32Array( 9 ), 3 ) );
|
|
1151
|
+
return new Mesh( emptyGeometry, dummyMaterial );
|
|
1192
1152
|
|
|
1193
|
-
|
|
1194
|
-
const originalGeometry = meshes[ i ].geometry;
|
|
1195
|
-
const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
|
|
1196
|
-
geometry.applyMatrix4( mesh.matrixWorld );
|
|
1153
|
+
}
|
|
1197
1154
|
|
|
1198
|
-
|
|
1199
|
-
setCommonAttributes( geometry, {
|
|
1200
|
-
attributes: options.attributes,
|
|
1201
|
-
normalMapRequired: ! ! mesh.material.normalMap,
|
|
1202
|
-
} );
|
|
1203
|
-
trimToAttributes( geometry, options.attributes );
|
|
1155
|
+
class DynamicPathTracingSceneGenerator {
|
|
1204
1156
|
|
|
1205
|
-
|
|
1206
|
-
const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, mesh.material, materials );
|
|
1207
|
-
geometry.setAttribute( 'materialIndex', materialIndexAttribute );
|
|
1157
|
+
get initialized() {
|
|
1208
1158
|
|
|
1209
|
-
|
|
1159
|
+
return Boolean( this.bvh );
|
|
1210
1160
|
|
|
1211
1161
|
}
|
|
1212
1162
|
|
|
1213
|
-
|
|
1214
|
-
materials.forEach( material => {
|
|
1215
|
-
|
|
1216
|
-
for ( const key in material ) {
|
|
1217
|
-
|
|
1218
|
-
const value = material[ key ];
|
|
1219
|
-
if ( value && value.isTexture ) {
|
|
1163
|
+
constructor( objects ) {
|
|
1220
1164
|
|
|
1221
|
-
|
|
1165
|
+
// ensure the objects is an array
|
|
1166
|
+
if ( ! Array.isArray( objects ) ) {
|
|
1222
1167
|
|
|
1223
|
-
|
|
1168
|
+
objects = [ objects ];
|
|
1224
1169
|
|
|
1225
1170
|
}
|
|
1226
1171
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
const textures = Array.from( textureSet );
|
|
1231
|
-
return { geometry, materials, textures };
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
class PathTracingSceneGenerator {
|
|
1236
|
-
|
|
1237
|
-
prepScene( scene ) {
|
|
1238
|
-
|
|
1239
|
-
scene = Array.isArray( scene ) ? scene : [ scene ];
|
|
1172
|
+
// use a dummy object for a fallback
|
|
1173
|
+
const finalObjects = [ ...objects ];
|
|
1174
|
+
if ( finalObjects.length === 0 ) {
|
|
1240
1175
|
|
|
1241
|
-
|
|
1242
|
-
const lights = [];
|
|
1243
|
-
|
|
1244
|
-
for ( let i = 0, l = scene.length; i < l; i ++ ) {
|
|
1245
|
-
|
|
1246
|
-
scene[ i ].traverseVisible( c => {
|
|
1247
|
-
|
|
1248
|
-
if ( c.isSkinnedMesh || c.isMesh && c.morphTargetInfluences ) {
|
|
1249
|
-
|
|
1250
|
-
const generator = new StaticGeometryGenerator( c );
|
|
1251
|
-
generator.attributes = [ 'position', 'color', 'normal', 'tangent', 'uv', 'uv2' ];
|
|
1252
|
-
generator.applyWorldTransforms = false;
|
|
1253
|
-
const mesh = new Mesh(
|
|
1254
|
-
generator.generate(),
|
|
1255
|
-
c.material,
|
|
1256
|
-
);
|
|
1257
|
-
mesh.matrixWorld.copy( c.matrixWorld );
|
|
1258
|
-
mesh.matrix.copy( c.matrixWorld );
|
|
1259
|
-
mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
|
|
1260
|
-
meshes.push( mesh );
|
|
1261
|
-
|
|
1262
|
-
} else if ( c.isMesh ) {
|
|
1263
|
-
|
|
1264
|
-
meshes.push( c );
|
|
1265
|
-
|
|
1266
|
-
} else if ( c.isRectAreaLight || c.isSpotLight || c.isDirectionalLight || c.isPointLight ) {
|
|
1267
|
-
|
|
1268
|
-
lights.push( c );
|
|
1269
|
-
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
} );
|
|
1176
|
+
finalObjects.push( getDummyMesh() );
|
|
1273
1177
|
|
|
1274
1178
|
}
|
|
1275
1179
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
} ),
|
|
1280
|
-
lights,
|
|
1281
|
-
};
|
|
1282
|
-
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
generate( scene, options = {} ) {
|
|
1286
|
-
|
|
1287
|
-
const { materials, textures, geometry, lights } = this.prepScene( scene );
|
|
1288
|
-
const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
|
|
1289
|
-
return {
|
|
1290
|
-
scene,
|
|
1291
|
-
materials,
|
|
1292
|
-
textures,
|
|
1293
|
-
lights,
|
|
1294
|
-
bvh: new MeshBVH( geometry, bvhOptions ),
|
|
1295
|
-
};
|
|
1296
|
-
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
class DynamicPathTracingSceneGenerator {
|
|
1302
|
-
|
|
1303
|
-
get initialized() {
|
|
1304
|
-
|
|
1305
|
-
return Boolean( this.bvh );
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
constructor( scene ) {
|
|
1180
|
+
// options
|
|
1181
|
+
this.bvhOptions = {};
|
|
1182
|
+
this.attributes = [ 'position', 'normal', 'tangent', 'color', 'uv', 'uv2' ];
|
|
1310
1183
|
|
|
1311
|
-
|
|
1184
|
+
// state
|
|
1185
|
+
this.objects = finalObjects;
|
|
1312
1186
|
this.bvh = null;
|
|
1313
1187
|
this.geometry = new BufferGeometry();
|
|
1314
1188
|
this.materials = null;
|
|
@@ -1332,59 +1206,75 @@ class DynamicPathTracingSceneGenerator {
|
|
|
1332
1206
|
|
|
1333
1207
|
dispose() {}
|
|
1334
1208
|
|
|
1335
|
-
|
|
1209
|
+
prepScene() {
|
|
1336
1210
|
|
|
1337
|
-
|
|
1338
|
-
if ( this.bvh === null ) {
|
|
1211
|
+
if ( this.bvh !== null ) {
|
|
1339
1212
|
|
|
1340
|
-
|
|
1213
|
+
return;
|
|
1341
1214
|
|
|
1342
|
-
|
|
1215
|
+
}
|
|
1343
1216
|
|
|
1344
|
-
|
|
1217
|
+
const { objects, staticGeometryGenerator, geometry, lights, attributes } = this;
|
|
1218
|
+
for ( let i = 0, l = objects.length; i < l; i ++ ) {
|
|
1345
1219
|
|
|
1346
|
-
|
|
1220
|
+
objects[ i ].traverse( c => {
|
|
1347
1221
|
|
|
1348
|
-
|
|
1349
|
-
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
1222
|
+
if ( c.isMesh ) {
|
|
1350
1223
|
|
|
1351
|
-
|
|
1224
|
+
const normalMapRequired = ! ! c.material.normalMap;
|
|
1225
|
+
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
1352
1226
|
|
|
1353
|
-
|
|
1227
|
+
} else if (
|
|
1228
|
+
c.isRectAreaLight ||
|
|
1229
|
+
c.isSpotLight ||
|
|
1230
|
+
c.isPointLight ||
|
|
1231
|
+
c.isDirectionalLight
|
|
1232
|
+
) {
|
|
1354
1233
|
|
|
1355
|
-
|
|
1234
|
+
lights.push( c );
|
|
1356
1235
|
|
|
1357
|
-
}
|
|
1236
|
+
}
|
|
1358
1237
|
|
|
1359
|
-
}
|
|
1238
|
+
} );
|
|
1360
1239
|
|
|
1361
|
-
|
|
1362
|
-
const materials = staticGeometryGenerator.getMaterials();
|
|
1363
|
-
materials.forEach( material => {
|
|
1240
|
+
}
|
|
1364
1241
|
|
|
1365
|
-
|
|
1242
|
+
const textureSet = new Set();
|
|
1243
|
+
const materials = staticGeometryGenerator.getMaterials();
|
|
1244
|
+
materials.forEach( material => {
|
|
1366
1245
|
|
|
1367
|
-
|
|
1368
|
-
if ( value && value.isTexture ) {
|
|
1246
|
+
for ( const key in material ) {
|
|
1369
1247
|
|
|
1370
|
-
|
|
1248
|
+
const value = material[ key ];
|
|
1249
|
+
if ( value && value.isTexture ) {
|
|
1371
1250
|
|
|
1372
|
-
|
|
1251
|
+
textureSet.add( value );
|
|
1373
1252
|
|
|
1374
1253
|
}
|
|
1375
1254
|
|
|
1376
|
-
}
|
|
1255
|
+
}
|
|
1377
1256
|
|
|
1378
|
-
|
|
1379
|
-
|
|
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();
|
|
1380
1265
|
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
geometry.clearGroups();
|
|
1266
|
+
this.materials = materials;
|
|
1267
|
+
this.textures = Array.from( textureSet );
|
|
1384
1268
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
generate() {
|
|
1272
|
+
|
|
1273
|
+
const { objects, staticGeometryGenerator, geometry, bvhOptions } = this;
|
|
1274
|
+
if ( this.bvh === null ) {
|
|
1275
|
+
|
|
1276
|
+
this.prepScene();
|
|
1277
|
+
this.bvh = new MeshBVH( geometry, { strategy: SAH, maxLeafTris: 1, ...bvhOptions } );
|
|
1388
1278
|
|
|
1389
1279
|
return {
|
|
1390
1280
|
lights: this.lights,
|
|
@@ -1414,6 +1304,30 @@ class DynamicPathTracingSceneGenerator {
|
|
|
1414
1304
|
|
|
1415
1305
|
}
|
|
1416
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
|
+
|
|
1417
1331
|
// https://github.com/gkjohnson/webxr-sandbox/blob/main/skinned-mesh-batching/src/MaterialReducer.js
|
|
1418
1332
|
|
|
1419
1333
|
function isTypedArray( arr ) {
|
|
@@ -1775,7 +1689,7 @@ const _polar = new Spherical();
|
|
|
1775
1689
|
const _color = new Color();
|
|
1776
1690
|
class ProceduralEquirectTexture extends DataTexture {
|
|
1777
1691
|
|
|
1778
|
-
constructor( width, height ) {
|
|
1692
|
+
constructor( width = 512, height = 512 ) {
|
|
1779
1693
|
|
|
1780
1694
|
super(
|
|
1781
1695
|
new Float32Array( width * height * 4 ),
|
|
@@ -1811,10 +1725,10 @@ class ProceduralEquirectTexture extends DataTexture {
|
|
|
1811
1725
|
|
|
1812
1726
|
const i = y * width + x;
|
|
1813
1727
|
const i4 = 4 * i;
|
|
1814
|
-
data[ i4 + 0 ] = _color.r;
|
|
1815
|
-
data[ i4 + 1 ] = _color.g;
|
|
1816
|
-
data[ i4 + 2 ] = _color.b;
|
|
1817
|
-
data[ i4 + 3 ] = 1.0;
|
|
1728
|
+
data[ i4 + 0 ] = ( _color.r );
|
|
1729
|
+
data[ i4 + 1 ] = ( _color.g );
|
|
1730
|
+
data[ i4 + 2 ] = ( _color.b );
|
|
1731
|
+
data[ i4 + 3 ] = ( 1.0 );
|
|
1818
1732
|
|
|
1819
1733
|
}
|
|
1820
1734
|
|
|
@@ -1869,7 +1783,7 @@ class GradientEquirectTexture extends ProceduralEquirectTexture {
|
|
|
1869
1783
|
// when rendering each texture to the texture array they must have a consistent color space.
|
|
1870
1784
|
function getTextureHash( t ) {
|
|
1871
1785
|
|
|
1872
|
-
return `${ t.source.uuid }:${ t.
|
|
1786
|
+
return `${ t.source.uuid }:${ t.colorSpace }`;
|
|
1873
1787
|
|
|
1874
1788
|
}
|
|
1875
1789
|
|
|
@@ -1948,6 +1862,8 @@ class MaterialsTexture extends DataTexture {
|
|
|
1948
1862
|
this.type = FloatType;
|
|
1949
1863
|
this.wrapS = ClampToEdgeWrapping;
|
|
1950
1864
|
this.wrapT = ClampToEdgeWrapping;
|
|
1865
|
+
this.minFilter = NearestFilter;
|
|
1866
|
+
this.magFilter = NearestFilter;
|
|
1951
1867
|
this.generateMipmaps = false;
|
|
1952
1868
|
this.threeCompatibilityTransforms = false;
|
|
1953
1869
|
this.features = new MaterialFeatures();
|
|
@@ -2417,7 +2333,7 @@ class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
2417
2333
|
|
|
2418
2334
|
};
|
|
2419
2335
|
|
|
2420
|
-
const fsQuad = new FullScreenQuad( new
|
|
2336
|
+
const fsQuad = new FullScreenQuad( new CopyMaterial() );
|
|
2421
2337
|
this.fsQuad = fsQuad;
|
|
2422
2338
|
|
|
2423
2339
|
}
|
|
@@ -2452,7 +2368,6 @@ class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
2452
2368
|
texture.matrix.identity();
|
|
2453
2369
|
|
|
2454
2370
|
fsQuad.material.map = texture;
|
|
2455
|
-
fsQuad.material.transparent = true;
|
|
2456
2371
|
|
|
2457
2372
|
renderer.setRenderTarget( this, i );
|
|
2458
2373
|
fsQuad.render( renderer );
|
|
@@ -2482,6 +2397,67 @@ class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
2482
2397
|
|
|
2483
2398
|
}
|
|
2484
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
|
+
|
|
2448
|
+
function toHalfFloatArray( f32Array ) {
|
|
2449
|
+
|
|
2450
|
+
const f16Array = new Uint16Array( f32Array.length );
|
|
2451
|
+
for ( let i = 0, n = f32Array.length; i < n; ++ i ) {
|
|
2452
|
+
|
|
2453
|
+
f16Array[ i ] = DataUtils.toHalfFloat( f32Array[ i ] );
|
|
2454
|
+
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
return f16Array;
|
|
2458
|
+
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2485
2461
|
function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) {
|
|
2486
2462
|
|
|
2487
2463
|
let lower = offset;
|
|
@@ -2520,7 +2496,7 @@ function colorToLuminance( r, g, b ) {
|
|
|
2520
2496
|
}
|
|
2521
2497
|
|
|
2522
2498
|
// ensures the data is all floating point values and flipY is false
|
|
2523
|
-
function preprocessEnvMap( envMap ) {
|
|
2499
|
+
function preprocessEnvMap( envMap, targetType = HalfFloatType ) {
|
|
2524
2500
|
|
|
2525
2501
|
const map = envMap.clone();
|
|
2526
2502
|
map.source = new Source( { ...map.image } );
|
|
@@ -2529,17 +2505,54 @@ function preprocessEnvMap( envMap ) {
|
|
|
2529
2505
|
// TODO: is there a simple way to avoid cloning and adjusting the env map data here?
|
|
2530
2506
|
// convert the data from half float uint 16 arrays to float arrays for cdf computation
|
|
2531
2507
|
let newData = data;
|
|
2532
|
-
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;
|
|
2524
|
+
|
|
2525
|
+
} else {
|
|
2526
|
+
|
|
2527
|
+
maxIntValue = 2 ** ( 8 * data.BYTES_PER_ELEMENT ) - 1;
|
|
2528
|
+
|
|
2529
|
+
}
|
|
2533
2530
|
|
|
2534
|
-
|
|
2535
|
-
for ( const i in data ) {
|
|
2531
|
+
for ( let i = 0, l = data.length; i < l; i ++ ) {
|
|
2536
2532
|
|
|
2537
|
-
|
|
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 ) {
|
|
2547
|
+
|
|
2548
|
+
newData[ i ] = DataUtils.toHalfFloat( v );
|
|
2549
|
+
|
|
2550
|
+
}
|
|
2538
2551
|
|
|
2539
2552
|
}
|
|
2540
2553
|
|
|
2541
2554
|
map.image.data = newData;
|
|
2542
|
-
map.type =
|
|
2555
|
+
map.type = targetType;
|
|
2543
2556
|
|
|
2544
2557
|
}
|
|
2545
2558
|
|
|
@@ -2580,8 +2593,8 @@ class EquirectHdrInfoUniform {
|
|
|
2580
2593
|
|
|
2581
2594
|
// Default to a white texture and associated weights so we don't
|
|
2582
2595
|
// just render black initially.
|
|
2583
|
-
const whiteTex = new DataTexture( new Float32Array( [ 1, 1, 1, 1 ] ), 1, 1 );
|
|
2584
|
-
whiteTex.type =
|
|
2596
|
+
const whiteTex = new DataTexture( toHalfFloatArray( new Float32Array( [ 1, 1, 1, 1 ] ) ), 1, 1 );
|
|
2597
|
+
whiteTex.type = HalfFloatType;
|
|
2585
2598
|
whiteTex.format = RGBAFormat;
|
|
2586
2599
|
whiteTex.minFilter = LinearFilter;
|
|
2587
2600
|
whiteTex.magFilter = LinearFilter;
|
|
@@ -2592,8 +2605,8 @@ class EquirectHdrInfoUniform {
|
|
|
2592
2605
|
|
|
2593
2606
|
// Stores a map of [0, 1] value -> cumulative importance row & pdf
|
|
2594
2607
|
// used to sampling a random value to a relevant row to sample from
|
|
2595
|
-
const marginalWeights = new DataTexture( new Float32Array( [ 0, 1 ] ), 1, 2 );
|
|
2596
|
-
marginalWeights.type =
|
|
2608
|
+
const marginalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 1 ] ) ), 1, 2 );
|
|
2609
|
+
marginalWeights.type = HalfFloatType;
|
|
2597
2610
|
marginalWeights.format = RedFormat;
|
|
2598
2611
|
marginalWeights.minFilter = LinearFilter;
|
|
2599
2612
|
marginalWeights.magFilter = LinearFilter;
|
|
@@ -2602,8 +2615,8 @@ class EquirectHdrInfoUniform {
|
|
|
2602
2615
|
|
|
2603
2616
|
// Stores a map of [0, 1] value -> cumulative importance column & pdf
|
|
2604
2617
|
// used to sampling a random value to a relevant pixel to sample from
|
|
2605
|
-
const conditionalWeights = new DataTexture( new Float32Array( [ 0, 0, 1, 1 ] ), 2, 2 );
|
|
2606
|
-
conditionalWeights.type =
|
|
2618
|
+
const conditionalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 0, 1, 1 ] ) ), 2, 2 );
|
|
2619
|
+
conditionalWeights.type = HalfFloatType;
|
|
2607
2620
|
conditionalWeights.format = RedFormat;
|
|
2608
2621
|
conditionalWeights.minFilter = LinearFilter;
|
|
2609
2622
|
conditionalWeights.magFilter = LinearFilter;
|
|
@@ -2631,7 +2644,7 @@ class EquirectHdrInfoUniform {
|
|
|
2631
2644
|
// https://pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Sampling_Light_Sources#InfiniteAreaLights
|
|
2632
2645
|
const map = preprocessEnvMap( hdr );
|
|
2633
2646
|
map.wrapS = RepeatWrapping;
|
|
2634
|
-
map.wrapT =
|
|
2647
|
+
map.wrapT = ClampToEdgeWrapping;
|
|
2635
2648
|
|
|
2636
2649
|
const { width, height, data } = map.image;
|
|
2637
2650
|
|
|
@@ -2653,9 +2666,9 @@ class EquirectHdrInfoUniform {
|
|
|
2653
2666
|
for ( let x = 0; x < width; x ++ ) {
|
|
2654
2667
|
|
|
2655
2668
|
const i = y * width + x;
|
|
2656
|
-
const r = data[ 4 * i + 0 ];
|
|
2657
|
-
const g = data[ 4 * i + 1 ];
|
|
2658
|
-
const b = data[ 4 * i + 2 ];
|
|
2669
|
+
const r = DataUtils.fromHalfFloat( data[ 4 * i + 0 ] );
|
|
2670
|
+
const g = DataUtils.fromHalfFloat( data[ 4 * i + 1 ] );
|
|
2671
|
+
const b = DataUtils.fromHalfFloat( data[ 4 * i + 2 ] );
|
|
2659
2672
|
|
|
2660
2673
|
// the probability of the pixel being selected in this row is the
|
|
2661
2674
|
// scale of the luminance relative to the rest of the pixels.
|
|
@@ -2707,8 +2720,8 @@ class EquirectHdrInfoUniform {
|
|
|
2707
2720
|
// the marginal and conditional data. These will be used to sample with a random number
|
|
2708
2721
|
// to retrieve a uv value to sample in the environment map.
|
|
2709
2722
|
// These values continually increase so it's okay to interpolate between them.
|
|
2710
|
-
const marginalDataArray = new
|
|
2711
|
-
const conditionalDataArray = new
|
|
2723
|
+
const marginalDataArray = new Uint16Array( height );
|
|
2724
|
+
const conditionalDataArray = new Uint16Array( width * height );
|
|
2712
2725
|
|
|
2713
2726
|
// we add a half texel offset so we're sampling the center of the pixel
|
|
2714
2727
|
for ( let i = 0; i < height; i ++ ) {
|
|
@@ -2716,7 +2729,7 @@ class EquirectHdrInfoUniform {
|
|
|
2716
2729
|
const dist = ( i + 1 ) / height;
|
|
2717
2730
|
const row = binarySearchFindClosestIndexOf( cdfMarginal, dist );
|
|
2718
2731
|
|
|
2719
|
-
marginalDataArray[ i ] = ( row + 0.5 ) / height;
|
|
2732
|
+
marginalDataArray[ i ] = DataUtils.toHalfFloat( ( row + 0.5 ) / height );
|
|
2720
2733
|
|
|
2721
2734
|
}
|
|
2722
2735
|
|
|
@@ -2728,7 +2741,7 @@ class EquirectHdrInfoUniform {
|
|
|
2728
2741
|
const dist = ( x + 1 ) / width;
|
|
2729
2742
|
const col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );
|
|
2730
2743
|
|
|
2731
|
-
conditionalDataArray[ i ] = ( col + 0.5 ) / width;
|
|
2744
|
+
conditionalDataArray[ i ] = DataUtils.toHalfFloat( ( col + 0.5 ) / width );
|
|
2732
2745
|
|
|
2733
2746
|
}
|
|
2734
2747
|
|
|
@@ -2802,6 +2815,8 @@ class LightsInfoUniformStruct {
|
|
|
2802
2815
|
tex.wrapS = ClampToEdgeWrapping;
|
|
2803
2816
|
tex.wrapT = ClampToEdgeWrapping;
|
|
2804
2817
|
tex.generateMipmaps = false;
|
|
2818
|
+
tex.minFilter = NearestFilter;
|
|
2819
|
+
tex.magFilter = NearestFilter;
|
|
2805
2820
|
|
|
2806
2821
|
this.tex = tex;
|
|
2807
2822
|
this.count = 0;
|
|
@@ -2832,7 +2847,7 @@ class LightsInfoUniformStruct {
|
|
|
2832
2847
|
const worldQuaternion = new Quaternion();
|
|
2833
2848
|
const eye = new Vector3();
|
|
2834
2849
|
const target = new Vector3();
|
|
2835
|
-
const up = new Vector3();
|
|
2850
|
+
const up = new Vector3( 0, 1, 0 );
|
|
2836
2851
|
|
|
2837
2852
|
for ( let i = 0, l = lights.length; i < l; i ++ ) {
|
|
2838
2853
|
|
|
@@ -2841,6 +2856,13 @@ class LightsInfoUniformStruct {
|
|
|
2841
2856
|
const baseIndex = i * LIGHT_PIXELS * 4;
|
|
2842
2857
|
let index = 0;
|
|
2843
2858
|
|
|
2859
|
+
// initialize to 0
|
|
2860
|
+
for ( let p = 0; p < LIGHT_PIXELS; p ++ ) {
|
|
2861
|
+
|
|
2862
|
+
floatArray[ baseIndex + p ] = 0;
|
|
2863
|
+
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2844
2866
|
// sample 1
|
|
2845
2867
|
// position
|
|
2846
2868
|
l.getWorldPosition( v );
|
|
@@ -2905,7 +2927,7 @@ class LightsInfoUniformStruct {
|
|
|
2905
2927
|
|
|
2906
2928
|
} else if ( l.isSpotLight ) {
|
|
2907
2929
|
|
|
2908
|
-
const radius = l.radius;
|
|
2930
|
+
const radius = l.radius || 0;
|
|
2909
2931
|
eye.setFromMatrixPosition( l.matrixWorld );
|
|
2910
2932
|
target.setFromMatrixPosition( l.target.matrixWorld );
|
|
2911
2933
|
m.lookAt( eye, target, up );
|
|
@@ -2935,24 +2957,21 @@ class LightsInfoUniformStruct {
|
|
|
2935
2957
|
// radius
|
|
2936
2958
|
floatArray[ baseIndex + ( index ++ ) ] = radius;
|
|
2937
2959
|
|
|
2938
|
-
// near
|
|
2939
|
-
floatArray[ baseIndex + ( index ++ ) ] = l.shadow.camera.near;
|
|
2940
|
-
|
|
2941
2960
|
// decay
|
|
2942
2961
|
floatArray[ baseIndex + ( index ++ ) ] = l.decay;
|
|
2943
2962
|
|
|
2944
2963
|
// distance
|
|
2945
2964
|
floatArray[ baseIndex + ( index ++ ) ] = l.distance;
|
|
2946
2965
|
|
|
2947
|
-
// sample 6
|
|
2948
2966
|
// coneCos
|
|
2949
2967
|
floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle );
|
|
2950
2968
|
|
|
2969
|
+
// sample 6
|
|
2951
2970
|
// penumbraCos
|
|
2952
2971
|
floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle * ( 1 - l.penumbra ) );
|
|
2953
2972
|
|
|
2954
2973
|
// iesProfile
|
|
2955
|
-
floatArray[ baseIndex + ( index ++ ) ] = iesTextures.indexOf( l.iesTexture );
|
|
2974
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.iesTexture ? iesTextures.indexOf( l.iesTexture ) : - 1;
|
|
2956
2975
|
|
|
2957
2976
|
} else if ( l.isPointLight ) {
|
|
2958
2977
|
|
|
@@ -2969,7 +2988,7 @@ class LightsInfoUniformStruct {
|
|
|
2969
2988
|
index += 4;
|
|
2970
2989
|
|
|
2971
2990
|
// sample 5
|
|
2972
|
-
index +=
|
|
2991
|
+
index += 1;
|
|
2973
2992
|
|
|
2974
2993
|
floatArray[ baseIndex + ( index ++ ) ] = l.decay;
|
|
2975
2994
|
floatArray[ baseIndex + ( index ++ ) ] = l.distance;
|
|
@@ -3275,7 +3294,7 @@ class IESLoader extends Loader {
|
|
|
3275
3294
|
loader.setPath( this.path );
|
|
3276
3295
|
loader.setRequestHeader( this.requestHeader );
|
|
3277
3296
|
|
|
3278
|
-
const texture = new DataTexture( null, 360, 180, RedFormat,
|
|
3297
|
+
const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
|
|
3279
3298
|
texture.minFilter = LinearFilter;
|
|
3280
3299
|
texture.magFilter = LinearFilter;
|
|
3281
3300
|
|
|
@@ -3283,7 +3302,7 @@ class IESLoader extends Loader {
|
|
|
3283
3302
|
|
|
3284
3303
|
const iesLamp = new IESLamp( text );
|
|
3285
3304
|
|
|
3286
|
-
texture.image.data = this._getIESValues( iesLamp );
|
|
3305
|
+
texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
|
|
3287
3306
|
texture.needsUpdate = true;
|
|
3288
3307
|
|
|
3289
3308
|
if ( onLoad !== undefined ) {
|
|
@@ -3301,10 +3320,10 @@ class IESLoader extends Loader {
|
|
|
3301
3320
|
parse( text ) {
|
|
3302
3321
|
|
|
3303
3322
|
const iesLamp = new IESLamp( text );
|
|
3304
|
-
const texture = new DataTexture( null, 360, 180, RedFormat,
|
|
3323
|
+
const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
|
|
3305
3324
|
texture.minFilter = LinearFilter;
|
|
3306
3325
|
texture.magFilter = LinearFilter;
|
|
3307
|
-
texture.image.data = this._getIESValues( iesLamp );
|
|
3326
|
+
texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
|
|
3308
3327
|
texture.needsUpdate = true;
|
|
3309
3328
|
|
|
3310
3329
|
return texture;
|
|
@@ -3322,7 +3341,7 @@ class IESProfilesTexture extends WebGLArrayRenderTarget {
|
|
|
3322
3341
|
|
|
3323
3342
|
const tex = this.texture;
|
|
3324
3343
|
tex.format = RGBAFormat;
|
|
3325
|
-
tex.type =
|
|
3344
|
+
tex.type = HalfFloatType;
|
|
3326
3345
|
tex.minFilter = LinearFilter;
|
|
3327
3346
|
tex.magFilter = LinearFilter;
|
|
3328
3347
|
tex.wrapS = ClampToEdgeWrapping;
|
|
@@ -3573,7 +3592,7 @@ class BlurredEnvMapGenerator {
|
|
|
3573
3592
|
this.renderer = renderer;
|
|
3574
3593
|
this.pmremGenerator = new PMREMGenerator( renderer );
|
|
3575
3594
|
this.copyQuad = new FullScreenQuad( new PMREMCopyMaterial() );
|
|
3576
|
-
this.renderTarget = new WebGLRenderTarget( 1, 1, { type:
|
|
3595
|
+
this.renderTarget = new WebGLRenderTarget( 1, 1, { type: HalfFloatType, format: RGBAFormat } );
|
|
3577
3596
|
|
|
3578
3597
|
}
|
|
3579
3598
|
|
|
@@ -3610,10 +3629,10 @@ class BlurredEnvMapGenerator {
|
|
|
3610
3629
|
renderer.autoClear = prevClear;
|
|
3611
3630
|
|
|
3612
3631
|
// read the data back
|
|
3613
|
-
const buffer = new
|
|
3632
|
+
const buffer = new Uint16Array( width * height * 4 );
|
|
3614
3633
|
renderer.readRenderTargetPixels( renderTarget, 0, 0, width, height, buffer );
|
|
3615
3634
|
|
|
3616
|
-
const result = new DataTexture( buffer, width, height, RGBAFormat,
|
|
3635
|
+
const result = new DataTexture( buffer, width, height, RGBAFormat, HalfFloatType );
|
|
3617
3636
|
result.minFilter = texture.minFilter;
|
|
3618
3637
|
result.magFilter = texture.magFilter;
|
|
3619
3638
|
result.wrapS = texture.wrapS;
|
|
@@ -3755,7 +3774,7 @@ class DenoiseMaterial extends MaterialBase {
|
|
|
3755
3774
|
|
|
3756
3775
|
gl_FragColor = smartDeNoise( map, vec2( vUv.x, vUv.y ), sigma, kSigma, threshold );
|
|
3757
3776
|
#include <tonemapping_fragment>
|
|
3758
|
-
#include <
|
|
3777
|
+
#include <colorspace_fragment>
|
|
3759
3778
|
#include <premultiplied_alpha_fragment>
|
|
3760
3779
|
|
|
3761
3780
|
}
|
|
@@ -3838,7 +3857,7 @@ class GradientMapMaterial extends MaterialBase {
|
|
|
3838
3857
|
gl_FragColor.rgb = vec3( mix( minColor, maxColor, t ) );
|
|
3839
3858
|
gl_FragColor.a = 1.0;
|
|
3840
3859
|
|
|
3841
|
-
#include <
|
|
3860
|
+
#include <colorspace_fragment>
|
|
3842
3861
|
|
|
3843
3862
|
}`,
|
|
3844
3863
|
|
|
@@ -4047,7 +4066,7 @@ class GraphMaterial extends MaterialBase {
|
|
|
4047
4066
|
|
|
4048
4067
|
}
|
|
4049
4068
|
|
|
4050
|
-
#include <
|
|
4069
|
+
#include <colorspace_fragment>
|
|
4051
4070
|
|
|
4052
4071
|
}
|
|
4053
4072
|
|
|
@@ -4383,13 +4402,22 @@ const lightsStructGLSL = /* glsl */`
|
|
|
4383
4402
|
vec4 s4 = texelFetch1D( tex, i + 4u );
|
|
4384
4403
|
vec4 s5 = texelFetch1D( tex, i + 5u );
|
|
4385
4404
|
l.radius = s4.r;
|
|
4386
|
-
l.
|
|
4387
|
-
l.
|
|
4388
|
-
l.
|
|
4405
|
+
l.decay = s4.g;
|
|
4406
|
+
l.distance = s4.b;
|
|
4407
|
+
l.coneCos = s4.a;
|
|
4408
|
+
|
|
4409
|
+
l.penumbraCos = s5.r;
|
|
4410
|
+
l.iesProfile = int( round( s5.g ) );
|
|
4389
4411
|
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
l.
|
|
4412
|
+
} else {
|
|
4413
|
+
|
|
4414
|
+
l.radius = 0.0;
|
|
4415
|
+
l.decay = 0.0;
|
|
4416
|
+
l.distance = 0.0;
|
|
4417
|
+
|
|
4418
|
+
l.coneCos = 0.0;
|
|
4419
|
+
l.penumbraCos = 0.0;
|
|
4420
|
+
l.iesProfile = - 1;
|
|
4393
4421
|
|
|
4394
4422
|
}
|
|
4395
4423
|
|
|
@@ -4585,21 +4613,22 @@ const materialStructGLSL = /* glsl */ `
|
|
|
4585
4613
|
|
|
4586
4614
|
uint firstTextureTransformIdx = i + 15u;
|
|
4587
4615
|
|
|
4588
|
-
|
|
4589
|
-
m.
|
|
4590
|
-
m.
|
|
4591
|
-
m.
|
|
4592
|
-
m.
|
|
4593
|
-
m.
|
|
4594
|
-
m.
|
|
4595
|
-
m.
|
|
4596
|
-
m.
|
|
4597
|
-
m.
|
|
4598
|
-
m.
|
|
4599
|
-
m.
|
|
4600
|
-
m.
|
|
4601
|
-
m.
|
|
4602
|
-
m.
|
|
4616
|
+
// mat3( 1.0 ) is an identity matrix
|
|
4617
|
+
m.mapTransform = m.map == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx );
|
|
4618
|
+
m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
|
|
4619
|
+
m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
|
|
4620
|
+
m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
|
|
4621
|
+
m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
|
|
4622
|
+
m.normalMapTransform = m.normalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
|
|
4623
|
+
m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
|
|
4624
|
+
m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
|
|
4625
|
+
m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
|
|
4626
|
+
m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
|
|
4627
|
+
m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
|
|
4628
|
+
m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
|
|
4629
|
+
m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
|
|
4630
|
+
m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
|
|
4631
|
+
m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
|
|
4603
4632
|
|
|
4604
4633
|
return m;
|
|
4605
4634
|
|
|
@@ -4624,9 +4653,9 @@ bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
|
|
|
4624
4653
|
|
|
4625
4654
|
// returns true if we're within the first fog volume we hit
|
|
4626
4655
|
bool bvhIntersectFogVolumeHit(
|
|
4627
|
-
|
|
4656
|
+
vec3 rayOrigin, vec3 rayDirection,
|
|
4628
4657
|
usampler2D materialIndexAttribute, sampler2D materials,
|
|
4629
|
-
|
|
4658
|
+
inout Material material
|
|
4630
4659
|
) {
|
|
4631
4660
|
|
|
4632
4661
|
material.fogVolume = false;
|
|
@@ -5086,7 +5115,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5086
5115
|
${ iridescenceGLSL }
|
|
5087
5116
|
|
|
5088
5117
|
// diffuse
|
|
5089
|
-
float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5118
|
+
float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5090
5119
|
|
|
5091
5120
|
// https://schuttejoe.github.io/post/disneybsdf/
|
|
5092
5121
|
float fl = schlickFresnel( wi.z, 0.0 );
|
|
@@ -5100,15 +5129,17 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5100
5129
|
|
|
5101
5130
|
// TODO: subsurface approx?
|
|
5102
5131
|
|
|
5103
|
-
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 );
|
|
5104
5134
|
color = ( 1.0 - F ) * transFactor * metalFactor * wi.z * surf.color * ( retro + lambert ) / PI;
|
|
5135
|
+
|
|
5105
5136
|
return wi.z / PI;
|
|
5106
5137
|
|
|
5107
5138
|
}
|
|
5108
5139
|
|
|
5109
5140
|
vec3 diffuseDirection( vec3 wo, SurfaceRecord surf ) {
|
|
5110
5141
|
|
|
5111
|
-
vec3 lightDirection = sampleSphere(
|
|
5142
|
+
vec3 lightDirection = sampleSphere( rand2( 11 ) );
|
|
5112
5143
|
lightDirection.z += 1.0;
|
|
5113
5144
|
lightDirection = normalize( lightDirection );
|
|
5114
5145
|
|
|
@@ -5117,7 +5148,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5117
5148
|
}
|
|
5118
5149
|
|
|
5119
5150
|
// specular
|
|
5120
|
-
float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5151
|
+
float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5121
5152
|
|
|
5122
5153
|
// if roughness is set to 0 then D === NaN which results in black pixels
|
|
5123
5154
|
float metalness = surf.metalness;
|
|
@@ -5153,7 +5184,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5153
5184
|
vec3 halfVector = ggxDirection(
|
|
5154
5185
|
wo,
|
|
5155
5186
|
vec2( roughness ),
|
|
5156
|
-
|
|
5187
|
+
rand2( 12 )
|
|
5157
5188
|
);
|
|
5158
5189
|
|
|
5159
5190
|
// apply to new ray by reflecting off the new normal
|
|
@@ -5164,7 +5195,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5164
5195
|
|
|
5165
5196
|
// transmission
|
|
5166
5197
|
/*
|
|
5167
|
-
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5198
|
+
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5168
5199
|
|
|
5169
5200
|
// See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
|
|
5170
5201
|
|
|
@@ -5190,7 +5221,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5190
5221
|
vec3 halfVector = ggxDirection(
|
|
5191
5222
|
wo,
|
|
5192
5223
|
vec2( filteredRoughness ),
|
|
5193
|
-
|
|
5224
|
+
rand2( 13 )
|
|
5194
5225
|
);
|
|
5195
5226
|
|
|
5196
5227
|
vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
|
|
@@ -5207,12 +5238,13 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5207
5238
|
|
|
5208
5239
|
// TODO: This is just using a basic cosine-weighted specular distribution with an
|
|
5209
5240
|
// incorrect PDF value at the moment. Update it to correctly use a GGX distribution
|
|
5210
|
-
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5241
|
+
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5211
5242
|
|
|
5212
5243
|
color = surf.transmission * surf.color;
|
|
5213
5244
|
|
|
5214
5245
|
// PDF
|
|
5215
|
-
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 );
|
|
5216
5248
|
if ( F >= 1.0 ) {
|
|
5217
5249
|
|
|
5218
5250
|
return 0.0;
|
|
@@ -5227,7 +5259,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5227
5259
|
|
|
5228
5260
|
float roughness = surf.filteredRoughness;
|
|
5229
5261
|
float eta = surf.eta;
|
|
5230
|
-
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 );
|
|
5231
5263
|
vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
|
|
5232
5264
|
|
|
5233
5265
|
if ( surf.thinFilm ) {
|
|
@@ -5268,7 +5300,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5268
5300
|
vec3 halfVector = ggxDirection(
|
|
5269
5301
|
wo,
|
|
5270
5302
|
vec2( roughness ),
|
|
5271
|
-
|
|
5303
|
+
rand2( 14 )
|
|
5272
5304
|
);
|
|
5273
5305
|
|
|
5274
5306
|
// apply to new ray by reflecting off the new normal
|
|
@@ -5298,12 +5330,13 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5298
5330
|
// bsdf
|
|
5299
5331
|
void getLobeWeights(
|
|
5300
5332
|
vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRecord surf,
|
|
5301
|
-
|
|
5333
|
+
inout float diffuseWeight, inout float specularWeight, inout float transmissionWeight, inout float clearcoatWeight
|
|
5302
5334
|
) {
|
|
5303
5335
|
|
|
5304
5336
|
float metalness = surf.metalness;
|
|
5305
5337
|
float transmission = surf.transmission;
|
|
5306
|
-
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 );
|
|
5307
5340
|
|
|
5308
5341
|
float transSpecularProb = mix( max( 0.25, fEstimate ), 1.0, metalness );
|
|
5309
5342
|
float diffSpecularProb = 0.5 + 0.5 * metalness;
|
|
@@ -5322,7 +5355,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5322
5355
|
|
|
5323
5356
|
float bsdfEval(
|
|
5324
5357
|
vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf,
|
|
5325
|
-
float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight,
|
|
5358
|
+
float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, inout float specularPdf, inout vec3 color
|
|
5326
5359
|
) {
|
|
5327
5360
|
|
|
5328
5361
|
float metalness = surf.metalness;
|
|
@@ -5385,7 +5418,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5385
5418
|
|
|
5386
5419
|
}
|
|
5387
5420
|
|
|
5388
|
-
float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf,
|
|
5421
|
+
float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, inout vec3 color ) {
|
|
5389
5422
|
|
|
5390
5423
|
if ( surf.volumeParticle ) {
|
|
5391
5424
|
|
|
@@ -5419,7 +5452,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5419
5452
|
ScatterRecord sampleRec;
|
|
5420
5453
|
sampleRec.specularPdf = 0.0;
|
|
5421
5454
|
sampleRec.pdf = 1.0 / ( 4.0 * PI );
|
|
5422
|
-
sampleRec.direction = sampleSphere(
|
|
5455
|
+
sampleRec.direction = sampleSphere( rand2( 16 ) );
|
|
5423
5456
|
sampleRec.color = surf.color / ( 4.0 * PI );
|
|
5424
5457
|
return sampleRec;
|
|
5425
5458
|
|
|
@@ -5471,7 +5504,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5471
5504
|
vec3 wi;
|
|
5472
5505
|
vec3 clearcoatWi;
|
|
5473
5506
|
|
|
5474
|
-
float r =
|
|
5507
|
+
float r = rand( 15 );
|
|
5475
5508
|
if ( r <= cdf[0] ) { // diffuse
|
|
5476
5509
|
|
|
5477
5510
|
wi = diffuseDirection( wo, surf );
|
|
@@ -5553,14 +5586,14 @@ const equirectSamplingGLSL = /* glsl */`
|
|
|
5553
5586
|
}
|
|
5554
5587
|
|
|
5555
5588
|
// samples the color given env map with CDF and returns the pdf of the direction
|
|
5556
|
-
float sampleEquirect(
|
|
5589
|
+
float sampleEquirect( vec3 direction, inout vec3 color ) {
|
|
5557
5590
|
|
|
5558
5591
|
vec2 uv = equirectDirectionToUv( direction );
|
|
5559
|
-
color = texture2D(
|
|
5592
|
+
color = texture2D( envMapInfo.map, uv ).rgb;
|
|
5560
5593
|
|
|
5561
|
-
float totalSum =
|
|
5594
|
+
float totalSum = envMapInfo.totalSum;
|
|
5562
5595
|
float lum = luminance( color );
|
|
5563
|
-
ivec2 resolution = textureSize(
|
|
5596
|
+
ivec2 resolution = textureSize( envMapInfo.map, 0 );
|
|
5564
5597
|
float pdf = lum / totalSum;
|
|
5565
5598
|
|
|
5566
5599
|
return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
|
|
@@ -5568,20 +5601,20 @@ const equirectSamplingGLSL = /* glsl */`
|
|
|
5568
5601
|
}
|
|
5569
5602
|
|
|
5570
5603
|
// samples a direction of the envmap with color and retrieves pdf
|
|
5571
|
-
float sampleEquirectProbability(
|
|
5604
|
+
float sampleEquirectProbability( vec2 r, inout vec3 color, inout vec3 direction ) {
|
|
5572
5605
|
|
|
5573
5606
|
// sample env map cdf
|
|
5574
|
-
float v = texture2D(
|
|
5575
|
-
float u = texture2D(
|
|
5607
|
+
float v = texture2D( envMapInfo.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
5608
|
+
float u = texture2D( envMapInfo.conditionalWeights, vec2( r.y, v ) ).x;
|
|
5576
5609
|
vec2 uv = vec2( u, v );
|
|
5577
5610
|
|
|
5578
5611
|
vec3 derivedDirection = equirectUvToDirection( uv );
|
|
5579
5612
|
direction = derivedDirection;
|
|
5580
|
-
color = texture2D(
|
|
5613
|
+
color = texture2D( envMapInfo.map, uv ).rgb;
|
|
5581
5614
|
|
|
5582
|
-
float totalSum =
|
|
5615
|
+
float totalSum = envMapInfo.totalSum;
|
|
5583
5616
|
float lum = luminance( color );
|
|
5584
|
-
ivec2 resolution = textureSize(
|
|
5617
|
+
ivec2 resolution = textureSize( envMapInfo.map, 0 );
|
|
5585
5618
|
float pdf = lum / totalSum;
|
|
5586
5619
|
|
|
5587
5620
|
return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
|
|
@@ -5634,24 +5667,17 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5634
5667
|
|
|
5635
5668
|
};
|
|
5636
5669
|
|
|
5637
|
-
bool
|
|
5670
|
+
bool intersectLightAtIndex( sampler2D lights, vec3 rayOrigin, vec3 rayDirection, uint l, inout LightRecord lightRec ) {
|
|
5638
5671
|
|
|
5639
5672
|
bool didHit = false;
|
|
5640
|
-
|
|
5641
|
-
for ( l = 0u; l < lightCount; l ++ ) {
|
|
5642
|
-
|
|
5643
|
-
Light light = readLightInfo( lights, l );
|
|
5644
|
-
|
|
5645
|
-
vec3 u = light.u;
|
|
5646
|
-
vec3 v = light.v;
|
|
5647
|
-
|
|
5648
|
-
// check for backface
|
|
5649
|
-
vec3 normal = normalize( cross( u, v ) );
|
|
5650
|
-
if ( dot( normal, rayDirection ) < 0.0 ) {
|
|
5673
|
+
Light light = readLightInfo( lights, l );
|
|
5651
5674
|
|
|
5652
|
-
|
|
5675
|
+
vec3 u = light.u;
|
|
5676
|
+
vec3 v = light.v;
|
|
5653
5677
|
|
|
5654
|
-
|
|
5678
|
+
// check for backface
|
|
5679
|
+
vec3 normal = normalize( cross( u, v ) );
|
|
5680
|
+
if ( dot( normal, rayDirection ) > 0.0 ) {
|
|
5655
5681
|
|
|
5656
5682
|
u *= 1.0 / dot( u, u );
|
|
5657
5683
|
v *= 1.0 / dot( v, v );
|
|
@@ -5664,17 +5690,13 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5664
5690
|
( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
|
|
5665
5691
|
) {
|
|
5666
5692
|
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
lightRec.direction = rayDirection;
|
|
5675
|
-
lightRec.type = light.type;
|
|
5676
|
-
|
|
5677
|
-
}
|
|
5693
|
+
float cosTheta = dot( rayDirection, normal );
|
|
5694
|
+
didHit = true;
|
|
5695
|
+
lightRec.dist = dist;
|
|
5696
|
+
lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
5697
|
+
lightRec.emission = light.color * light.intensity;
|
|
5698
|
+
lightRec.direction = rayDirection;
|
|
5699
|
+
lightRec.type = light.type;
|
|
5678
5700
|
|
|
5679
5701
|
}
|
|
5680
5702
|
|
|
@@ -5686,11 +5708,6 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5686
5708
|
|
|
5687
5709
|
LightRecord randomAreaLightSample( Light light, vec3 rayOrigin, vec2 ruv ) {
|
|
5688
5710
|
|
|
5689
|
-
LightRecord lightRec;
|
|
5690
|
-
lightRec.type = light.type;
|
|
5691
|
-
|
|
5692
|
-
lightRec.emission = light.color * light.intensity;
|
|
5693
|
-
|
|
5694
5711
|
vec3 randomPos;
|
|
5695
5712
|
if( light.type == RECT_AREA_LIGHT_TYPE ) {
|
|
5696
5713
|
|
|
@@ -5711,12 +5728,17 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5711
5728
|
|
|
5712
5729
|
vec3 toLight = randomPos - rayOrigin;
|
|
5713
5730
|
float lightDistSq = dot( toLight, toLight );
|
|
5714
|
-
|
|
5731
|
+
float dist = sqrt( lightDistSq );
|
|
5732
|
+
vec3 direction = toLight / dist;
|
|
5733
|
+
vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
5715
5734
|
|
|
5716
|
-
|
|
5735
|
+
LightRecord lightRec;
|
|
5736
|
+
lightRec.type = light.type;
|
|
5737
|
+
lightRec.emission = light.color * light.intensity;
|
|
5738
|
+
lightRec.dist = dist;
|
|
5717
5739
|
lightRec.direction = direction;
|
|
5718
5740
|
|
|
5719
|
-
|
|
5741
|
+
// TODO: the denominator is potentially zero
|
|
5720
5742
|
lightRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
|
|
5721
5743
|
|
|
5722
5744
|
return lightRec;
|
|
@@ -5764,13 +5786,15 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5764
5786
|
|
|
5765
5787
|
LightRecord randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin, vec3 ruv ) {
|
|
5766
5788
|
|
|
5789
|
+
LightRecord result;
|
|
5790
|
+
|
|
5767
5791
|
// pick a random light
|
|
5768
5792
|
uint l = uint( ruv.x * float( lightCount ) );
|
|
5769
5793
|
Light light = readLightInfo( lights, l );
|
|
5770
5794
|
|
|
5771
5795
|
if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
5772
5796
|
|
|
5773
|
-
|
|
5797
|
+
result = randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
|
|
5774
5798
|
|
|
5775
5799
|
} else if ( light.type == POINT_LIGHT_TYPE ) {
|
|
5776
5800
|
|
|
@@ -5790,7 +5814,7 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5790
5814
|
rec.pdf = 1.0;
|
|
5791
5815
|
rec.emission = light.color * light.intensity * distanceFalloff;
|
|
5792
5816
|
rec.type = light.type;
|
|
5793
|
-
|
|
5817
|
+
result = rec;
|
|
5794
5818
|
|
|
5795
5819
|
} else if ( light.type == DIR_LIGHT_TYPE ) {
|
|
5796
5820
|
|
|
@@ -5801,15 +5825,17 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5801
5825
|
rec.emission = light.color * light.intensity;
|
|
5802
5826
|
rec.type = light.type;
|
|
5803
5827
|
|
|
5804
|
-
|
|
5828
|
+
result = rec;
|
|
5805
5829
|
|
|
5806
5830
|
} else {
|
|
5807
5831
|
|
|
5808
5832
|
// sample the light
|
|
5809
|
-
|
|
5833
|
+
result = randomAreaLightSample( light, rayOrigin, ruv.yz );
|
|
5810
5834
|
|
|
5811
5835
|
}
|
|
5812
5836
|
|
|
5837
|
+
return result;
|
|
5838
|
+
|
|
5813
5839
|
}
|
|
5814
5840
|
|
|
5815
5841
|
`;
|
|
@@ -5906,7 +5932,7 @@ const intersectShapesGLSL = /* glsl */`
|
|
|
5906
5932
|
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
5907
5933
|
// falls in the bounds of the rectangle on that same plane.
|
|
5908
5934
|
// Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
|
|
5909
|
-
bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection,
|
|
5935
|
+
bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
|
|
5910
5936
|
|
|
5911
5937
|
float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
5912
5938
|
|
|
@@ -5937,7 +5963,7 @@ const intersectShapesGLSL = /* glsl */`
|
|
|
5937
5963
|
|
|
5938
5964
|
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
5939
5965
|
// falls in the bounds of the circle on that same plane. See above URL for a description of the plane intersection algorithm.
|
|
5940
|
-
bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection,
|
|
5966
|
+
bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
|
|
5941
5967
|
|
|
5942
5968
|
float t = dot( position - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
5943
5969
|
|
|
@@ -6117,32 +6143,38 @@ const fresnelGLSL = /* glsl */`
|
|
|
6117
6143
|
|
|
6118
6144
|
}
|
|
6119
6145
|
|
|
6120
|
-
|
|
6146
|
+
// TODO: disney fresnel was removed and replaced with this fresnel function to better align with
|
|
6147
|
+
// the glTF but is causing blown out pixels. Should be revisited
|
|
6148
|
+
// float evaluateFresnelWeight( float cosTheta, float eta, float f0 ) {
|
|
6121
6149
|
|
|
6122
|
-
|
|
6150
|
+
// if ( totalInternalReflection( cosTheta, eta ) ) {
|
|
6123
6151
|
|
|
6124
|
-
|
|
6152
|
+
// return 1.0;
|
|
6125
6153
|
|
|
6126
|
-
|
|
6154
|
+
// }
|
|
6127
6155
|
|
|
6128
|
-
|
|
6156
|
+
// return schlickFresnel( cosTheta, f0 );
|
|
6129
6157
|
|
|
6130
|
-
}
|
|
6158
|
+
// }
|
|
6131
6159
|
|
|
6132
|
-
/*
|
|
6133
6160
|
// https://schuttejoe.github.io/post/disneybsdf/
|
|
6134
6161
|
float disneyFresnel( vec3 wo, vec3 wi, vec3 wh, float f0, float eta, float metalness ) {
|
|
6135
6162
|
|
|
6136
6163
|
float dotHV = dot( wo, wh );
|
|
6137
|
-
|
|
6164
|
+
if ( totalInternalReflection( dotHV, eta ) ) {
|
|
6165
|
+
|
|
6166
|
+
return 1.0;
|
|
6138
6167
|
|
|
6168
|
+
}
|
|
6169
|
+
|
|
6170
|
+
float dotHL = dot( wi, wh );
|
|
6139
6171
|
float dielectricFresnel = dielectricFresnel( abs( dotHV ), eta );
|
|
6140
6172
|
float metallicFresnel = schlickFresnel( dotHL, f0 );
|
|
6141
6173
|
|
|
6142
6174
|
return mix( dielectricFresnel, metallicFresnel, metalness );
|
|
6143
6175
|
|
|
6144
6176
|
}
|
|
6145
|
-
|
|
6177
|
+
|
|
6146
6178
|
`;
|
|
6147
6179
|
|
|
6148
6180
|
const arraySamplerTexelFetchGLSL = /*glsl */`
|
|
@@ -6199,28 +6231,28 @@ const pcgGLSL = /* glsl */`
|
|
|
6199
6231
|
}
|
|
6200
6232
|
|
|
6201
6233
|
// returns [ 0, 1 ]
|
|
6202
|
-
float
|
|
6234
|
+
float pcgRand() {
|
|
6203
6235
|
|
|
6204
6236
|
pcg4d( WHITE_NOISE_SEED );
|
|
6205
6237
|
return float( WHITE_NOISE_SEED.x ) / float( 0xffffffffu );
|
|
6206
6238
|
|
|
6207
6239
|
}
|
|
6208
6240
|
|
|
6209
|
-
vec2
|
|
6241
|
+
vec2 pcgRand2() {
|
|
6210
6242
|
|
|
6211
6243
|
pcg4d( WHITE_NOISE_SEED );
|
|
6212
6244
|
return vec2( WHITE_NOISE_SEED.xy ) / float(0xffffffffu);
|
|
6213
6245
|
|
|
6214
6246
|
}
|
|
6215
6247
|
|
|
6216
|
-
vec3
|
|
6248
|
+
vec3 pcgRand3() {
|
|
6217
6249
|
|
|
6218
6250
|
pcg4d( WHITE_NOISE_SEED );
|
|
6219
6251
|
return vec3( WHITE_NOISE_SEED.xyz ) / float( 0xffffffffu );
|
|
6220
6252
|
|
|
6221
6253
|
}
|
|
6222
6254
|
|
|
6223
|
-
vec4
|
|
6255
|
+
vec4 pcgRand4() {
|
|
6224
6256
|
|
|
6225
6257
|
pcg4d( WHITE_NOISE_SEED );
|
|
6226
6258
|
return vec4( WHITE_NOISE_SEED ) / float( 0xffffffffu );
|
|
@@ -6293,7 +6325,7 @@ const cameraUtilsGLSL = /* glsl */`
|
|
|
6293
6325
|
|
|
6294
6326
|
// Jitter the camera ray by finding a uv coordinate at a random sample
|
|
6295
6327
|
// around this pixel's UV coordinate for AA
|
|
6296
|
-
vec2 ruv =
|
|
6328
|
+
vec2 ruv = rand2( 0 );
|
|
6297
6329
|
vec2 jitteredUv = vUv + vec2( tentFilter( ruv.x ) * ssd.x, tentFilter( ruv.y ) * ssd.y );
|
|
6298
6330
|
Ray ray;
|
|
6299
6331
|
|
|
@@ -6338,7 +6370,7 @@ const cameraUtilsGLSL = /* glsl */`
|
|
|
6338
6370
|
|
|
6339
6371
|
// get the aperture sample
|
|
6340
6372
|
// if blades === 0 then we assume a circle
|
|
6341
|
-
vec3 shapeUVW=
|
|
6373
|
+
vec3 shapeUVW= rand3( 1 );
|
|
6342
6374
|
int blades = physicalCamera.apertureBlades;
|
|
6343
6375
|
float anamorphicRatio = physicalCamera.anamorphicRatio;
|
|
6344
6376
|
vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularPolygon( blades, shapeUVW );
|
|
@@ -6369,11 +6401,14 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6369
6401
|
// step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
|
|
6370
6402
|
// returns true if a solid surface was hit
|
|
6371
6403
|
bool attenuateHit(
|
|
6372
|
-
|
|
6404
|
+
RenderState state,
|
|
6373
6405
|
Ray ray, float rayDist,
|
|
6374
6406
|
out vec3 color
|
|
6375
6407
|
) {
|
|
6376
6408
|
|
|
6409
|
+
// store the original bounce index so we can reset it after
|
|
6410
|
+
uint originalBounceIndex = sobolBounceIndex;
|
|
6411
|
+
|
|
6377
6412
|
int traversals = state.traversals;
|
|
6378
6413
|
int transmissiveTraversals = state.transmissiveTraversals;
|
|
6379
6414
|
bool isShadowRay = state.isShadowRay;
|
|
@@ -6383,34 +6418,28 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6383
6418
|
|
|
6384
6419
|
// hit results
|
|
6385
6420
|
SurfaceHit surfaceHit;
|
|
6386
|
-
LightRecord lightRec;
|
|
6387
6421
|
|
|
6388
6422
|
color = vec3( 1.0 );
|
|
6389
6423
|
|
|
6390
|
-
|
|
6391
|
-
// and then reset.
|
|
6424
|
+
bool result = true;
|
|
6392
6425
|
for ( int i = 0; i < traversals; i ++ ) {
|
|
6393
6426
|
|
|
6394
|
-
|
|
6395
|
-
ray, bvh, lights, fogMaterial,
|
|
6396
|
-
surfaceHit, lightRec
|
|
6397
|
-
);
|
|
6398
|
-
|
|
6399
|
-
if ( hitType == FOG_HIT ) {
|
|
6427
|
+
sobolBounceIndex ++;
|
|
6400
6428
|
|
|
6401
|
-
|
|
6429
|
+
int hitType = traceScene( ray, fogMaterial, surfaceHit );
|
|
6402
6430
|
|
|
6403
|
-
|
|
6431
|
+
if ( hitType == FOG_HIT ) {
|
|
6404
6432
|
|
|
6405
|
-
|
|
6406
|
-
|
|
6433
|
+
result = true;
|
|
6434
|
+
break;
|
|
6407
6435
|
|
|
6408
6436
|
} else if ( hitType == SURFACE_HIT ) {
|
|
6409
6437
|
|
|
6410
6438
|
float totalDist = distance( startPoint, ray.origin + ray.direction * surfaceHit.dist );
|
|
6411
6439
|
if ( totalDist > rayDist ) {
|
|
6412
6440
|
|
|
6413
|
-
|
|
6441
|
+
result = false;
|
|
6442
|
+
break;
|
|
6414
6443
|
|
|
6415
6444
|
}
|
|
6416
6445
|
|
|
@@ -6492,7 +6521,7 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6492
6521
|
bool useAlphaTest = alphaTest != 0.0;
|
|
6493
6522
|
float transmissionFactor = ( 1.0 - metalness ) * transmission;
|
|
6494
6523
|
if (
|
|
6495
|
-
transmissionFactor < rand() && ! (
|
|
6524
|
+
transmissionFactor < rand( 9 ) && ! (
|
|
6496
6525
|
// material sidedness
|
|
6497
6526
|
material.side != 0.0 && surfaceHit.side == material.side
|
|
6498
6527
|
|
|
@@ -6500,11 +6529,12 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6500
6529
|
|| useAlphaTest && albedo.a < alphaTest
|
|
6501
6530
|
|
|
6502
6531
|
// opacity
|
|
6503
|
-
|| material.transparent && ! useAlphaTest && albedo.a < rand()
|
|
6532
|
+
|| material.transparent && ! useAlphaTest && albedo.a < rand( 10 )
|
|
6504
6533
|
)
|
|
6505
6534
|
) {
|
|
6506
6535
|
|
|
6507
|
-
|
|
6536
|
+
result = true;
|
|
6537
|
+
break;
|
|
6508
6538
|
|
|
6509
6539
|
}
|
|
6510
6540
|
|
|
@@ -6530,13 +6560,16 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6530
6560
|
|
|
6531
6561
|
} else {
|
|
6532
6562
|
|
|
6533
|
-
|
|
6563
|
+
result = false;
|
|
6564
|
+
break;
|
|
6534
6565
|
|
|
6535
6566
|
}
|
|
6536
6567
|
|
|
6537
6568
|
}
|
|
6538
6569
|
|
|
6539
|
-
|
|
6570
|
+
// reset the bounce index
|
|
6571
|
+
sobolBounceIndex = originalBounceIndex;
|
|
6572
|
+
return result;
|
|
6540
6573
|
|
|
6541
6574
|
}
|
|
6542
6575
|
|
|
@@ -6549,22 +6582,26 @@ const traceSceneGLSL = /* glsl */`
|
|
|
6549
6582
|
#define LIGHT_HIT 2
|
|
6550
6583
|
#define FOG_HIT 3
|
|
6551
6584
|
|
|
6585
|
+
// Passing the global variable 'lights' into this function caused shader program errors.
|
|
6586
|
+
// So global variables like 'lights' and 'bvh' were moved out of the function parameters.
|
|
6587
|
+
// For more information, refer to: https://github.com/gkjohnson/three-gpu-pathtracer/pull/457
|
|
6552
6588
|
int traceScene(
|
|
6553
6589
|
|
|
6554
|
-
Ray ray,
|
|
6555
|
-
out SurfaceHit surfaceHit, out LightRecord lightRec
|
|
6590
|
+
Ray ray, Material fogMaterial, inout SurfaceHit surfaceHit
|
|
6556
6591
|
|
|
6557
6592
|
) {
|
|
6558
6593
|
|
|
6594
|
+
int result = NO_HIT;
|
|
6559
6595
|
bool hit = bvhIntersectFirstHit( bvh, ray.origin, ray.direction, surfaceHit.faceIndices, surfaceHit.faceNormal, surfaceHit.barycoord, surfaceHit.side, surfaceHit.dist );
|
|
6560
|
-
bool lightHit = lightsClosestHit( lights.tex, lights.count, ray.origin, ray.direction, lightRec );
|
|
6561
6596
|
|
|
6562
6597
|
#if FEATURE_FOG
|
|
6563
6598
|
|
|
6564
6599
|
if ( fogMaterial.fogVolume ) {
|
|
6565
6600
|
|
|
6566
|
-
|
|
6567
|
-
|
|
6601
|
+
// offset the distance so we don't run into issues with particles on the same surface
|
|
6602
|
+
// as other objects
|
|
6603
|
+
float particleDist = intersectFogVolume( fogMaterial, rand( 1 ) );
|
|
6604
|
+
if ( particleDist + RAY_OFFSET < surfaceHit.dist ) {
|
|
6568
6605
|
|
|
6569
6606
|
surfaceHit.side = 1.0;
|
|
6570
6607
|
surfaceHit.faceNormal = normalize( - ray.direction );
|
|
@@ -6577,19 +6614,13 @@ const traceSceneGLSL = /* glsl */`
|
|
|
6577
6614
|
|
|
6578
6615
|
#endif
|
|
6579
6616
|
|
|
6580
|
-
if ( lightHit && ( lightRec.dist < surfaceHit.dist || ! hit ) ) {
|
|
6581
|
-
|
|
6582
|
-
return LIGHT_HIT;
|
|
6583
|
-
|
|
6584
|
-
}
|
|
6585
|
-
|
|
6586
6617
|
if ( hit ) {
|
|
6587
6618
|
|
|
6588
|
-
|
|
6619
|
+
result = SURFACE_HIT;
|
|
6589
6620
|
|
|
6590
6621
|
}
|
|
6591
6622
|
|
|
6592
|
-
return
|
|
6623
|
+
return result;
|
|
6593
6624
|
|
|
6594
6625
|
}
|
|
6595
6626
|
|
|
@@ -6602,7 +6633,7 @@ const getSurfaceRecordGLSL = /* glsl */`
|
|
|
6602
6633
|
int getSurfaceRecord(
|
|
6603
6634
|
Material material, SurfaceHit surfaceHit, sampler2DArray attributesArray,
|
|
6604
6635
|
float accumulatedRoughness,
|
|
6605
|
-
|
|
6636
|
+
inout SurfaceRecord surf
|
|
6606
6637
|
) {
|
|
6607
6638
|
|
|
6608
6639
|
if ( material.fogVolume ) {
|
|
@@ -6632,6 +6663,7 @@ const getSurfaceRecordGLSL = /* glsl */`
|
|
|
6632
6663
|
|
|
6633
6664
|
vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
|
|
6634
6665
|
albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
|
|
6666
|
+
|
|
6635
6667
|
}
|
|
6636
6668
|
|
|
6637
6669
|
if ( material.vertexColors ) {
|
|
@@ -6663,7 +6695,7 @@ const getSurfaceRecordGLSL = /* glsl */`
|
|
|
6663
6695
|
|| useAlphaTest && albedo.a < alphaTest
|
|
6664
6696
|
|
|
6665
6697
|
// opacity
|
|
6666
|
-
|| material.transparent && ! useAlphaTest && albedo.a <
|
|
6698
|
+
|| material.transparent && ! useAlphaTest && albedo.a < rand( 3 )
|
|
6667
6699
|
) {
|
|
6668
6700
|
|
|
6669
6701
|
return SKIP_SURFACE;
|
|
@@ -6922,11 +6954,13 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6922
6954
|
|
|
6923
6955
|
vec3 directLightContribution( vec3 worldWo, SurfaceRecord surf, RenderState state, vec3 rayOrigin ) {
|
|
6924
6956
|
|
|
6957
|
+
vec3 result = vec3( 0.0 );
|
|
6958
|
+
|
|
6925
6959
|
// uniformly pick a light or environment map
|
|
6926
|
-
if( lightsDenom != 0.0 &&
|
|
6960
|
+
if( lightsDenom != 0.0 && rand( 5 ) < float( lights.count ) / lightsDenom ) {
|
|
6927
6961
|
|
|
6928
6962
|
// sample a light or environment
|
|
6929
|
-
LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin,
|
|
6963
|
+
LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, rand3( 6 ) );
|
|
6930
6964
|
|
|
6931
6965
|
bool isSampleBelowSurface = ! surf.volumeParticle && dot( surf.faceNormal, lightRec.direction ) < 0.0;
|
|
6932
6966
|
if ( isSampleBelowSurface ) {
|
|
@@ -6943,7 +6977,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6943
6977
|
if (
|
|
6944
6978
|
lightRec.pdf > 0.0 &&
|
|
6945
6979
|
isDirectionValid( lightRec.direction, surf.normal, surf.faceNormal ) &&
|
|
6946
|
-
! attenuateHit(
|
|
6980
|
+
! attenuateHit( state, lightRay, lightRec.dist, attenuatedColor )
|
|
6947
6981
|
) {
|
|
6948
6982
|
|
|
6949
6983
|
// get the material pdf
|
|
@@ -6955,7 +6989,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6955
6989
|
// weight the direct light contribution
|
|
6956
6990
|
float lightPdf = lightRec.pdf / lightsDenom;
|
|
6957
6991
|
float misWeight = lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
|
|
6958
|
-
|
|
6992
|
+
result = attenuatedColor * lightRec.emission * state.throughputColor * sampleColor * misWeight / lightPdf;
|
|
6959
6993
|
|
|
6960
6994
|
}
|
|
6961
6995
|
|
|
@@ -6965,7 +6999,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6965
6999
|
|
|
6966
7000
|
// find a sample in the environment map to include in the contribution
|
|
6967
7001
|
vec3 envColor, envDirection;
|
|
6968
|
-
float envPdf = sampleEquirectProbability(
|
|
7002
|
+
float envPdf = sampleEquirectProbability( rand2( 7 ), envColor, envDirection );
|
|
6969
7003
|
envDirection = invEnvRotation3x3 * envDirection;
|
|
6970
7004
|
|
|
6971
7005
|
// this env sampling is not set up for transmissive sampling and yields overly bright
|
|
@@ -6986,7 +7020,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6986
7020
|
if (
|
|
6987
7021
|
envPdf > 0.0 &&
|
|
6988
7022
|
isDirectionValid( envDirection, surf.normal, surf.faceNormal ) &&
|
|
6989
|
-
! attenuateHit(
|
|
7023
|
+
! attenuateHit( state, envRay, INFINITY, attenuatedColor )
|
|
6990
7024
|
) {
|
|
6991
7025
|
|
|
6992
7026
|
// get the material pdf
|
|
@@ -6998,7 +7032,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6998
7032
|
// weight the direct light contribution
|
|
6999
7033
|
envPdf /= lightsDenom;
|
|
7000
7034
|
float misWeight = misHeuristic( envPdf, envMaterialPdf );
|
|
7001
|
-
|
|
7035
|
+
result = attenuatedColor * environmentIntensity * envColor * state.throughputColor * sampleColor * misWeight / envPdf;
|
|
7002
7036
|
|
|
7003
7037
|
}
|
|
7004
7038
|
|
|
@@ -7006,12 +7040,675 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
7006
7040
|
|
|
7007
7041
|
}
|
|
7008
7042
|
|
|
7009
|
-
return
|
|
7043
|
+
// Function changed to have a single return statement to potentially help with crashes on Mac OS.
|
|
7044
|
+
// See issue #470
|
|
7045
|
+
return result;
|
|
7046
|
+
|
|
7047
|
+
}
|
|
7048
|
+
|
|
7049
|
+
`;
|
|
7050
|
+
|
|
7051
|
+
const stratifiedTextureGLSL = /* glsl */`
|
|
7052
|
+
|
|
7053
|
+
uniform sampler2D stratifiedTexture;
|
|
7054
|
+
uniform sampler2D stratifiedOffsetTexture;
|
|
7055
|
+
|
|
7056
|
+
uint sobolPixelIndex = 0u;
|
|
7057
|
+
uint sobolPathIndex = 0u;
|
|
7058
|
+
uint sobolBounceIndex = 0u;
|
|
7059
|
+
vec4 pixelSeed = vec4( 0 );
|
|
7060
|
+
|
|
7061
|
+
vec4 rand4( int v ) {
|
|
7062
|
+
|
|
7063
|
+
ivec2 uv = ivec2( v, sobolBounceIndex );
|
|
7064
|
+
vec4 stratifiedSample = texelFetch( stratifiedTexture, uv, 0 );
|
|
7065
|
+
return fract( stratifiedSample + pixelSeed.r ); // blue noise + stratified samples
|
|
7066
|
+
|
|
7067
|
+
}
|
|
7068
|
+
|
|
7069
|
+
vec3 rand3( int v ) {
|
|
7070
|
+
|
|
7071
|
+
return rand4( v ).xyz;
|
|
7072
|
+
|
|
7073
|
+
}
|
|
7074
|
+
|
|
7075
|
+
vec2 rand2( int v ) {
|
|
7076
|
+
|
|
7077
|
+
return rand4( v ).xy;
|
|
7078
|
+
|
|
7079
|
+
}
|
|
7080
|
+
|
|
7081
|
+
float rand( int v ) {
|
|
7082
|
+
|
|
7083
|
+
return rand4( v ).x;
|
|
7084
|
+
|
|
7085
|
+
}
|
|
7086
|
+
|
|
7087
|
+
void rng_initialize( vec2 screenCoord, int frame ) {
|
|
7088
|
+
|
|
7089
|
+
// tile the small noise texture across the entire screen
|
|
7090
|
+
ivec2 noiseSize = ivec2( textureSize( stratifiedOffsetTexture, 0 ) );
|
|
7091
|
+
pixelSeed = texelFetch( stratifiedOffsetTexture, ivec2( screenCoord.xy ) % noiseSize, 0 );
|
|
7010
7092
|
|
|
7011
7093
|
}
|
|
7012
7094
|
|
|
7013
7095
|
`;
|
|
7014
7096
|
|
|
7097
|
+
// Stratified Sampling based on implementation from hoverinc pathtracer
|
|
7098
|
+
// - https://github.com/hoverinc/ray-tracing-renderer
|
|
7099
|
+
// - http://www.pbr-book.org/3ed-2018/Sampling_and_Reconstruction/Stratified_Sampling.html
|
|
7100
|
+
|
|
7101
|
+
function shuffle( arr ) {
|
|
7102
|
+
|
|
7103
|
+
for ( let i = arr.length - 1; i > 0; i -- ) {
|
|
7104
|
+
|
|
7105
|
+
const j = Math.floor( Math.random() * ( i + 1 ) );
|
|
7106
|
+
const x = arr[ i ];
|
|
7107
|
+
arr[ i ] = arr[ j ];
|
|
7108
|
+
arr[ j ] = x;
|
|
7109
|
+
|
|
7110
|
+
}
|
|
7111
|
+
|
|
7112
|
+
return arr;
|
|
7113
|
+
|
|
7114
|
+
}
|
|
7115
|
+
|
|
7116
|
+
// strataCount : The number of bins per dimension
|
|
7117
|
+
// dimensions : The number of dimensions to generate stratified values for
|
|
7118
|
+
class StratifiedSampler {
|
|
7119
|
+
|
|
7120
|
+
constructor( strataCount, dimensions ) {
|
|
7121
|
+
|
|
7122
|
+
const l = strataCount ** dimensions;
|
|
7123
|
+
const strata = new Uint16Array( l );
|
|
7124
|
+
let index = l;
|
|
7125
|
+
|
|
7126
|
+
// each integer represents a statum bin
|
|
7127
|
+
for ( let i = 0; i < l; i ++ ) {
|
|
7128
|
+
|
|
7129
|
+
strata[ i ] = i;
|
|
7130
|
+
|
|
7131
|
+
}
|
|
7132
|
+
|
|
7133
|
+
this.samples = new Float32Array( dimensions );
|
|
7134
|
+
|
|
7135
|
+
this.strataCount = strataCount;
|
|
7136
|
+
|
|
7137
|
+
this.restart = function () {
|
|
7138
|
+
|
|
7139
|
+
index = 0;
|
|
7140
|
+
|
|
7141
|
+
};
|
|
7142
|
+
|
|
7143
|
+
this.next = function () {
|
|
7144
|
+
|
|
7145
|
+
const { samples } = this;
|
|
7146
|
+
|
|
7147
|
+
if ( index >= strata.length ) {
|
|
7148
|
+
|
|
7149
|
+
shuffle( strata );
|
|
7150
|
+
this.restart();
|
|
7151
|
+
|
|
7152
|
+
}
|
|
7153
|
+
|
|
7154
|
+
let stratum = strata[ index ++ ];
|
|
7155
|
+
|
|
7156
|
+
for ( let i = 0; i < dimensions; i ++ ) {
|
|
7157
|
+
|
|
7158
|
+
samples[ i ] = ( stratum % strataCount + Math.random() ) / strataCount;
|
|
7159
|
+
stratum = Math.floor( stratum / strataCount );
|
|
7160
|
+
|
|
7161
|
+
}
|
|
7162
|
+
|
|
7163
|
+
return samples;
|
|
7164
|
+
|
|
7165
|
+
};
|
|
7166
|
+
|
|
7167
|
+
}
|
|
7168
|
+
|
|
7169
|
+
}
|
|
7170
|
+
|
|
7171
|
+
// Stratified Sampling based on implementation from hoverinc pathtracer
|
|
7172
|
+
|
|
7173
|
+
// Stratified set of data with each tuple stratified separately and combined
|
|
7174
|
+
class StratifiedSamplerCombined {
|
|
7175
|
+
|
|
7176
|
+
constructor( strataCount, listOfDimensions ) {
|
|
7177
|
+
|
|
7178
|
+
let totalDim = 0;
|
|
7179
|
+
for ( const dim of listOfDimensions ) {
|
|
7180
|
+
|
|
7181
|
+
totalDim += dim;
|
|
7182
|
+
|
|
7183
|
+
}
|
|
7184
|
+
|
|
7185
|
+
const combined = new Float32Array( totalDim );
|
|
7186
|
+
const strataObjs = [];
|
|
7187
|
+
let offset = 0;
|
|
7188
|
+
for ( const dim of listOfDimensions ) {
|
|
7189
|
+
|
|
7190
|
+
const sampler = new StratifiedSampler( strataCount, dim );
|
|
7191
|
+
sampler.samples = new Float32Array( combined.buffer, offset, sampler.samples.length );
|
|
7192
|
+
offset += sampler.samples.length * 4;
|
|
7193
|
+
strataObjs.push( sampler );
|
|
7194
|
+
|
|
7195
|
+
}
|
|
7196
|
+
|
|
7197
|
+
this.samples = combined;
|
|
7198
|
+
|
|
7199
|
+
this.strataCount = strataCount;
|
|
7200
|
+
|
|
7201
|
+
this.next = function () {
|
|
7202
|
+
|
|
7203
|
+
for ( const strata of strataObjs ) {
|
|
7204
|
+
|
|
7205
|
+
strata.next();
|
|
7206
|
+
|
|
7207
|
+
}
|
|
7208
|
+
|
|
7209
|
+
return combined;
|
|
7210
|
+
|
|
7211
|
+
};
|
|
7212
|
+
|
|
7213
|
+
this.restart = function () {
|
|
7214
|
+
|
|
7215
|
+
for ( const strata of strataObjs ) {
|
|
7216
|
+
|
|
7217
|
+
strata.restart();
|
|
7218
|
+
|
|
7219
|
+
}
|
|
7220
|
+
|
|
7221
|
+
};
|
|
7222
|
+
|
|
7223
|
+
}
|
|
7224
|
+
|
|
7225
|
+
}
|
|
7226
|
+
|
|
7227
|
+
class StratifiedSamplesTexture extends DataTexture {
|
|
7228
|
+
|
|
7229
|
+
constructor( count = 1, depth = 1, strata = 8 ) {
|
|
7230
|
+
|
|
7231
|
+
super( new Float32Array( 1 ), 1, 1, RGBAFormat, FloatType );
|
|
7232
|
+
this.minFilter = NearestFilter;
|
|
7233
|
+
this.magFilter = NearestFilter;
|
|
7234
|
+
|
|
7235
|
+
this.strata = strata;
|
|
7236
|
+
this.sampler = null;
|
|
7237
|
+
|
|
7238
|
+
this.init( count, depth, strata );
|
|
7239
|
+
|
|
7240
|
+
}
|
|
7241
|
+
|
|
7242
|
+
init( count, depth, strata = this.strata ) {
|
|
7243
|
+
|
|
7244
|
+
const { image } = this;
|
|
7245
|
+
if ( image.width === depth && image.height === count ) {
|
|
7246
|
+
|
|
7247
|
+
return;
|
|
7248
|
+
|
|
7249
|
+
}
|
|
7250
|
+
|
|
7251
|
+
const dimensions = new Array( count * depth ).fill( 4 );
|
|
7252
|
+
const sampler = new StratifiedSamplerCombined( strata, dimensions );
|
|
7253
|
+
|
|
7254
|
+
image.width = depth;
|
|
7255
|
+
image.height = count;
|
|
7256
|
+
image.data = sampler.samples;
|
|
7257
|
+
|
|
7258
|
+
this.sampler = sampler;
|
|
7259
|
+
|
|
7260
|
+
this.dispose();
|
|
7261
|
+
this.next();
|
|
7262
|
+
|
|
7263
|
+
}
|
|
7264
|
+
|
|
7265
|
+
next() {
|
|
7266
|
+
|
|
7267
|
+
this.sampler.next();
|
|
7268
|
+
this.needsUpdate = true;
|
|
7269
|
+
|
|
7270
|
+
}
|
|
7271
|
+
|
|
7272
|
+
}
|
|
7273
|
+
|
|
7274
|
+
function shuffleArray( array, random = Math.random ) {
|
|
7275
|
+
|
|
7276
|
+
for ( let i = array.length - 1; i > 0; i -- ) {
|
|
7277
|
+
|
|
7278
|
+
const replaceIndex = ~ ~ ( ( random() - 1e-6 ) * i );
|
|
7279
|
+
const tmp = array[ i ];
|
|
7280
|
+
array[ i ] = array[ replaceIndex ];
|
|
7281
|
+
array[ replaceIndex ] = tmp;
|
|
7282
|
+
|
|
7283
|
+
}
|
|
7284
|
+
|
|
7285
|
+
}
|
|
7286
|
+
|
|
7287
|
+
function fillWithOnes( array, count ) {
|
|
7288
|
+
|
|
7289
|
+
array.fill( 0 );
|
|
7290
|
+
|
|
7291
|
+
for ( let i = 0; i < count; i ++ ) {
|
|
7292
|
+
|
|
7293
|
+
array[ i ] = 1;
|
|
7294
|
+
|
|
7295
|
+
}
|
|
7296
|
+
|
|
7297
|
+
}
|
|
7298
|
+
|
|
7299
|
+
class BlueNoiseSamples {
|
|
7300
|
+
|
|
7301
|
+
constructor( size ) {
|
|
7302
|
+
|
|
7303
|
+
this.count = 0;
|
|
7304
|
+
this.size = - 1;
|
|
7305
|
+
this.sigma = - 1;
|
|
7306
|
+
this.radius = - 1;
|
|
7307
|
+
this.lookupTable = null;
|
|
7308
|
+
this.score = null;
|
|
7309
|
+
this.binaryPattern = null;
|
|
7310
|
+
|
|
7311
|
+
this.resize( size );
|
|
7312
|
+
this.setSigma( 1.5 );
|
|
7313
|
+
|
|
7314
|
+
}
|
|
7315
|
+
|
|
7316
|
+
findVoid() {
|
|
7317
|
+
|
|
7318
|
+
const { score, binaryPattern } = this;
|
|
7319
|
+
|
|
7320
|
+
let currValue = Infinity;
|
|
7321
|
+
let currIndex = - 1;
|
|
7322
|
+
for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
|
|
7323
|
+
|
|
7324
|
+
if ( binaryPattern[ i ] !== 0 ) {
|
|
7325
|
+
|
|
7326
|
+
continue;
|
|
7327
|
+
|
|
7328
|
+
}
|
|
7329
|
+
|
|
7330
|
+
const pScore = score[ i ];
|
|
7331
|
+
if ( pScore < currValue ) {
|
|
7332
|
+
|
|
7333
|
+
currValue = pScore;
|
|
7334
|
+
currIndex = i;
|
|
7335
|
+
|
|
7336
|
+
}
|
|
7337
|
+
|
|
7338
|
+
}
|
|
7339
|
+
|
|
7340
|
+
return currIndex;
|
|
7341
|
+
|
|
7342
|
+
}
|
|
7343
|
+
|
|
7344
|
+
findCluster() {
|
|
7345
|
+
|
|
7346
|
+
const { score, binaryPattern } = this;
|
|
7347
|
+
|
|
7348
|
+
let currValue = - Infinity;
|
|
7349
|
+
let currIndex = - 1;
|
|
7350
|
+
for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
|
|
7351
|
+
|
|
7352
|
+
if ( binaryPattern[ i ] !== 1 ) {
|
|
7353
|
+
|
|
7354
|
+
continue;
|
|
7355
|
+
|
|
7356
|
+
}
|
|
7357
|
+
|
|
7358
|
+
const pScore = score[ i ];
|
|
7359
|
+
if ( pScore > currValue ) {
|
|
7360
|
+
|
|
7361
|
+
currValue = pScore;
|
|
7362
|
+
currIndex = i;
|
|
7363
|
+
|
|
7364
|
+
}
|
|
7365
|
+
|
|
7366
|
+
}
|
|
7367
|
+
|
|
7368
|
+
return currIndex;
|
|
7369
|
+
|
|
7370
|
+
}
|
|
7371
|
+
|
|
7372
|
+
setSigma( sigma ) {
|
|
7373
|
+
|
|
7374
|
+
if ( sigma === this.sigma ) {
|
|
7375
|
+
|
|
7376
|
+
return;
|
|
7377
|
+
|
|
7378
|
+
}
|
|
7379
|
+
|
|
7380
|
+
// generate a radius in which the score will be updated under the
|
|
7381
|
+
// assumption that e^-10 is insignificant enough to be the border at
|
|
7382
|
+
// which we drop off.
|
|
7383
|
+
const radius = ~ ~ ( Math.sqrt( 10 * 2 * ( sigma ** 2 ) ) + 1 );
|
|
7384
|
+
const lookupWidth = 2 * radius + 1;
|
|
7385
|
+
const lookupTable = new Float32Array( lookupWidth * lookupWidth );
|
|
7386
|
+
const sigma2 = sigma * sigma;
|
|
7387
|
+
for ( let x = - radius; x <= radius; x ++ ) {
|
|
7388
|
+
|
|
7389
|
+
for ( let y = - radius; y <= radius; y ++ ) {
|
|
7390
|
+
|
|
7391
|
+
const index = ( radius + y ) * lookupWidth + x + radius;
|
|
7392
|
+
const dist2 = x * x + y * y;
|
|
7393
|
+
lookupTable[ index ] = Math.E ** ( - dist2 / ( 2 * sigma2 ) );
|
|
7394
|
+
|
|
7395
|
+
}
|
|
7396
|
+
|
|
7397
|
+
}
|
|
7398
|
+
|
|
7399
|
+
this.lookupTable = lookupTable;
|
|
7400
|
+
this.sigma = sigma;
|
|
7401
|
+
this.radius = radius;
|
|
7402
|
+
|
|
7403
|
+
}
|
|
7404
|
+
|
|
7405
|
+
resize( size ) {
|
|
7406
|
+
|
|
7407
|
+
if ( this.size !== size ) {
|
|
7408
|
+
|
|
7409
|
+
this.size = size;
|
|
7410
|
+
this.score = new Float32Array( size * size );
|
|
7411
|
+
this.binaryPattern = new Uint8Array( size * size );
|
|
7412
|
+
|
|
7413
|
+
}
|
|
7414
|
+
|
|
7415
|
+
|
|
7416
|
+
}
|
|
7417
|
+
|
|
7418
|
+
invert() {
|
|
7419
|
+
|
|
7420
|
+
const { binaryPattern, score, size } = this;
|
|
7421
|
+
|
|
7422
|
+
score.fill( 0 );
|
|
7423
|
+
|
|
7424
|
+
for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
|
|
7425
|
+
|
|
7426
|
+
if ( binaryPattern[ i ] === 0 ) {
|
|
7427
|
+
|
|
7428
|
+
const y = ~ ~ ( i / size );
|
|
7429
|
+
const x = i - y * size;
|
|
7430
|
+
this.updateScore( x, y, 1 );
|
|
7431
|
+
binaryPattern[ i ] = 1;
|
|
7432
|
+
|
|
7433
|
+
} else {
|
|
7434
|
+
|
|
7435
|
+
binaryPattern[ i ] = 0;
|
|
7436
|
+
|
|
7437
|
+
}
|
|
7438
|
+
|
|
7439
|
+
}
|
|
7440
|
+
|
|
7441
|
+
}
|
|
7442
|
+
|
|
7443
|
+
updateScore( x, y, multiplier ) {
|
|
7444
|
+
|
|
7445
|
+
// TODO: Is there a way to keep track of the highest and lowest scores here to avoid have to search over
|
|
7446
|
+
// everything in the buffer?
|
|
7447
|
+
const { size, score, lookupTable } = this;
|
|
7448
|
+
|
|
7449
|
+
// const sigma2 = sigma * sigma;
|
|
7450
|
+
// const radius = Math.floor( size / 2 );
|
|
7451
|
+
const radius = this.radius;
|
|
7452
|
+
const lookupWidth = 2 * radius + 1;
|
|
7453
|
+
for ( let px = - radius; px <= radius; px ++ ) {
|
|
7454
|
+
|
|
7455
|
+
for ( let py = - radius; py <= radius; py ++ ) {
|
|
7456
|
+
|
|
7457
|
+
// const dist2 = px * px + py * py;
|
|
7458
|
+
// const value = Math.E ** ( - dist2 / ( 2 * sigma2 ) );
|
|
7459
|
+
|
|
7460
|
+
const lookupIndex = ( radius + py ) * lookupWidth + px + radius;
|
|
7461
|
+
const value = lookupTable[ lookupIndex ];
|
|
7462
|
+
|
|
7463
|
+
let sx = ( x + px );
|
|
7464
|
+
sx = sx < 0 ? size + sx : sx % size;
|
|
7465
|
+
|
|
7466
|
+
let sy = ( y + py );
|
|
7467
|
+
sy = sy < 0 ? size + sy : sy % size;
|
|
7468
|
+
|
|
7469
|
+
const sindex = sy * size + sx;
|
|
7470
|
+
score[ sindex ] += multiplier * value;
|
|
7471
|
+
|
|
7472
|
+
}
|
|
7473
|
+
|
|
7474
|
+
}
|
|
7475
|
+
|
|
7476
|
+
}
|
|
7477
|
+
|
|
7478
|
+
addPointIndex( index ) {
|
|
7479
|
+
|
|
7480
|
+
this.binaryPattern[ index ] = 1;
|
|
7481
|
+
|
|
7482
|
+
const size = this.size;
|
|
7483
|
+
const y = ~ ~ ( index / size );
|
|
7484
|
+
const x = index - y * size;
|
|
7485
|
+
this.updateScore( x, y, 1 );
|
|
7486
|
+
this.count ++;
|
|
7487
|
+
|
|
7488
|
+
}
|
|
7489
|
+
|
|
7490
|
+
removePointIndex( index ) {
|
|
7491
|
+
|
|
7492
|
+
this.binaryPattern[ index ] = 0;
|
|
7493
|
+
|
|
7494
|
+
const size = this.size;
|
|
7495
|
+
const y = ~ ~ ( index / size );
|
|
7496
|
+
const x = index - y * size;
|
|
7497
|
+
this.updateScore( x, y, - 1 );
|
|
7498
|
+
this.count --;
|
|
7499
|
+
|
|
7500
|
+
}
|
|
7501
|
+
|
|
7502
|
+
copy( source ) {
|
|
7503
|
+
|
|
7504
|
+
this.resize( source.size );
|
|
7505
|
+
this.score.set( source.score );
|
|
7506
|
+
this.binaryPattern.set( source.binaryPattern );
|
|
7507
|
+
this.setSigma( source.sigma );
|
|
7508
|
+
this.count = source.count;
|
|
7509
|
+
|
|
7510
|
+
}
|
|
7511
|
+
|
|
7512
|
+
}
|
|
7513
|
+
|
|
7514
|
+
class BlueNoiseGenerator {
|
|
7515
|
+
|
|
7516
|
+
constructor() {
|
|
7517
|
+
|
|
7518
|
+
this.random = Math.random;
|
|
7519
|
+
this.sigma = 1.5;
|
|
7520
|
+
this.size = 64;
|
|
7521
|
+
this.majorityPointsRatio = 0.1;
|
|
7522
|
+
|
|
7523
|
+
this.samples = new BlueNoiseSamples( 1 );
|
|
7524
|
+
this.savedSamples = new BlueNoiseSamples( 1 );
|
|
7525
|
+
|
|
7526
|
+
}
|
|
7527
|
+
|
|
7528
|
+
generate() {
|
|
7529
|
+
|
|
7530
|
+
// http://cv.ulichney.com/papers/1993-void-cluster.pdf
|
|
7531
|
+
|
|
7532
|
+
const {
|
|
7533
|
+
samples,
|
|
7534
|
+
savedSamples,
|
|
7535
|
+
sigma,
|
|
7536
|
+
majorityPointsRatio,
|
|
7537
|
+
size,
|
|
7538
|
+
} = this;
|
|
7539
|
+
|
|
7540
|
+
samples.resize( size );
|
|
7541
|
+
samples.setSigma( sigma );
|
|
7542
|
+
|
|
7543
|
+
// 1. Randomly place the minority points.
|
|
7544
|
+
const pointCount = Math.floor( size * size * majorityPointsRatio );
|
|
7545
|
+
const initialSamples = samples.binaryPattern;
|
|
7546
|
+
|
|
7547
|
+
fillWithOnes( initialSamples, pointCount );
|
|
7548
|
+
shuffleArray( initialSamples, this.random );
|
|
7549
|
+
|
|
7550
|
+
for ( let i = 0, l = initialSamples.length; i < l; i ++ ) {
|
|
7551
|
+
|
|
7552
|
+
if ( initialSamples[ i ] === 1 ) {
|
|
7553
|
+
|
|
7554
|
+
samples.addPointIndex( i );
|
|
7555
|
+
|
|
7556
|
+
}
|
|
7557
|
+
|
|
7558
|
+
}
|
|
7559
|
+
|
|
7560
|
+
// 2. Remove minority point that is in densest cluster and place it in the largest void.
|
|
7561
|
+
while ( true ) {
|
|
7562
|
+
|
|
7563
|
+
const clusterIndex = samples.findCluster();
|
|
7564
|
+
samples.removePointIndex( clusterIndex );
|
|
7565
|
+
|
|
7566
|
+
const voidIndex = samples.findVoid();
|
|
7567
|
+
if ( clusterIndex === voidIndex ) {
|
|
7568
|
+
|
|
7569
|
+
samples.addPointIndex( clusterIndex );
|
|
7570
|
+
break;
|
|
7571
|
+
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7574
|
+
samples.addPointIndex( voidIndex );
|
|
7575
|
+
|
|
7576
|
+
}
|
|
7577
|
+
|
|
7578
|
+
// 3. PHASE I: Assign a rank to each progressively less dense cluster point and put it
|
|
7579
|
+
// in the dither array.
|
|
7580
|
+
const ditherArray = new Uint32Array( size * size );
|
|
7581
|
+
savedSamples.copy( samples );
|
|
7582
|
+
|
|
7583
|
+
let rank;
|
|
7584
|
+
rank = samples.count - 1;
|
|
7585
|
+
while ( rank >= 0 ) {
|
|
7586
|
+
|
|
7587
|
+
const clusterIndex = samples.findCluster();
|
|
7588
|
+
samples.removePointIndex( clusterIndex );
|
|
7589
|
+
|
|
7590
|
+
ditherArray[ clusterIndex ] = rank;
|
|
7591
|
+
rank --;
|
|
7592
|
+
|
|
7593
|
+
}
|
|
7594
|
+
|
|
7595
|
+
// 4. PHASE II: Do the same thing for the largest voids up to half of the total pixels using
|
|
7596
|
+
// the initial binary pattern.
|
|
7597
|
+
const totalSize = size * size;
|
|
7598
|
+
rank = savedSamples.count;
|
|
7599
|
+
while ( rank < totalSize / 2 ) {
|
|
7600
|
+
|
|
7601
|
+
const voidIndex = savedSamples.findVoid();
|
|
7602
|
+
savedSamples.addPointIndex( voidIndex );
|
|
7603
|
+
ditherArray[ voidIndex ] = rank;
|
|
7604
|
+
rank ++;
|
|
7605
|
+
|
|
7606
|
+
}
|
|
7607
|
+
|
|
7608
|
+
// 5. PHASE III: Invert the pattern and finish out by assigning a rank to the remaining
|
|
7609
|
+
// and iteratively removing them.
|
|
7610
|
+
savedSamples.invert();
|
|
7611
|
+
|
|
7612
|
+
while ( rank < totalSize ) {
|
|
7613
|
+
|
|
7614
|
+
const clusterIndex = savedSamples.findCluster();
|
|
7615
|
+
savedSamples.removePointIndex( clusterIndex );
|
|
7616
|
+
ditherArray[ clusterIndex ] = rank;
|
|
7617
|
+
rank ++;
|
|
7618
|
+
|
|
7619
|
+
}
|
|
7620
|
+
|
|
7621
|
+
return { data: ditherArray, maxValue: totalSize };
|
|
7622
|
+
|
|
7623
|
+
}
|
|
7624
|
+
|
|
7625
|
+
}
|
|
7626
|
+
|
|
7627
|
+
function getStride( channels ) {
|
|
7628
|
+
|
|
7629
|
+
if ( channels >= 3 ) {
|
|
7630
|
+
|
|
7631
|
+
return 4;
|
|
7632
|
+
|
|
7633
|
+
} else {
|
|
7634
|
+
|
|
7635
|
+
return channels;
|
|
7636
|
+
|
|
7637
|
+
}
|
|
7638
|
+
|
|
7639
|
+
}
|
|
7640
|
+
|
|
7641
|
+
function getFormat( channels ) {
|
|
7642
|
+
|
|
7643
|
+
switch ( channels ) {
|
|
7644
|
+
|
|
7645
|
+
case 1:
|
|
7646
|
+
return RedFormat;
|
|
7647
|
+
case 2:
|
|
7648
|
+
return RGFormat;
|
|
7649
|
+
default:
|
|
7650
|
+
return RGBAFormat;
|
|
7651
|
+
|
|
7652
|
+
}
|
|
7653
|
+
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
class BlueNoiseTexture extends DataTexture {
|
|
7657
|
+
|
|
7658
|
+
constructor( size = 64, channels = 1 ) {
|
|
7659
|
+
|
|
7660
|
+
super( new Float32Array( 4 ), 1, 1, RGBAFormat, FloatType );
|
|
7661
|
+
this.minFilter = NearestFilter;
|
|
7662
|
+
this.magFilter = NearestFilter;
|
|
7663
|
+
|
|
7664
|
+
this.size = size;
|
|
7665
|
+
this.channels = channels;
|
|
7666
|
+
this.update();
|
|
7667
|
+
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
update() {
|
|
7671
|
+
|
|
7672
|
+
const channels = this.channels;
|
|
7673
|
+
const size = this.size;
|
|
7674
|
+
const generator = new BlueNoiseGenerator();
|
|
7675
|
+
generator.channels = channels;
|
|
7676
|
+
generator.size = size;
|
|
7677
|
+
|
|
7678
|
+
const stride = getStride( channels );
|
|
7679
|
+
const format = getFormat( stride );
|
|
7680
|
+
if ( this.image.width !== size || format !== this.format ) {
|
|
7681
|
+
|
|
7682
|
+
this.image.width = size;
|
|
7683
|
+
this.image.height = size;
|
|
7684
|
+
this.image.data = new Float32Array( ( size ** 2 ) * stride );
|
|
7685
|
+
this.format = format;
|
|
7686
|
+
this.dispose();
|
|
7687
|
+
|
|
7688
|
+
}
|
|
7689
|
+
|
|
7690
|
+
const data = this.image.data;
|
|
7691
|
+
for ( let i = 0, l = channels; i < l; i ++ ) {
|
|
7692
|
+
|
|
7693
|
+
const result = generator.generate();
|
|
7694
|
+
const bin = result.data;
|
|
7695
|
+
const maxValue = result.maxValue;
|
|
7696
|
+
|
|
7697
|
+
for ( let j = 0, l2 = bin.length; j < l2; j ++ ) {
|
|
7698
|
+
|
|
7699
|
+
const value = bin[ j ] / maxValue;
|
|
7700
|
+
data[ j * stride + i ] = value;
|
|
7701
|
+
|
|
7702
|
+
}
|
|
7703
|
+
|
|
7704
|
+
}
|
|
7705
|
+
|
|
7706
|
+
this.needsUpdate = true;
|
|
7707
|
+
|
|
7708
|
+
}
|
|
7709
|
+
|
|
7710
|
+
}
|
|
7711
|
+
|
|
7015
7712
|
class PhysicalPathTracingMaterial extends MaterialBase {
|
|
7016
7713
|
|
|
7017
7714
|
onBeforeRender() {
|
|
@@ -7035,6 +7732,12 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7035
7732
|
FEATURE_DOF: 1,
|
|
7036
7733
|
FEATURE_BACKGROUND_MAP: 0,
|
|
7037
7734
|
FEATURE_FOG: 1,
|
|
7735
|
+
|
|
7736
|
+
// 0 = PCG
|
|
7737
|
+
// 1 = Sobol
|
|
7738
|
+
// 2 = Stratified List
|
|
7739
|
+
RANDOM_TYPE: 2,
|
|
7740
|
+
|
|
7038
7741
|
// 0 = Perspective
|
|
7039
7742
|
// 1 = Orthographic
|
|
7040
7743
|
// 2 = Equirectangular
|
|
@@ -7076,6 +7779,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7076
7779
|
|
|
7077
7780
|
backgroundAlpha: { value: 1.0 },
|
|
7078
7781
|
sobolTexture: { value: null },
|
|
7782
|
+
stratifiedTexture: { value: new StratifiedSamplesTexture() },
|
|
7783
|
+
stratifiedOffsetTexture: { value: new BlueNoiseTexture( 64, 1 ) },
|
|
7079
7784
|
},
|
|
7080
7785
|
|
|
7081
7786
|
vertexShader: /* glsl */`
|
|
@@ -7104,13 +7809,48 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7104
7809
|
#include <common>
|
|
7105
7810
|
|
|
7106
7811
|
// bvh intersection
|
|
7107
|
-
${
|
|
7108
|
-
${
|
|
7812
|
+
${ BVHShaderGLSL.common_functions }
|
|
7813
|
+
${ BVHShaderGLSL.bvh_struct_definitions }
|
|
7814
|
+
${ BVHShaderGLSL.bvh_ray_functions }
|
|
7815
|
+
|
|
7816
|
+
// uniform structs
|
|
7817
|
+
${ cameraStructGLSL }
|
|
7818
|
+
${ lightsStructGLSL }
|
|
7819
|
+
${ equirectStructGLSL }
|
|
7820
|
+
${ materialStructGLSL }
|
|
7109
7821
|
|
|
7110
7822
|
// random
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7823
|
+
#if RANDOM_TYPE == 2 // Stratified List
|
|
7824
|
+
|
|
7825
|
+
${ stratifiedTextureGLSL }
|
|
7826
|
+
|
|
7827
|
+
#elif RANDOM_TYPE == 1 // Sobol
|
|
7828
|
+
|
|
7829
|
+
${ pcgGLSL }
|
|
7830
|
+
${ sobolCommonGLSL }
|
|
7831
|
+
${ sobolSamplingGLSL }
|
|
7832
|
+
|
|
7833
|
+
#define rand(v) sobol(v)
|
|
7834
|
+
#define rand2(v) sobol2(v)
|
|
7835
|
+
#define rand3(v) sobol3(v)
|
|
7836
|
+
#define rand4(v) sobol4(v)
|
|
7837
|
+
|
|
7838
|
+
#else // PCG
|
|
7839
|
+
|
|
7840
|
+
${ pcgGLSL }
|
|
7841
|
+
|
|
7842
|
+
// Using the sobol functions seems to break the the compiler on MacOS
|
|
7843
|
+
// - specifically the "sobolReverseBits" function.
|
|
7844
|
+
uint sobolPixelIndex = 0u;
|
|
7845
|
+
uint sobolPathIndex = 0u;
|
|
7846
|
+
uint sobolBounceIndex = 0u;
|
|
7847
|
+
|
|
7848
|
+
#define rand(v) pcgRand()
|
|
7849
|
+
#define rand2(v) pcgRand2()
|
|
7850
|
+
#define rand3(v) pcgRand3()
|
|
7851
|
+
#define rand4(v) pcgRand4()
|
|
7852
|
+
|
|
7853
|
+
#endif
|
|
7114
7854
|
|
|
7115
7855
|
// common
|
|
7116
7856
|
${ arraySamplerTexelFetchGLSL }
|
|
@@ -7119,20 +7859,6 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7119
7859
|
${ mathGLSL }
|
|
7120
7860
|
${ intersectShapesGLSL }
|
|
7121
7861
|
|
|
7122
|
-
// uniform structs
|
|
7123
|
-
${ cameraStructGLSL }
|
|
7124
|
-
${ lightsStructGLSL }
|
|
7125
|
-
${ equirectStructGLSL }
|
|
7126
|
-
${ materialStructGLSL }
|
|
7127
|
-
${ fogMaterialBvhGLSL }
|
|
7128
|
-
|
|
7129
|
-
// sampling
|
|
7130
|
-
${ shapeSamplingGLSL }
|
|
7131
|
-
${ bsdfSamplingGLSL }
|
|
7132
|
-
${ equirectSamplingGLSL }
|
|
7133
|
-
${ lightSamplingGLSL }
|
|
7134
|
-
${ fogGLSL }
|
|
7135
|
-
|
|
7136
7862
|
// environment
|
|
7137
7863
|
uniform EquirectHdrInfo envMapInfo;
|
|
7138
7864
|
uniform mat4 environmentRotation;
|
|
@@ -7184,6 +7910,14 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7184
7910
|
mat3 invEnvRotation3x3;
|
|
7185
7911
|
float lightsDenom;
|
|
7186
7912
|
|
|
7913
|
+
// sampling
|
|
7914
|
+
${ fogMaterialBvhGLSL }
|
|
7915
|
+
${ shapeSamplingGLSL }
|
|
7916
|
+
${ bsdfSamplingGLSL }
|
|
7917
|
+
${ equirectSamplingGLSL }
|
|
7918
|
+
${ lightSamplingGLSL }
|
|
7919
|
+
${ fogGLSL }
|
|
7920
|
+
|
|
7187
7921
|
float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
|
|
7188
7922
|
|
|
7189
7923
|
return clamp(
|
|
@@ -7223,7 +7957,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7223
7957
|
|
|
7224
7958
|
// init
|
|
7225
7959
|
rng_initialize( gl_FragCoord.xy, seed );
|
|
7226
|
-
sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) |
|
|
7960
|
+
sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
|
|
7227
7961
|
sobolPathIndex = uint( seed );
|
|
7228
7962
|
|
|
7229
7963
|
// get camera ray
|
|
@@ -7239,7 +7973,6 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7239
7973
|
|
|
7240
7974
|
// surface results
|
|
7241
7975
|
SurfaceHit surfaceHit;
|
|
7242
|
-
LightRecord lightRec;
|
|
7243
7976
|
ScatterRecord scatterRec;
|
|
7244
7977
|
|
|
7245
7978
|
// path tracing state
|
|
@@ -7248,7 +7981,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7248
7981
|
#if FEATURE_FOG
|
|
7249
7982
|
|
|
7250
7983
|
state.fogMaterial.fogVolume = bvhIntersectFogVolumeHit(
|
|
7251
|
-
|
|
7984
|
+
ray.origin, - ray.direction,
|
|
7252
7985
|
materialIndexAttribute, materials,
|
|
7253
7986
|
state.fogMaterial
|
|
7254
7987
|
);
|
|
@@ -7263,48 +7996,46 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7263
7996
|
state.traversals = bounces - i;
|
|
7264
7997
|
state.firstRay = i == 0 && state.transmissiveTraversals == transmissiveBounces;
|
|
7265
7998
|
|
|
7266
|
-
int hitType = traceScene(
|
|
7267
|
-
ray, bvh, lights, state.fogMaterial,
|
|
7268
|
-
surfaceHit, lightRec
|
|
7269
|
-
);
|
|
7999
|
+
int hitType = traceScene( ray, state.fogMaterial, surfaceHit );
|
|
7270
8000
|
|
|
7271
|
-
if
|
|
8001
|
+
// check if we intersect any lights and accumulate the light contribution
|
|
8002
|
+
// TODO: we can add support for light surface rendering in the else condition if we
|
|
8003
|
+
// add the ability to toggle visibility of the the light
|
|
8004
|
+
if ( ! state.firstRay && ! state.transmissiveRay ) {
|
|
7272
8005
|
|
|
7273
|
-
|
|
8006
|
+
LightRecord lightRec;
|
|
8007
|
+
float lightDist = hitType == NO_HIT ? INFINITY : surfaceHit.dist;
|
|
8008
|
+
for ( uint i = 0u; i < lights.count; i ++ ) {
|
|
7274
8009
|
|
|
7275
|
-
|
|
8010
|
+
if (
|
|
8011
|
+
intersectLightAtIndex( lights.tex, ray.origin, ray.direction, i, lightRec ) &&
|
|
8012
|
+
lightRec.dist < lightDist
|
|
8013
|
+
) {
|
|
7276
8014
|
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
#if FEATURE_MIS
|
|
7280
|
-
|
|
7281
|
-
// NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
|
|
7282
|
-
if ( lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ) {
|
|
7283
|
-
|
|
7284
|
-
gl_FragColor.rgb += lightRec.emission * state.throughputColor;
|
|
7285
|
-
|
|
7286
|
-
} else {
|
|
8015
|
+
#if FEATURE_MIS
|
|
7287
8016
|
|
|
7288
8017
|
// weight the contribution
|
|
8018
|
+
// NOTE: Only area lights are supported for forward sampling and can be hit
|
|
7289
8019
|
float misWeight = misHeuristic( scatterRec.pdf, lightRec.pdf / lightsDenom );
|
|
7290
8020
|
gl_FragColor.rgb += lightRec.emission * state.throughputColor * misWeight;
|
|
7291
8021
|
|
|
7292
|
-
|
|
8022
|
+
#else
|
|
7293
8023
|
|
|
7294
|
-
|
|
8024
|
+
gl_FragColor.rgb += lightRec.emission * state.throughputColor;
|
|
7295
8025
|
|
|
7296
|
-
|
|
8026
|
+
#endif
|
|
7297
8027
|
|
|
7298
|
-
|
|
8028
|
+
}
|
|
7299
8029
|
|
|
7300
8030
|
}
|
|
7301
|
-
break;
|
|
7302
8031
|
|
|
7303
|
-
}
|
|
8032
|
+
}
|
|
8033
|
+
|
|
8034
|
+
if ( hitType == NO_HIT ) {
|
|
7304
8035
|
|
|
7305
8036
|
if ( state.firstRay || state.transmissiveRay ) {
|
|
7306
8037
|
|
|
7307
|
-
gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction,
|
|
8038
|
+
gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction, rand2( 2 ) ) * state.throughputColor;
|
|
7308
8039
|
gl_FragColor.a = backgroundAlpha;
|
|
7309
8040
|
|
|
7310
8041
|
} else {
|
|
@@ -7313,7 +8044,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7313
8044
|
|
|
7314
8045
|
// get the PDF of the hit envmap point
|
|
7315
8046
|
vec3 envColor;
|
|
7316
|
-
float envPdf = sampleEquirect(
|
|
8047
|
+
float envPdf = sampleEquirect( envRotation3x3 * ray.direction, envColor );
|
|
7317
8048
|
envPdf /= lightsDenom;
|
|
7318
8049
|
|
|
7319
8050
|
// and weight the contribution
|
|
@@ -7395,7 +8126,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7395
8126
|
}
|
|
7396
8127
|
|
|
7397
8128
|
scatterRec = bsdfSample( - ray.direction, surf );
|
|
7398
|
-
state.isShadowRay = scatterRec.specularPdf <
|
|
8129
|
+
state.isShadowRay = scatterRec.specularPdf < rand( 4 );
|
|
7399
8130
|
|
|
7400
8131
|
bool isBelowSurface = ! surf.volumeParticle && dot( scatterRec.direction, surf.faceNormal ) < 0.0;
|
|
7401
8132
|
vec3 hitPoint = stepRayOrigin( ray.origin, ray.direction, isBelowSurface ? - surf.faceNormal : surf.faceNormal, surfaceHit.dist );
|
|
@@ -7465,7 +8196,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7465
8196
|
rrProb = sqrt( rrProb );
|
|
7466
8197
|
rrProb = max( rrProb, depthProb );
|
|
7467
8198
|
rrProb = min( rrProb, 1.0 );
|
|
7468
|
-
if (
|
|
8199
|
+
if ( rand( 8 ) > rrProb ) {
|
|
7469
8200
|
|
|
7470
8201
|
break;
|
|
7471
8202
|
|
|
@@ -7870,5 +8601,5 @@ class CompatibilityDetector {
|
|
|
7870
8601
|
|
|
7871
8602
|
// core
|
|
7872
8603
|
|
|
7873
|
-
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,
|
|
8604
|
+
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 };
|
|
7874
8605
|
//# sourceMappingURL=index.module.js.map
|