three-gpu-pathtracer 0.0.1 → 0.0.2

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.
@@ -1,23 +1,28 @@
1
- import { SAH } from 'three-mesh-bvh';
2
- import { GenerateMeshBVHWorker } from 'three-mesh-bvh/src/workers/GenerateMeshBVHWorker.js';
1
+ import { Mesh } from 'three';
2
+ import { SAH, MeshBVH, StaticGeometryGenerator } from 'three-mesh-bvh';
3
3
  import { mergeMeshes } from '../utils/GeometryPreparationUtils.js';
4
4
 
5
5
  export class PathTracingSceneGenerator {
6
6
 
7
- constructor() {
7
+ prepScene( scene ) {
8
8
 
9
- this.bvhGenerator = new GenerateMeshBVHWorker();
10
-
11
- }
12
-
13
- async generate( scene, options = {} ) {
14
-
15
- const { bvhGenerator } = this;
16
9
  const meshes = [];
17
-
18
10
  scene.traverse( c => {
19
11
 
20
- if ( c.isMesh ) {
12
+ if ( c.isSkinnedMesh || c.isMesh && c.morphTargetInfluences ) {
13
+
14
+ const generator = new StaticGeometryGenerator( c );
15
+ generator.applyWorldTransforms = false;
16
+ const mesh = new Mesh(
17
+ generator.generate(),
18
+ c.material,
19
+ );
20
+ mesh.matrixWorld.copy( c.matrixWorld );
21
+ mesh.matrix.copy( c.matrixWorld );
22
+ mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
23
+ meshes.push( mesh );
24
+
25
+ } else if ( c.isMesh ) {
21
26
 
22
27
  meshes.push( c );
23
28
 
@@ -25,22 +30,23 @@ export class PathTracingSceneGenerator {
25
30
 
26
31
  } );
27
32
 
28
- const { geometry, materials, textures } = mergeMeshes( meshes, { attributes: [ 'position', 'normal', 'tangent', 'uv' ] } );
29
- const bvhPromise = bvhGenerator.generate( geometry, { strategy: SAH, ...options, maxLeafTris: 1 } );
33
+ return mergeMeshes( meshes, {
34
+ attributes: [ 'position', 'normal', 'tangent', 'uv' ],
35
+ } );
30
36
 
37
+ }
38
+
39
+ generate( scene, options = {} ) {
40
+
41
+ const { materials, textures, geometry } = this.prepScene( scene );
42
+ const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
31
43
  return {
32
44
  scene,
33
45
  materials,
34
46
  textures,
35
- bvh: await bvhPromise,
47
+ bvh: new MeshBVH( geometry, bvhOptions ),
36
48
  };
37
49
 
38
50
  }
39
51
 
40
- dispose() {
41
-
42
- this.bvhGenerator.terminate();
43
-
44
- }
45
-
46
52
  }
@@ -0,0 +1,28 @@
1
+ import { PerspectiveCamera } from 'three';
2
+
3
+ export class PhysicalCamera extends PerspectiveCamera {
4
+
5
+ set bokehSize( size ) {
6
+
7
+ this.fStop = this.getFocalLength() / size;
8
+
9
+ }
10
+
11
+ get bokehSize() {
12
+
13
+ return this.getFocalLength() / this.fStop;
14
+
15
+ }
16
+
17
+ constructor( ...args ) {
18
+
19
+ super( ...args );
20
+ this.fStop = 1.4;
21
+ this.apertureBlades = 0;
22
+ this.apertureRotation = 0;
23
+ this.focusDistance = 25;
24
+ this.anamorphicRatio = 1;
25
+
26
+ }
27
+
28
+ }
package/src/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  // core
2
2
  export * from './core/PathTracingRenderer.js';
3
3
  export * from './core/PathTracingSceneGenerator.js';
4
+ export * from './core/DynamicPathTracingSceneGenerator.js';
4
5
  export * from './core/MaterialReducer.js';
6
+ export * from './core/PhysicalCamera.js';
5
7
 
6
8
  // uniforms
7
9
  export * from './uniforms/MaterialStructArrayUniform.js';
@@ -9,6 +9,7 @@ import { MaterialStructArrayUniform } from '../uniforms/MaterialStructArrayUnifo
9
9
  import { RenderTarget2DArray } from '../uniforms/RenderTarget2DArray.js';
10
10
  import { shaderMaterialSampling } from '../shader/shaderMaterialSampling.js';
11
11
  import { shaderUtils } from '../shader/shaderUtils.js';
12
+ import { PhysicalCameraUniform } from '../uniforms/PhysicalCameraUniform.js';
12
13
 
13
14
  export class PhysicalPathTracingMaterial extends MaterialBase {
14
15
 
@@ -27,13 +28,16 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
27
28
  depthWrite: false,
28
29
 
29
30
  defines: {
30
- BOUNCES: 3,
31
+ DOF_SUPPORT: 1,
31
32
  TRANSPARENT_TRAVERSALS: 5,
32
33
  MATERIAL_LENGTH: 0,
33
34
  GRADIENT_BG: 0,
34
35
  },
35
36
 
36
37
  uniforms: {
38
+ bounces: { value: 3 },
39
+ physicalCamera: { value: new PhysicalCameraUniform() },
40
+
37
41
  bvh: { value: new MeshBVHUniformStruct() },
38
42
  normalAttribute: { value: new FloatVertexAttributeTexture() },
39
43
  tangentAttribute: { value: new FloatVertexAttributeTexture() },
@@ -110,6 +114,14 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
110
114
 
111
115
  #endif
112
116
 
117
+ #if DOF_SUPPORT
118
+
119
+ uniform PhysicalCamera physicalCamera;
120
+
121
+ #endif
122
+
123
+ uniform int bounces;
124
+
113
125
  uniform mat4 cameraWorldMatrix;
114
126
  uniform mat4 invProjectionMatrix;
115
127
  uniform sampler2D normalAttribute;
@@ -134,6 +146,30 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
134
146
  vec3 rayOrigin, rayDirection;
135
147
  ndcToCameraRay( ndc, cameraWorldMatrix, invProjectionMatrix, rayOrigin, rayDirection );
136
148
 
149
+ #if DOF_SUPPORT
150
+
151
+ // depth of field
152
+ vec3 focalPoint = rayOrigin + normalize( rayDirection ) * physicalCamera.focusDistance;
153
+
154
+ // get the aperture sample
155
+ vec2 apertureSample = sampleAperture( physicalCamera.apertureBlades ) * physicalCamera.bokehSize * 0.5 * 1e-3;
156
+
157
+ // rotate the aperture shape
158
+ float ac = cos( physicalCamera.apertureRotation );
159
+ float as = sin( physicalCamera.apertureRotation );
160
+ apertureSample = vec2(
161
+ apertureSample.x * ac - apertureSample.y * as,
162
+ apertureSample.x * as + apertureSample.y * ac
163
+ );
164
+ apertureSample.x *= saturate( physicalCamera.anamorphicRatio );
165
+ apertureSample.y *= saturate( 1.0 / physicalCamera.anamorphicRatio );
166
+
167
+ #endif
168
+
169
+ // create the new ray
170
+ rayOrigin += ( cameraWorldMatrix * vec4( apertureSample, 0.0, 0.0 ) ).xyz;
171
+ rayDirection = focalPoint - rayOrigin;
172
+
137
173
  // Lambertian render
138
174
  gl_FragColor = vec4( 0.0 );
139
175
 
@@ -148,7 +184,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
148
184
  float accumulatedRoughness = 0.0;
149
185
  int i;
150
186
  int transparentTraversals = TRANSPARENT_TRAVERSALS;
151
- for ( i = 0; i < BOUNCES; i ++ ) {
187
+ for ( i = 0; i < bounces; i ++ ) {
152
188
 
153
189
  if ( ! bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
154
190
 
@@ -199,12 +235,23 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
199
235
 
200
236
  }
201
237
 
202
- // possibly skip this sample if it's transparent or alpha test is enabled
203
- // alpha test is disabled when it === 0
238
+ // possibly skip this sample if it's transparent, alpha test is enabled, or we hit the wrong material side
239
+ // and it's single sided.
240
+ // - alpha test is disabled when it === 0
241
+ // - the material sidedness test is complicated because we want light to pass through the back side but still
242
+ // be able to see the front side. This boolean checks if the side we hit is the front side on the first ray
243
+ // and we're rendering the other then we skip it. Do the opposite on subsequent bounces to get incoming light.
204
244
  float alphaTest = material.alphaTest;
205
245
  bool useAlphaTest = alphaTest != 0.0;
246
+ bool isFirstHit = i == 0;
206
247
  if (
207
- useAlphaTest && albedo.a < alphaTest
248
+ // material sidedness
249
+ material.side != 0.0 && ( side != material.side ) == isFirstHit
250
+
251
+ // alpha test
252
+ || useAlphaTest && albedo.a < alphaTest
253
+
254
+ // opacity
208
255
  || ! useAlphaTest && albedo.a < rand()
209
256
  ) {
210
257
 
@@ -288,7 +335,6 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
288
335
  SurfaceRec surfaceRec;
289
336
  surfaceRec.normal = normal;
290
337
  surfaceRec.faceNormal = faceNormal;
291
- surfaceRec.frontFace = side == 1.0;
292
338
  surfaceRec.transmission = transmission;
293
339
  surfaceRec.ior = material.ior;
294
340
  surfaceRec.emission = emission;
@@ -296,6 +342,10 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
296
342
  surfaceRec.color = albedo.rgb;
297
343
  surfaceRec.roughness = roughness;
298
344
 
345
+ // frontFace is used to determine transmissive properties and PDF. If no transmission is used
346
+ // then we can just always assume this is a front face.
347
+ surfaceRec.frontFace = side == 1.0 || transmission == 0.0;
348
+
299
349
  // Compute the filtered roughness value to use during specular reflection computations. A minimum
300
350
  // value of 1e-6 is needed because the GGX functions do not work with a roughness value of 0 and
301
351
  // the accumulated roughness value is scaled by a user setting and a "magic value" of 5.0.
@@ -1,5 +1,15 @@
1
1
  export const shaderMaterialStructs = /* glsl */ `
2
2
 
3
+ struct PhysicalCamera {
4
+
5
+ float focusDistance;
6
+ float anamorphicRatio;
7
+ float bokehSize;
8
+ int apertureBlades;
9
+ float apertureRotation;
10
+
11
+ };
12
+
3
13
  struct Material {
4
14
 
5
15
  vec3 color;
@@ -25,6 +35,8 @@ export const shaderMaterialStructs = /* glsl */ `
25
35
  float opacity;
26
36
  float alphaTest;
27
37
 
38
+ float side;
39
+
28
40
  };
29
41
 
30
42
  `;
@@ -98,6 +98,7 @@ export const shaderUtils = /* glsl */`
98
98
 
99
99
  }
100
100
 
101
+ // returns [ 0, 1 ]
101
102
  float rand() {
102
103
 
103
104
  pcg4d(s0);
@@ -137,4 +138,54 @@ export const shaderUtils = /* glsl */`
137
138
  return vec3( f * cos( t ), f * sin( t ), u );
138
139
 
139
140
  }
141
+
142
+ vec2 triangleSample( vec2 a, vec2 b, vec2 c ) {
143
+
144
+ // get the edges of the triangle and the diagonal across the
145
+ // center of the parallelogram
146
+ vec2 e1 = a - b;
147
+ vec2 e2 = c - b;
148
+ vec2 diag = normalize( e1 + e2 );
149
+
150
+ // pick a random point in the parallelogram
151
+ vec2 r = rand2();
152
+ if ( r.x + r.y > 1.0 ) {
153
+
154
+ r = vec2( 1.0 ) - r;
155
+
156
+ }
157
+
158
+ return e1 * r.x + e2 * r.y;
159
+
160
+ }
161
+
162
+ // samples an aperture shape with the given number of sides. 0 means circle
163
+ vec2 sampleAperture( int blades ) {
164
+
165
+ if ( blades == 0 ) {
166
+
167
+ vec2 r = rand2();
168
+ float angle = 2.0 * PI * r.x;
169
+ float radius = sqrt( rand() );
170
+ return vec2( cos( angle ), sin( angle ) ) * radius;
171
+
172
+ } else {
173
+
174
+ blades = max( blades, 3 );
175
+
176
+ vec3 r = rand3();
177
+ float anglePerSegment = 2.0 * PI / float( blades );
178
+ float segment = floor( float( blades ) * r.x );
179
+
180
+ float angle1 = anglePerSegment * segment;
181
+ float angle2 = angle1 + anglePerSegment;
182
+ vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
183
+ vec2 b = vec2( 0.0, 0.0 );
184
+ vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
185
+
186
+ return triangleSample( a, b, c );
187
+
188
+ }
189
+
190
+ }
140
191
  `;
@@ -1,4 +1,4 @@
1
- import { Color, Vector2 } from 'three';
1
+ import { Color, Vector2, DoubleSide, FrontSide, BackSide } from 'three';
2
2
  export class MaterialStructUniform {
3
3
 
4
4
  constructor() {
@@ -32,6 +32,8 @@ export class MaterialStructUniform {
32
32
  this.opacity = 1.0;
33
33
  this.alphaTest = 0.0;
34
34
 
35
+ this.side = 0;
36
+
35
37
  // TODO: Clearcoat
36
38
 
37
39
  // TODO: Sheen
@@ -91,4 +93,22 @@ export class MaterialStructUniform {
91
93
 
92
94
  }
93
95
 
96
+ setSide( side ) {
97
+
98
+ switch ( side ) {
99
+
100
+ case DoubleSide:
101
+ this.side = 0;
102
+ break;
103
+ case FrontSide:
104
+ this.side = 1;
105
+ break;
106
+ case BackSide:
107
+ this.side = - 1;
108
+ break;
109
+
110
+ }
111
+
112
+ }
113
+
94
114
  }
@@ -0,0 +1,36 @@
1
+ import { PhysicalCamera } from '../core/PhysicalCamera.js';
2
+ export class PhysicalCameraUniform {
3
+
4
+ constructor() {
5
+
6
+ this.bokehSize = 0;
7
+ this.apertureBlades = 0;
8
+ this.apertureRotation = 0;
9
+ this.focusDistance = 10;
10
+ this.anamorphicRatio = 1;
11
+
12
+ }
13
+
14
+ updateFrom( camera ) {
15
+
16
+ if ( camera instanceof PhysicalCamera ) {
17
+
18
+ this.bokehSize = camera.bokehSize;
19
+ this.apertureBlades = camera.apertureBlades;
20
+ this.apertureRotation = camera.apertureRotation;
21
+ this.focusDistance = camera.focusDistance;
22
+ this.anamorphicRatio = camera.anamorphicRatio;
23
+
24
+ } else {
25
+
26
+ this.bokehSize = 0;
27
+ this.apertureRotation = 0;
28
+ this.apertureBlades = 0;
29
+ this.focusDistance = 10;
30
+ this.anamorphicRatio = 1;
31
+
32
+ }
33
+
34
+ }
35
+
36
+ }