three-gpu-pathtracer 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.module.js +1013 -322
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +1010 -320
- package/build/index.umd.cjs.map +1 -1
- package/package.json +2 -2
- package/src/core/DynamicPathTracingSceneGenerator.js +80 -40
- package/src/core/PathTracingRenderer.js +28 -34
- package/src/core/PathTracingSceneGenerator.js +11 -60
- package/src/materials/pathtracing/LambertPathTracingMaterial.js +1 -1
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +37 -12
- package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +19 -9
- package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
- package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +4 -4
- package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +2 -1
- package/src/materials/pathtracing/glsl/traceScene.glsl.js +1 -1
- package/src/shader/bsdf/bsdfSampling.glsl.js +14 -10
- package/src/shader/common/fresnel.glsl.js +15 -9
- package/src/shader/rand/pcg.glsl.js +4 -4
- package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
- package/src/shader/sampling/equirectSampling.glsl.js +8 -1
- package/src/shader/structs/lightsStruct.glsl.js +5 -7
- package/src/textures/BlueNoiseTexture.js +87 -0
- package/src/textures/ProceduralEquirectTexture.js +7 -8
- package/src/textures/blueNoise/BlueNoiseGenerator.js +115 -0
- package/src/textures/blueNoise/BlueNoiseSamples.js +214 -0
- package/src/textures/blueNoise/utils.js +24 -0
- package/src/uniforms/EquirectHdrInfoUniform.js +45 -8
- package/src/uniforms/LightsInfoUniformStruct.js +11 -7
- package/src/uniforms/MaterialsTexture.js +1 -1
- package/src/uniforms/RenderTarget2DArray.js +50 -3
- package/src/uniforms/StratifiedSamplesTexture.js +49 -0
- package/src/uniforms/stratified/StratifiedSampler.js +73 -0
- package/src/uniforms/stratified/StratifiedSamplerCombined.js +59 -0
- package/src/uniforms/utils.js +1 -1
- package/src/utils/GeometryPreparationUtils.js +8 -101
- package/src/workers/PathTracingSceneWorker.js +18 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-gpu-pathtracer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Path tracing renderer and utilities for three.js built on top of three-mesh-bvh.",
|
|
5
5
|
"module": "src/index.js",
|
|
6
6
|
"main": "build/index.umd.cjs",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"rollup": "^2.70.0",
|
|
43
43
|
"simple-git": "^3.10.0",
|
|
44
44
|
"three": "^0.160.0",
|
|
45
|
-
"three-mesh-bvh": "0.7.
|
|
45
|
+
"three-mesh-bvh": "0.7.3",
|
|
46
46
|
"yargs": "^17.5.1"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { BufferGeometry } from 'three';
|
|
2
|
-
import { StaticGeometryGenerator, MeshBVH } from 'three-mesh-bvh';
|
|
1
|
+
import { BufferGeometry, MeshBasicMaterial, BufferAttribute, Mesh } from 'three';
|
|
2
|
+
import { StaticGeometryGenerator, MeshBVH, SAH } from 'three-mesh-bvh';
|
|
3
3
|
import { setCommonAttributes, getGroupMaterialIndicesAttribute } from '../utils/GeometryPreparationUtils.js';
|
|
4
4
|
|
|
5
|
+
const dummyMaterial = new MeshBasicMaterial();
|
|
6
|
+
export function getDummyMesh() {
|
|
7
|
+
|
|
8
|
+
const emptyGeometry = new BufferGeometry();
|
|
9
|
+
emptyGeometry.setAttribute( 'position', new BufferAttribute( new Float32Array( 9 ), 3 ) );
|
|
10
|
+
return new Mesh( emptyGeometry, dummyMaterial );
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
export class DynamicPathTracingSceneGenerator {
|
|
6
15
|
|
|
7
16
|
get initialized() {
|
|
@@ -10,9 +19,29 @@ export class DynamicPathTracingSceneGenerator {
|
|
|
10
19
|
|
|
11
20
|
}
|
|
12
21
|
|
|
13
|
-
constructor(
|
|
22
|
+
constructor( objects ) {
|
|
23
|
+
|
|
24
|
+
// ensure the objects is an array
|
|
25
|
+
if ( ! Array.isArray( objects ) ) {
|
|
26
|
+
|
|
27
|
+
objects = [ objects ];
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// use a dummy object for a fallback
|
|
32
|
+
const finalObjects = [ ...objects ];
|
|
33
|
+
if ( finalObjects.length === 0 ) {
|
|
34
|
+
|
|
35
|
+
finalObjects.push( getDummyMesh() );
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// options
|
|
40
|
+
this.bvhOptions = {};
|
|
41
|
+
this.attributes = [ 'position', 'normal', 'tangent', 'color', 'uv', 'uv2' ];
|
|
14
42
|
|
|
15
|
-
|
|
43
|
+
// state
|
|
44
|
+
this.objects = finalObjects;
|
|
16
45
|
this.bvh = null;
|
|
17
46
|
this.geometry = new BufferGeometry();
|
|
18
47
|
this.materials = null;
|
|
@@ -36,64 +65,75 @@ export class DynamicPathTracingSceneGenerator {
|
|
|
36
65
|
|
|
37
66
|
dispose() {}
|
|
38
67
|
|
|
39
|
-
|
|
68
|
+
prepScene() {
|
|
40
69
|
|
|
41
|
-
|
|
42
|
-
if ( this.bvh === null ) {
|
|
70
|
+
if ( this.bvh !== null ) {
|
|
43
71
|
|
|
44
|
-
|
|
72
|
+
return;
|
|
45
73
|
|
|
46
|
-
|
|
74
|
+
}
|
|
47
75
|
|
|
48
|
-
|
|
76
|
+
const { objects, staticGeometryGenerator, geometry, lights, attributes } = this;
|
|
77
|
+
for ( let i = 0, l = objects.length; i < l; i ++ ) {
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
objects[ i ].traverse( c => {
|
|
51
80
|
|
|
52
|
-
|
|
53
|
-
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
81
|
+
if ( c.isMesh ) {
|
|
54
82
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
c.isSpotLight ||
|
|
58
|
-
c.isPointLight ||
|
|
59
|
-
c.isDirectionalLight
|
|
60
|
-
) {
|
|
83
|
+
const normalMapRequired = ! ! c.material.normalMap;
|
|
84
|
+
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
61
85
|
|
|
62
|
-
|
|
86
|
+
} else if (
|
|
87
|
+
c.isRectAreaLight ||
|
|
88
|
+
c.isSpotLight ||
|
|
89
|
+
c.isPointLight ||
|
|
90
|
+
c.isDirectionalLight
|
|
91
|
+
) {
|
|
63
92
|
|
|
64
|
-
|
|
93
|
+
lights.push( c );
|
|
65
94
|
|
|
66
|
-
}
|
|
95
|
+
}
|
|
67
96
|
|
|
68
|
-
}
|
|
97
|
+
} );
|
|
69
98
|
|
|
70
|
-
|
|
71
|
-
const materials = staticGeometryGenerator.getMaterials();
|
|
72
|
-
materials.forEach( material => {
|
|
99
|
+
}
|
|
73
100
|
|
|
74
|
-
|
|
101
|
+
const textureSet = new Set();
|
|
102
|
+
const materials = staticGeometryGenerator.getMaterials();
|
|
103
|
+
materials.forEach( material => {
|
|
75
104
|
|
|
76
|
-
|
|
77
|
-
if ( value && value.isTexture ) {
|
|
105
|
+
for ( const key in material ) {
|
|
78
106
|
|
|
79
|
-
|
|
107
|
+
const value = material[ key ];
|
|
108
|
+
if ( value && value.isTexture ) {
|
|
80
109
|
|
|
81
|
-
|
|
110
|
+
textureSet.add( value );
|
|
82
111
|
|
|
83
112
|
}
|
|
84
113
|
|
|
85
|
-
}
|
|
114
|
+
}
|
|
86
115
|
|
|
87
|
-
|
|
88
|
-
|
|
116
|
+
} );
|
|
117
|
+
|
|
118
|
+
staticGeometryGenerator.attributes = attributes;
|
|
119
|
+
staticGeometryGenerator.generate( geometry );
|
|
89
120
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
121
|
+
const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, materials, materials );
|
|
122
|
+
geometry.setAttribute( 'materialIndex', materialIndexAttribute );
|
|
123
|
+
geometry.clearGroups();
|
|
124
|
+
|
|
125
|
+
this.materials = materials;
|
|
126
|
+
this.textures = Array.from( textureSet );
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
generate() {
|
|
131
|
+
|
|
132
|
+
const { objects, staticGeometryGenerator, geometry, bvhOptions } = this;
|
|
133
|
+
if ( this.bvh === null ) {
|
|
93
134
|
|
|
94
|
-
this.
|
|
95
|
-
this.
|
|
96
|
-
this.textures = Array.from( textureSet );
|
|
135
|
+
this.prepScene();
|
|
136
|
+
this.bvh = new MeshBVH( geometry, { strategy: SAH, maxLeafTris: 1, ...bvhOptions } );
|
|
97
137
|
|
|
98
138
|
return {
|
|
99
139
|
lights: this.lights,
|
|
@@ -3,9 +3,6 @@ import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
|
3
3
|
import { BlendMaterial } from '../materials/fullscreen/BlendMaterial.js';
|
|
4
4
|
import { SobolNumberMapGenerator } from '../utils/SobolNumberMapGenerator.js';
|
|
5
5
|
|
|
6
|
-
const _scissor = new Vector4();
|
|
7
|
-
const _viewport = new Vector4();
|
|
8
|
-
|
|
9
6
|
function* renderTask() {
|
|
10
7
|
|
|
11
8
|
const {
|
|
@@ -47,12 +44,21 @@ function* renderTask() {
|
|
|
47
44
|
const h = _primaryTarget.height;
|
|
48
45
|
material.resolution.set( w * subW, h * subH );
|
|
49
46
|
material.sobolTexture = _sobolTarget.texture;
|
|
47
|
+
material.stratifiedTexture.init( 20, material.bounces + material.transmissiveBounces + 5 );
|
|
48
|
+
material.stratifiedTexture.next();
|
|
50
49
|
material.seed ++;
|
|
51
50
|
|
|
52
51
|
const tilesX = this.tiles.x || 1;
|
|
53
52
|
const tilesY = this.tiles.y || 1;
|
|
54
53
|
const totalTiles = tilesX * tilesY;
|
|
55
|
-
|
|
54
|
+
|
|
55
|
+
const pxSubW = Math.ceil( w * subW );
|
|
56
|
+
const pxSubH = Math.ceil( h * subH );
|
|
57
|
+
const pxSubX = Math.floor( subX * w );
|
|
58
|
+
const pxSubY = Math.floor( subY * h );
|
|
59
|
+
|
|
60
|
+
const pxTileW = Math.ceil( pxSubW / tilesX );
|
|
61
|
+
const pxTileH = Math.ceil( pxSubH / tilesY );
|
|
56
62
|
|
|
57
63
|
for ( let y = 0; y < tilesY; y ++ ) {
|
|
58
64
|
|
|
@@ -101,40 +107,28 @@ function* renderTask() {
|
|
|
101
107
|
|
|
102
108
|
}
|
|
103
109
|
|
|
110
|
+
// set the scissor and the viewport on the render target
|
|
111
|
+
// note that when using the webgl renderer set viewport the device pixel ratio
|
|
112
|
+
// is multiplied into the field causing some pixels to not be rendered
|
|
113
|
+
const reverseTy = tilesY - ty - 1;
|
|
114
|
+
_primaryTarget.scissor.set(
|
|
115
|
+
pxSubX + tx * pxTileW,
|
|
116
|
+
pxSubY + reverseTy * pxTileH,
|
|
117
|
+
Math.min( pxTileW, pxSubW - tx * pxTileW ),
|
|
118
|
+
Math.min( pxTileH, pxSubH - reverseTy * pxTileH ),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
_primaryTarget.viewport.set(
|
|
122
|
+
pxSubX,
|
|
123
|
+
pxSubY,
|
|
124
|
+
pxSubW,
|
|
125
|
+
pxSubH,
|
|
126
|
+
);
|
|
127
|
+
|
|
104
128
|
// three.js renderer takes values relative to the current pixel ratio
|
|
105
129
|
_renderer.setRenderTarget( _primaryTarget );
|
|
106
130
|
_renderer.setScissorTest( true );
|
|
107
131
|
|
|
108
|
-
// set the scissor window for a subtile
|
|
109
|
-
_scissor.x = tx * w / tilesX;
|
|
110
|
-
_scissor.y = ( tilesY - ty - 1 ) * h / tilesY;
|
|
111
|
-
_scissor.z = w / tilesX;
|
|
112
|
-
_scissor.w = h / tilesY;
|
|
113
|
-
|
|
114
|
-
// adjust for the subframe
|
|
115
|
-
_scissor.x = subX * w + subW * _scissor.x;
|
|
116
|
-
_scissor.y = subY * h + subH * _scissor.y;
|
|
117
|
-
_scissor.z = subW * _scissor.z;
|
|
118
|
-
_scissor.w = subH * _scissor.w;
|
|
119
|
-
|
|
120
|
-
// round for floating point cases
|
|
121
|
-
_scissor.x = _scissor.x;
|
|
122
|
-
_scissor.y = _scissor.y;
|
|
123
|
-
_scissor.z = _scissor.z;
|
|
124
|
-
_scissor.w = _scissor.w;
|
|
125
|
-
|
|
126
|
-
// multiply inverse of DPR in because threes multiplies it in
|
|
127
|
-
_scissor.multiplyScalar( dprInv ).ceil();
|
|
128
|
-
|
|
129
|
-
_viewport.x = subX * w;
|
|
130
|
-
_viewport.y = subY * h;
|
|
131
|
-
_viewport.z = subW * w;
|
|
132
|
-
_viewport.w = subH * h;
|
|
133
|
-
_viewport.multiplyScalar( dprInv ).ceil();
|
|
134
|
-
|
|
135
|
-
_renderer.setScissor( _scissor );
|
|
136
|
-
_renderer.setViewport( _viewport );
|
|
137
|
-
|
|
138
132
|
_renderer.autoClear = false;
|
|
139
133
|
_fsQuad.render( _renderer );
|
|
140
134
|
|
|
@@ -1,73 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SAH, MeshBVH, StaticGeometryGenerator } from 'three-mesh-bvh';
|
|
3
|
-
import { mergeMeshes } from '../utils/GeometryPreparationUtils.js';
|
|
1
|
+
import { DynamicPathTracingSceneGenerator } from './DynamicPathTracingSceneGenerator.js';
|
|
4
2
|
|
|
5
3
|
export class PathTracingSceneGenerator {
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
scene = Array.isArray( scene ) ? scene : [ scene ];
|
|
10
|
-
|
|
11
|
-
const meshes = [];
|
|
12
|
-
const lights = [];
|
|
13
|
-
|
|
14
|
-
for ( let i = 0, l = scene.length; i < l; i ++ ) {
|
|
15
|
-
|
|
16
|
-
scene[ i ].traverseVisible( c => {
|
|
17
|
-
|
|
18
|
-
if ( c.isSkinnedMesh || c.isMesh && c.morphTargetInfluences ) {
|
|
19
|
-
|
|
20
|
-
const generator = new StaticGeometryGenerator( c );
|
|
21
|
-
generator.attributes = [ 'position', 'color', 'normal', 'tangent', 'uv', 'uv2' ];
|
|
22
|
-
generator.applyWorldTransforms = false;
|
|
23
|
-
const mesh = new Mesh(
|
|
24
|
-
generator.generate(),
|
|
25
|
-
c.material,
|
|
26
|
-
);
|
|
27
|
-
mesh.matrixWorld.copy( c.matrixWorld );
|
|
28
|
-
mesh.matrix.copy( c.matrixWorld );
|
|
29
|
-
mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
|
|
30
|
-
meshes.push( mesh );
|
|
31
|
-
|
|
32
|
-
} else if ( c.isMesh ) {
|
|
33
|
-
|
|
34
|
-
meshes.push( c );
|
|
5
|
+
generate( scene, options = {} ) {
|
|
35
6
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
c.isPointLight ||
|
|
40
|
-
c.isDirectionalLight
|
|
41
|
-
) {
|
|
7
|
+
// ensure scene transforms are up to date
|
|
8
|
+
// TODO: remove this?
|
|
9
|
+
if ( Array.isArray( scene ) ) {
|
|
42
10
|
|
|
43
|
-
|
|
11
|
+
scene.forEach( s => s.updateMatrixWorld( true ) );
|
|
44
12
|
|
|
45
|
-
|
|
13
|
+
} else {
|
|
46
14
|
|
|
47
|
-
|
|
15
|
+
scene.updateMatrixWorld( true );
|
|
48
16
|
|
|
49
17
|
}
|
|
50
18
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} ),
|
|
55
|
-
lights,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
generate( scene, options = {} ) {
|
|
61
|
-
|
|
62
|
-
const { materials, textures, geometry, lights } = this.prepScene( scene );
|
|
63
|
-
const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
|
|
64
|
-
return {
|
|
65
|
-
scene,
|
|
66
|
-
materials,
|
|
67
|
-
textures,
|
|
68
|
-
lights,
|
|
69
|
-
bvh: new MeshBVH( geometry, bvhOptions ),
|
|
70
|
-
};
|
|
19
|
+
const generator = new DynamicPathTracingSceneGenerator( scene );
|
|
20
|
+
generator.bvhOptions = options;
|
|
21
|
+
return generator.generate();
|
|
71
22
|
|
|
72
23
|
}
|
|
73
24
|
|
|
@@ -194,7 +194,7 @@ export class LambertPathTracingMaterial extends MaterialBase {
|
|
|
194
194
|
uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
|
|
195
195
|
Material material = materials[ materialIndex ];
|
|
196
196
|
|
|
197
|
-
if ( material.opacity <
|
|
197
|
+
if ( material.opacity < pcgRand() ) {
|
|
198
198
|
|
|
199
199
|
vec3 point = rayOrigin + rayDirection * dist;
|
|
200
200
|
rayOrigin += rayDirection * dist - faceNormal * RAY_OFFSET;
|
|
@@ -48,6 +48,9 @@ import { attenuateHitGLSL } from './glsl/attenuateHit.glsl.js';
|
|
|
48
48
|
import { traceSceneGLSL } from './glsl/traceScene.glsl.js';
|
|
49
49
|
import { getSurfaceRecordGLSL } from './glsl/getSurfaceRecord.glsl.js';
|
|
50
50
|
import { directLightContributionGLSL } from './glsl/directLightContribution.glsl.js';
|
|
51
|
+
import { stratifiedTextureGLSL } from '../../shader/rand/stratifiedTexture.glsl.js';
|
|
52
|
+
import { StratifiedSamplesTexture } from '../../uniforms/StratifiedSamplesTexture.js';
|
|
53
|
+
import { BlueNoiseTexture } from '../../textures/BlueNoiseTexture.js';
|
|
51
54
|
|
|
52
55
|
export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
53
56
|
|
|
@@ -72,7 +75,12 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
72
75
|
FEATURE_DOF: 1,
|
|
73
76
|
FEATURE_BACKGROUND_MAP: 0,
|
|
74
77
|
FEATURE_FOG: 1,
|
|
75
|
-
|
|
78
|
+
|
|
79
|
+
// 0 = PCG
|
|
80
|
+
// 1 = Sobol
|
|
81
|
+
// 2 = Stratified List
|
|
82
|
+
RANDOM_TYPE: 2,
|
|
83
|
+
|
|
76
84
|
// 0 = Perspective
|
|
77
85
|
// 1 = Orthographic
|
|
78
86
|
// 2 = Equirectangular
|
|
@@ -114,6 +122,8 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
114
122
|
|
|
115
123
|
backgroundAlpha: { value: 1.0 },
|
|
116
124
|
sobolTexture: { value: null },
|
|
125
|
+
stratifiedTexture: { value: new StratifiedSamplesTexture() },
|
|
126
|
+
stratifiedOffsetTexture: { value: new BlueNoiseTexture( 64, 1 ) },
|
|
117
127
|
},
|
|
118
128
|
|
|
119
129
|
vertexShader: /* glsl */`
|
|
@@ -153,23 +163,35 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
153
163
|
${ materialStructGLSL }
|
|
154
164
|
|
|
155
165
|
// random
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
#if RANDOM_TYPE == 2 // Stratified List
|
|
167
|
+
|
|
168
|
+
${ stratifiedTextureGLSL }
|
|
169
|
+
|
|
170
|
+
#elif RANDOM_TYPE == 1 // Sobol
|
|
158
171
|
|
|
172
|
+
${ pcgGLSL }
|
|
159
173
|
${ sobolCommonGLSL }
|
|
160
174
|
${ sobolSamplingGLSL }
|
|
161
175
|
|
|
162
|
-
|
|
176
|
+
#define rand(v) sobol(v)
|
|
177
|
+
#define rand2(v) sobol2(v)
|
|
178
|
+
#define rand3(v) sobol3(v)
|
|
179
|
+
#define rand4(v) sobol4(v)
|
|
180
|
+
|
|
181
|
+
#else // PCG
|
|
182
|
+
|
|
183
|
+
${ pcgGLSL }
|
|
163
184
|
|
|
164
185
|
// Using the sobol functions seems to break the the compiler on MacOS
|
|
165
186
|
// - specifically the "sobolReverseBits" function.
|
|
166
187
|
uint sobolPixelIndex = 0u;
|
|
167
188
|
uint sobolPathIndex = 0u;
|
|
168
189
|
uint sobolBounceIndex = 0u;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
190
|
+
|
|
191
|
+
#define rand(v) pcgRand()
|
|
192
|
+
#define rand2(v) pcgRand2()
|
|
193
|
+
#define rand3(v) pcgRand3()
|
|
194
|
+
#define rand4(v) pcgRand4()
|
|
173
195
|
|
|
174
196
|
#endif
|
|
175
197
|
|
|
@@ -287,7 +309,10 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
287
309
|
// inverse environment rotation
|
|
288
310
|
envRotation3x3 = mat3( environmentRotation );
|
|
289
311
|
invEnvRotation3x3 = inverse( envRotation3x3 );
|
|
290
|
-
lightsDenom =
|
|
312
|
+
lightsDenom =
|
|
313
|
+
( environmentIntensity == 0.0 || envMapInfo.totalSum == 0.0 ) && lights.count != 0u ?
|
|
314
|
+
float( lights.count ) :
|
|
315
|
+
float( lights.count + 1u );
|
|
291
316
|
|
|
292
317
|
// final color
|
|
293
318
|
gl_FragColor = vec4( 0, 0, 0, 1 );
|
|
@@ -356,7 +381,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
356
381
|
|
|
357
382
|
if ( state.firstRay || state.transmissiveRay ) {
|
|
358
383
|
|
|
359
|
-
gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction,
|
|
384
|
+
gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction, rand2( 2 ) ) * state.throughputColor;
|
|
360
385
|
gl_FragColor.a = backgroundAlpha;
|
|
361
386
|
|
|
362
387
|
} else {
|
|
@@ -447,7 +472,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
447
472
|
}
|
|
448
473
|
|
|
449
474
|
scatterRec = bsdfSample( - ray.direction, surf );
|
|
450
|
-
state.isShadowRay = scatterRec.specularPdf <
|
|
475
|
+
state.isShadowRay = scatterRec.specularPdf < rand( 4 );
|
|
451
476
|
|
|
452
477
|
bool isBelowSurface = ! surf.volumeParticle && dot( scatterRec.direction, surf.faceNormal ) < 0.0;
|
|
453
478
|
vec3 hitPoint = stepRayOrigin( ray.origin, ray.direction, isBelowSurface ? - surf.faceNormal : surf.faceNormal, surfaceHit.dist );
|
|
@@ -517,7 +542,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
517
542
|
rrProb = sqrt( rrProb );
|
|
518
543
|
rrProb = max( rrProb, depthProb );
|
|
519
544
|
rrProb = min( rrProb, 1.0 );
|
|
520
|
-
if (
|
|
545
|
+
if ( rand( 8 ) > rrProb ) {
|
|
521
546
|
|
|
522
547
|
break;
|
|
523
548
|
|
|
@@ -8,6 +8,9 @@ export const attenuateHitGLSL = /* glsl */`
|
|
|
8
8
|
out vec3 color
|
|
9
9
|
) {
|
|
10
10
|
|
|
11
|
+
// store the original bounce index so we can reset it after
|
|
12
|
+
uint originalBounceIndex = sobolBounceIndex;
|
|
13
|
+
|
|
11
14
|
int traversals = state.traversals;
|
|
12
15
|
int transmissiveTraversals = state.transmissiveTraversals;
|
|
13
16
|
bool isShadowRay = state.isShadowRay;
|
|
@@ -20,22 +23,25 @@ export const attenuateHitGLSL = /* glsl */`
|
|
|
20
23
|
|
|
21
24
|
color = vec3( 1.0 );
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
// and then reset.
|
|
26
|
+
bool result = true;
|
|
25
27
|
for ( int i = 0; i < traversals; i ++ ) {
|
|
26
28
|
|
|
29
|
+
sobolBounceIndex ++;
|
|
30
|
+
|
|
27
31
|
int hitType = traceScene( ray, fogMaterial, surfaceHit );
|
|
28
32
|
|
|
29
33
|
if ( hitType == FOG_HIT ) {
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
result = true;
|
|
36
|
+
break;
|
|
32
37
|
|
|
33
38
|
} else if ( hitType == SURFACE_HIT ) {
|
|
34
39
|
|
|
35
40
|
float totalDist = distance( startPoint, ray.origin + ray.direction * surfaceHit.dist );
|
|
36
41
|
if ( totalDist > rayDist ) {
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
result = false;
|
|
44
|
+
break;
|
|
39
45
|
|
|
40
46
|
}
|
|
41
47
|
|
|
@@ -117,7 +123,7 @@ export const attenuateHitGLSL = /* glsl */`
|
|
|
117
123
|
bool useAlphaTest = alphaTest != 0.0;
|
|
118
124
|
float transmissionFactor = ( 1.0 - metalness ) * transmission;
|
|
119
125
|
if (
|
|
120
|
-
transmissionFactor < rand() && ! (
|
|
126
|
+
transmissionFactor < rand( 9 ) && ! (
|
|
121
127
|
// material sidedness
|
|
122
128
|
material.side != 0.0 && surfaceHit.side == material.side
|
|
123
129
|
|
|
@@ -125,11 +131,12 @@ export const attenuateHitGLSL = /* glsl */`
|
|
|
125
131
|
|| useAlphaTest && albedo.a < alphaTest
|
|
126
132
|
|
|
127
133
|
// opacity
|
|
128
|
-
|| material.transparent && ! useAlphaTest && albedo.a < rand()
|
|
134
|
+
|| material.transparent && ! useAlphaTest && albedo.a < rand( 10 )
|
|
129
135
|
)
|
|
130
136
|
) {
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
result = true;
|
|
139
|
+
break;
|
|
133
140
|
|
|
134
141
|
}
|
|
135
142
|
|
|
@@ -155,13 +162,16 @@ export const attenuateHitGLSL = /* glsl */`
|
|
|
155
162
|
|
|
156
163
|
} else {
|
|
157
164
|
|
|
158
|
-
|
|
165
|
+
result = false;
|
|
166
|
+
break;
|
|
159
167
|
|
|
160
168
|
}
|
|
161
169
|
|
|
162
170
|
}
|
|
163
171
|
|
|
164
|
-
|
|
172
|
+
// reset the bounce index
|
|
173
|
+
sobolBounceIndex = originalBounceIndex;
|
|
174
|
+
return result;
|
|
165
175
|
|
|
166
176
|
}
|
|
167
177
|
|
|
@@ -12,7 +12,7 @@ export const cameraUtilsGLSL = /* glsl */`
|
|
|
12
12
|
|
|
13
13
|
// Jitter the camera ray by finding a uv coordinate at a random sample
|
|
14
14
|
// around this pixel's UV coordinate for AA
|
|
15
|
-
vec2 ruv =
|
|
15
|
+
vec2 ruv = rand2( 0 );
|
|
16
16
|
vec2 jitteredUv = vUv + vec2( tentFilter( ruv.x ) * ssd.x, tentFilter( ruv.y ) * ssd.y );
|
|
17
17
|
Ray ray;
|
|
18
18
|
|
|
@@ -57,7 +57,7 @@ export const cameraUtilsGLSL = /* glsl */`
|
|
|
57
57
|
|
|
58
58
|
// get the aperture sample
|
|
59
59
|
// if blades === 0 then we assume a circle
|
|
60
|
-
vec3 shapeUVW=
|
|
60
|
+
vec3 shapeUVW= rand3( 1 );
|
|
61
61
|
int blades = physicalCamera.apertureBlades;
|
|
62
62
|
float anamorphicRatio = physicalCamera.anamorphicRatio;
|
|
63
63
|
vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularPolygon( blades, shapeUVW );
|
|
@@ -5,10 +5,10 @@ export const directLightContributionGLSL = /*glsl*/`
|
|
|
5
5
|
vec3 result = vec3( 0.0 );
|
|
6
6
|
|
|
7
7
|
// uniformly pick a light or environment map
|
|
8
|
-
if( lightsDenom != 0.0 &&
|
|
8
|
+
if( lightsDenom != 0.0 && rand( 5 ) < float( lights.count ) / lightsDenom ) {
|
|
9
9
|
|
|
10
10
|
// sample a light or environment
|
|
11
|
-
LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin,
|
|
11
|
+
LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, rand3( 6 ) );
|
|
12
12
|
|
|
13
13
|
bool isSampleBelowSurface = ! surf.volumeParticle && dot( surf.faceNormal, lightRec.direction ) < 0.0;
|
|
14
14
|
if ( isSampleBelowSurface ) {
|
|
@@ -43,11 +43,11 @@ export const directLightContributionGLSL = /*glsl*/`
|
|
|
43
43
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
} else {
|
|
46
|
+
} else if ( envMapInfo.totalSum != 0.0 && environmentIntensity != 0.0 ) {
|
|
47
47
|
|
|
48
48
|
// find a sample in the environment map to include in the contribution
|
|
49
49
|
vec3 envColor, envDirection;
|
|
50
|
-
float envPdf = sampleEquirectProbability(
|
|
50
|
+
float envPdf = sampleEquirectProbability( rand2( 7 ), envColor, envDirection );
|
|
51
51
|
envDirection = invEnvRotation3x3 * envDirection;
|
|
52
52
|
|
|
53
53
|
// this env sampling is not set up for transmissive sampling and yields overly bright
|
|
@@ -36,6 +36,7 @@ export const getSurfaceRecordGLSL = /* glsl */`
|
|
|
36
36
|
|
|
37
37
|
vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
|
|
38
38
|
albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
|
|
39
|
+
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
if ( material.vertexColors ) {
|
|
@@ -67,7 +68,7 @@ export const getSurfaceRecordGLSL = /* glsl */`
|
|
|
67
68
|
|| useAlphaTest && albedo.a < alphaTest
|
|
68
69
|
|
|
69
70
|
// opacity
|
|
70
|
-
|| material.transparent && ! useAlphaTest && albedo.a <
|
|
71
|
+
|| material.transparent && ! useAlphaTest && albedo.a < rand( 3 )
|
|
71
72
|
) {
|
|
72
73
|
|
|
73
74
|
return SKIP_SURFACE;
|
|
@@ -23,7 +23,7 @@ export const traceSceneGLSL = /* glsl */`
|
|
|
23
23
|
|
|
24
24
|
// offset the distance so we don't run into issues with particles on the same surface
|
|
25
25
|
// as other objects
|
|
26
|
-
float particleDist = intersectFogVolume( fogMaterial,
|
|
26
|
+
float particleDist = intersectFogVolume( fogMaterial, rand( 1 ) );
|
|
27
27
|
if ( particleDist + RAY_OFFSET < surfaceHit.dist ) {
|
|
28
28
|
|
|
29
29
|
surfaceHit.side = 1.0;
|