three-gpu-pathtracer 0.0.14 → 0.0.16

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.
Files changed (76) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1004 -981
  3. package/build/index.module.js +7413 -6902
  4. package/build/index.module.js.map +1 -1
  5. package/build/index.umd.cjs +7446 -6933
  6. package/build/index.umd.cjs.map +1 -1
  7. package/package.json +73 -73
  8. package/src/core/DynamicPathTracingSceneGenerator.js +119 -119
  9. package/src/core/MaterialReducer.js +256 -256
  10. package/src/core/PathTracingRenderer.js +346 -346
  11. package/src/core/PathTracingSceneGenerator.js +69 -69
  12. package/src/core/QuiltPathTracingRenderer.js +223 -223
  13. package/src/detectors/CompatibilityDetector.js +38 -0
  14. package/src/detectors/MaterialCompileDetector.js +50 -0
  15. package/src/detectors/PrecisionDetector.js +85 -0
  16. package/src/detectors/PrecisionMaterial.js +160 -0
  17. package/src/index.js +40 -36
  18. package/src/materials/MaterialBase.js +56 -56
  19. package/src/materials/debug/GraphMaterial.js +243 -243
  20. package/src/materials/fullscreen/AlphaDisplayMaterial.js +50 -48
  21. package/src/materials/fullscreen/BlendMaterial.js +67 -67
  22. package/src/materials/fullscreen/DenoiseMaterial.js +142 -142
  23. package/src/materials/fullscreen/GradientMapMaterial.js +82 -0
  24. package/src/materials/pathtracing/LambertPathTracingMaterial.js +296 -296
  25. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +118 -196
  26. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +177 -179
  27. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +84 -81
  28. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +93 -0
  29. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +323 -317
  30. package/src/materials/pathtracing/glsl/renderStructs.glsl.js +50 -0
  31. package/src/materials/pathtracing/glsl/traceScene.glsl.js +52 -54
  32. package/src/materials/surface/AmbientOcclusionMaterial.js +207 -207
  33. package/src/materials/surface/FogVolumeMaterial.js +23 -23
  34. package/src/objects/EquirectCamera.js +13 -13
  35. package/src/objects/PhysicalCamera.js +42 -28
  36. package/src/objects/PhysicalSpotLight.js +25 -14
  37. package/src/objects/ShapedAreaLight.js +22 -12
  38. package/src/shader/bsdf/bsdfSampling.glsl.js +499 -490
  39. package/src/shader/bsdf/fog.glsl.js +22 -23
  40. package/src/shader/bsdf/ggx.glsl.js +102 -102
  41. package/src/shader/bsdf/iridescence.glsl.js +135 -135
  42. package/src/shader/bsdf/sheen.glsl.js +98 -98
  43. package/src/shader/common/arraySamplerTexelFetch.glsl.js +25 -25
  44. package/src/shader/common/bvhAnyHit.glsl.js +76 -76
  45. package/src/shader/common/fresnel.glsl.js +98 -98
  46. package/src/shader/common/intersectShapes.glsl.js +62 -62
  47. package/src/shader/common/math.glsl.js +81 -81
  48. package/src/shader/common/utils.glsl.js +116 -116
  49. package/src/shader/rand/pcg.glsl.js +57 -57
  50. package/src/shader/rand/sobol.glsl.js +256 -256
  51. package/src/shader/sampling/equirectSampling.glsl.js +62 -62
  52. package/src/shader/sampling/lightSampling.glsl.js +223 -223
  53. package/src/shader/sampling/shapeSampling.glsl.js +86 -86
  54. package/src/shader/structs/cameraStruct.glsl.js +13 -13
  55. package/src/shader/structs/equirectStruct.glsl.js +13 -14
  56. package/src/shader/structs/fogMaterialBvh.glsl.js +62 -62
  57. package/src/shader/structs/lightsStruct.glsl.js +78 -78
  58. package/src/shader/structs/materialStruct.glsl.js +207 -207
  59. package/src/textures/GradientEquirectTexture.js +35 -35
  60. package/src/textures/ProceduralEquirectTexture.js +75 -75
  61. package/src/uniforms/AttributesTextureArray.js +35 -35
  62. package/src/uniforms/EquirectHdrInfoUniform.js +269 -277
  63. package/src/uniforms/FloatAttributeTextureArray.js +169 -169
  64. package/src/uniforms/IESProfilesTexture.js +100 -100
  65. package/src/uniforms/LightsInfoUniformStruct.js +212 -212
  66. package/src/uniforms/MaterialsTexture.js +503 -503
  67. package/src/uniforms/PhysicalCameraUniform.js +36 -36
  68. package/src/uniforms/RenderTarget2DArray.js +97 -97
  69. package/src/uniforms/utils.js +30 -30
  70. package/src/utils/BlurredEnvMapGenerator.js +116 -116
  71. package/src/utils/GeometryPreparationUtils.js +214 -214
  72. package/src/utils/IESLoader.js +325 -325
  73. package/src/utils/SobolNumberMapGenerator.js +80 -80
  74. package/src/utils/UVUnwrapper.js +101 -101
  75. package/src/utils/macroify.js +9 -9
  76. package/src/workers/PathTracingSceneWorker.js +42 -42
@@ -1,69 +1,69 @@
1
- import { Mesh } from 'three';
2
- import { SAH, MeshBVH, StaticGeometryGenerator } from 'three-mesh-bvh';
3
- import { mergeMeshes } from '../utils/GeometryPreparationUtils.js';
4
-
5
- export class PathTracingSceneGenerator {
6
-
7
- prepScene( scene ) {
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 );
35
-
36
- } else if ( c.isRectAreaLight || c.isSpotLight || c.isDirectionalLight || c.isPointLight ) {
37
-
38
- lights.push( c );
39
-
40
- }
41
-
42
- } );
43
-
44
- }
45
-
46
- return {
47
- ...mergeMeshes( meshes, {
48
- attributes: [ 'position', 'normal', 'tangent', 'uv', 'color' ],
49
- } ),
50
- lights,
51
- };
52
-
53
- }
54
-
55
- generate( scene, options = {} ) {
56
-
57
- const { materials, textures, geometry, lights } = this.prepScene( scene );
58
- const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
59
- return {
60
- scene,
61
- materials,
62
- textures,
63
- lights,
64
- bvh: new MeshBVH( geometry, bvhOptions ),
65
- };
66
-
67
- }
68
-
69
- }
1
+ import { Mesh } from 'three';
2
+ import { SAH, MeshBVH, StaticGeometryGenerator } from 'three-mesh-bvh';
3
+ import { mergeMeshes } from '../utils/GeometryPreparationUtils.js';
4
+
5
+ export class PathTracingSceneGenerator {
6
+
7
+ prepScene( scene ) {
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 );
35
+
36
+ } else if ( c.isRectAreaLight || c.isSpotLight || c.isDirectionalLight || c.isPointLight ) {
37
+
38
+ lights.push( c );
39
+
40
+ }
41
+
42
+ } );
43
+
44
+ }
45
+
46
+ return {
47
+ ...mergeMeshes( meshes, {
48
+ attributes: [ 'position', 'normal', 'tangent', 'uv', 'color' ],
49
+ } ),
50
+ lights,
51
+ };
52
+
53
+ }
54
+
55
+ generate( scene, options = {} ) {
56
+
57
+ const { materials, textures, geometry, lights } = this.prepScene( scene );
58
+ const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
59
+ return {
60
+ scene,
61
+ materials,
62
+ textures,
63
+ lights,
64
+ bvh: new MeshBVH( geometry, bvhOptions ),
65
+ };
66
+
67
+ }
68
+
69
+ }
@@ -1,223 +1,223 @@
1
- import { PerspectiveCamera, Vector3, MathUtils, Vector2, Matrix4, Vector4 } from 'three';
2
- import { PathTracingRenderer } from './PathTracingRenderer.js';
3
-
4
- function* _task( cb ) {
5
-
6
- const {
7
- viewCount,
8
- _camera,
9
- _quiltUtility,
10
- _subframe,
11
- } = this;
12
-
13
- const quiltViewInfo = {
14
- subframe: _subframe,
15
- projectionMatrix: _camera.projectionMatrix,
16
- offsetDirection: new Vector3(),
17
- };
18
-
19
- while ( true ) {
20
-
21
- for ( let i = 0; i < viewCount; i ++ ) {
22
-
23
- // get the camera info for the current view index
24
- _quiltUtility.near = this.camera.near;
25
- _quiltUtility.far = this.camera.far;
26
- _quiltUtility.getCameraViewInfo( i, quiltViewInfo );
27
-
28
- // transform offset into world frame from camera frame
29
- quiltViewInfo.offsetDirection.transformDirection( this.camera.matrixWorld );
30
-
31
- // adjust the render camera with the view offset
32
- this.camera.matrixWorld.decompose(
33
- _camera.position,
34
- _camera.quaternion,
35
- _camera.scale,
36
- );
37
- _camera.position.addScaledVector( quiltViewInfo.offsetDirection, quiltViewInfo.offset );
38
- _camera.updateMatrixWorld();
39
-
40
- // get the inverse projection
41
- _camera.projectionMatrixInverse
42
- .copy( _camera.projectionMatrix )
43
- .invert();
44
-
45
- this._opacityFactor = Math.floor( this._samples + 1 ) / Math.floor( this._quiltSamples + 1 );
46
-
47
- do {
48
-
49
- const ogCamera = this.camera;
50
- this.camera = _camera;
51
- cb();
52
- this.camera = ogCamera;
53
- yield;
54
-
55
- } while ( this._samples % 1 !== 0 );
56
-
57
- this._quiltSamples += 1 / viewCount;
58
-
59
- }
60
-
61
- this._quiltSamples = Math.round( this._quiltSamples );
62
-
63
- }
64
-
65
- }
66
-
67
- // Helper for extracting the camera projection, offset, and quilt subframe needed
68
- // for rendering a quilt with the provided parameters.
69
- class QuiltViewUtility {
70
-
71
- constructor() {
72
-
73
- this.viewCount = 48;
74
- this.quiltDimensions = new Vector2( 8, 6 );
75
- this.viewCone = 35 * MathUtils.DEG2RAD;
76
- this.viewFoV = 14 * MathUtils.DEG2RAD;
77
- this.displayDistance = 1;
78
- this.displayAspect = 0.75;
79
- this.near = 0.01;
80
- this.far = 10;
81
-
82
- }
83
-
84
- getCameraViewInfo( i, target = {} ) {
85
-
86
- const {
87
- quiltDimensions,
88
- viewCone,
89
- displayDistance,
90
- viewCount,
91
- viewFoV,
92
- displayAspect,
93
- near,
94
- far,
95
- } = this;
96
-
97
- // initialize defaults
98
- target.subframe = target.subframe || new Vector4();
99
- target.offsetDirection = target.offsetDirection || new Vector3();
100
- target.projectionMatrix = target.projectionMatrix || new Matrix4();
101
-
102
- // set camera offset
103
- const halfWidth = Math.tan( 0.5 * viewCone ) * displayDistance;
104
- const totalWidth = halfWidth * 2.0;
105
- const stride = totalWidth / ( viewCount - 1 );
106
- const offset = viewCount === 1 ? 0 : - halfWidth + stride * i;
107
- target.offsetDirection.set( 1.0, 0, 0 );
108
- target.offset = offset;
109
-
110
- // set the projection matrix
111
- const displayHalfHeight = Math.tan( viewFoV * 0.5 ) * displayDistance;
112
- const displayHalfWidth = displayAspect * displayHalfHeight;
113
- const nearScale = near / displayDistance;
114
-
115
- target.projectionMatrix.makePerspective(
116
- nearScale * ( - displayHalfWidth - offset ), nearScale * ( displayHalfWidth - offset ),
117
- nearScale * displayHalfHeight, nearScale * - displayHalfHeight,
118
- near, far,
119
- );
120
-
121
- // set the quilt subframe
122
- const x = i % quiltDimensions.x;
123
- const y = Math.floor( i / quiltDimensions.x );
124
-
125
- const qw = 1 / quiltDimensions.x;
126
- const qh = 1 / quiltDimensions.y;
127
- target.subframe.set( x * qw, y * qh, qw, qh );
128
-
129
- return target;
130
-
131
- }
132
-
133
- setFromDisplayView( viewerDistance, displayWidth, displayHeight ) {
134
-
135
- this.displayAspect = displayWidth / displayHeight;
136
- this.displayDistance = viewerDistance;
137
- this.viewFoV = 2.0 * Math.atan( 0.5 * displayHeight / viewerDistance );
138
-
139
- }
140
-
141
- }
142
-
143
- export class QuiltPathTracingRenderer extends PathTracingRenderer {
144
-
145
- get samples() {
146
-
147
- return this._samples / this.viewCount;
148
-
149
- }
150
-
151
- constructor( ...args ) {
152
-
153
- super( ...args );
154
-
155
- [
156
- 'quiltDimensions',
157
- 'viewCount',
158
- 'viewCone',
159
- 'viewFoV',
160
- 'displayDistance',
161
- 'displayAspect',
162
- ].forEach( member => {
163
-
164
- Object.defineProperty( this, member, {
165
-
166
- enumerable: true,
167
-
168
- set: v => {
169
-
170
- this._quiltUtility[ member ] = v;
171
-
172
- },
173
-
174
- get: () => {
175
-
176
- return this._quiltUtility[ member ];
177
-
178
- }
179
-
180
- } );
181
-
182
- } );
183
-
184
-
185
- this._quiltUtility = new QuiltViewUtility();
186
- this._quiltSamples = 0;
187
- this._camera = new PerspectiveCamera();
188
- this._quiltTask = null;
189
-
190
- }
191
-
192
- setFromDisplayView( ...args ) {
193
-
194
- this._quiltUtility.setFromDisplayView( ...args );
195
-
196
- }
197
-
198
- update() {
199
-
200
- this.alpha = false;
201
- if ( ! this._quiltTask ) {
202
-
203
- this._quiltTask = _task.call( this, () => {
204
-
205
- super.update();
206
-
207
- } );
208
-
209
- }
210
-
211
- this._quiltTask.next();
212
-
213
- }
214
-
215
- reset() {
216
-
217
- super.reset();
218
- this._quiltTask = null;
219
- this._quiltSamples = 0;
220
-
221
- }
222
-
223
- }
1
+ import { PerspectiveCamera, Vector3, MathUtils, Vector2, Matrix4, Vector4 } from 'three';
2
+ import { PathTracingRenderer } from './PathTracingRenderer.js';
3
+
4
+ function* _task( cb ) {
5
+
6
+ const {
7
+ viewCount,
8
+ _camera,
9
+ _quiltUtility,
10
+ _subframe,
11
+ } = this;
12
+
13
+ const quiltViewInfo = {
14
+ subframe: _subframe,
15
+ projectionMatrix: _camera.projectionMatrix,
16
+ offsetDirection: new Vector3(),
17
+ };
18
+
19
+ while ( true ) {
20
+
21
+ for ( let i = 0; i < viewCount; i ++ ) {
22
+
23
+ // get the camera info for the current view index
24
+ _quiltUtility.near = this.camera.near;
25
+ _quiltUtility.far = this.camera.far;
26
+ _quiltUtility.getCameraViewInfo( i, quiltViewInfo );
27
+
28
+ // transform offset into world frame from camera frame
29
+ quiltViewInfo.offsetDirection.transformDirection( this.camera.matrixWorld );
30
+
31
+ // adjust the render camera with the view offset
32
+ this.camera.matrixWorld.decompose(
33
+ _camera.position,
34
+ _camera.quaternion,
35
+ _camera.scale,
36
+ );
37
+ _camera.position.addScaledVector( quiltViewInfo.offsetDirection, quiltViewInfo.offset );
38
+ _camera.updateMatrixWorld();
39
+
40
+ // get the inverse projection
41
+ _camera.projectionMatrixInverse
42
+ .copy( _camera.projectionMatrix )
43
+ .invert();
44
+
45
+ this._opacityFactor = Math.floor( this._samples + 1 ) / Math.floor( this._quiltSamples + 1 );
46
+
47
+ do {
48
+
49
+ const ogCamera = this.camera;
50
+ this.camera = _camera;
51
+ cb();
52
+ this.camera = ogCamera;
53
+ yield;
54
+
55
+ } while ( this._samples % 1 !== 0 );
56
+
57
+ this._quiltSamples += 1 / viewCount;
58
+
59
+ }
60
+
61
+ this._quiltSamples = Math.round( this._quiltSamples );
62
+
63
+ }
64
+
65
+ }
66
+
67
+ // Helper for extracting the camera projection, offset, and quilt subframe needed
68
+ // for rendering a quilt with the provided parameters.
69
+ class QuiltViewUtility {
70
+
71
+ constructor() {
72
+
73
+ this.viewCount = 48;
74
+ this.quiltDimensions = new Vector2( 8, 6 );
75
+ this.viewCone = 35 * MathUtils.DEG2RAD;
76
+ this.viewFoV = 14 * MathUtils.DEG2RAD;
77
+ this.displayDistance = 1;
78
+ this.displayAspect = 0.75;
79
+ this.near = 0.01;
80
+ this.far = 10;
81
+
82
+ }
83
+
84
+ getCameraViewInfo( i, target = {} ) {
85
+
86
+ const {
87
+ quiltDimensions,
88
+ viewCone,
89
+ displayDistance,
90
+ viewCount,
91
+ viewFoV,
92
+ displayAspect,
93
+ near,
94
+ far,
95
+ } = this;
96
+
97
+ // initialize defaults
98
+ target.subframe = target.subframe || new Vector4();
99
+ target.offsetDirection = target.offsetDirection || new Vector3();
100
+ target.projectionMatrix = target.projectionMatrix || new Matrix4();
101
+
102
+ // set camera offset
103
+ const halfWidth = Math.tan( 0.5 * viewCone ) * displayDistance;
104
+ const totalWidth = halfWidth * 2.0;
105
+ const stride = totalWidth / ( viewCount - 1 );
106
+ const offset = viewCount === 1 ? 0 : - halfWidth + stride * i;
107
+ target.offsetDirection.set( 1.0, 0, 0 );
108
+ target.offset = offset;
109
+
110
+ // set the projection matrix
111
+ const displayHalfHeight = Math.tan( viewFoV * 0.5 ) * displayDistance;
112
+ const displayHalfWidth = displayAspect * displayHalfHeight;
113
+ const nearScale = near / displayDistance;
114
+
115
+ target.projectionMatrix.makePerspective(
116
+ nearScale * ( - displayHalfWidth - offset ), nearScale * ( displayHalfWidth - offset ),
117
+ nearScale * displayHalfHeight, nearScale * - displayHalfHeight,
118
+ near, far,
119
+ );
120
+
121
+ // set the quilt subframe
122
+ const x = i % quiltDimensions.x;
123
+ const y = Math.floor( i / quiltDimensions.x );
124
+
125
+ const qw = 1 / quiltDimensions.x;
126
+ const qh = 1 / quiltDimensions.y;
127
+ target.subframe.set( x * qw, y * qh, qw, qh );
128
+
129
+ return target;
130
+
131
+ }
132
+
133
+ setFromDisplayView( viewerDistance, displayWidth, displayHeight ) {
134
+
135
+ this.displayAspect = displayWidth / displayHeight;
136
+ this.displayDistance = viewerDistance;
137
+ this.viewFoV = 2.0 * Math.atan( 0.5 * displayHeight / viewerDistance );
138
+
139
+ }
140
+
141
+ }
142
+
143
+ export class QuiltPathTracingRenderer extends PathTracingRenderer {
144
+
145
+ get samples() {
146
+
147
+ return this._samples / this.viewCount;
148
+
149
+ }
150
+
151
+ constructor( ...args ) {
152
+
153
+ super( ...args );
154
+
155
+ [
156
+ 'quiltDimensions',
157
+ 'viewCount',
158
+ 'viewCone',
159
+ 'viewFoV',
160
+ 'displayDistance',
161
+ 'displayAspect',
162
+ ].forEach( member => {
163
+
164
+ Object.defineProperty( this, member, {
165
+
166
+ enumerable: true,
167
+
168
+ set: v => {
169
+
170
+ this._quiltUtility[ member ] = v;
171
+
172
+ },
173
+
174
+ get: () => {
175
+
176
+ return this._quiltUtility[ member ];
177
+
178
+ }
179
+
180
+ } );
181
+
182
+ } );
183
+
184
+
185
+ this._quiltUtility = new QuiltViewUtility();
186
+ this._quiltSamples = 0;
187
+ this._camera = new PerspectiveCamera();
188
+ this._quiltTask = null;
189
+
190
+ }
191
+
192
+ setFromDisplayView( ...args ) {
193
+
194
+ this._quiltUtility.setFromDisplayView( ...args );
195
+
196
+ }
197
+
198
+ update() {
199
+
200
+ this.alpha = false;
201
+ if ( ! this._quiltTask ) {
202
+
203
+ this._quiltTask = _task.call( this, () => {
204
+
205
+ super.update();
206
+
207
+ } );
208
+
209
+ }
210
+
211
+ this._quiltTask.next();
212
+
213
+ }
214
+
215
+ reset() {
216
+
217
+ super.reset();
218
+ this._quiltTask = null;
219
+ this._quiltSamples = 0;
220
+
221
+ }
222
+
223
+ }
@@ -0,0 +1,38 @@
1
+ import { PrecisionDetector } from './PrecisionDetector.js';
2
+ import { MaterialCompileDetector } from './MaterialCompileDetector.js';
3
+ export class CompatibilityDetector {
4
+
5
+ constructor( renderer, material ) {
6
+
7
+ this._renderer = renderer;
8
+ this._material = material;
9
+
10
+ }
11
+
12
+ detect() {
13
+
14
+ let detector = new PrecisionDetector( this._renderer );
15
+ let result = detector.detect();
16
+ if ( ! result.pass ) {
17
+
18
+ return result;
19
+
20
+ }
21
+
22
+ detector = new MaterialCompileDetector( this._renderer );
23
+ result = detector.detect( this._material );
24
+ if ( ! result.pass ) {
25
+
26
+ return result;
27
+
28
+ }
29
+
30
+ return {
31
+ detail: {},
32
+ pass: true,
33
+ message: '',
34
+ };
35
+
36
+ }
37
+
38
+ }
@@ -0,0 +1,50 @@
1
+ import { Mesh, BoxGeometry, PerspectiveCamera } from 'three';
2
+
3
+ // Returns whether a material can be compiled and run on the current device or not
4
+ export class MaterialCompileDetector {
5
+
6
+ constructor( renderer ) {
7
+
8
+ this._renderer = renderer;
9
+
10
+ }
11
+
12
+ detect( material ) {
13
+
14
+ const renderer = this._renderer;
15
+ const mesh = new Mesh( new BoxGeometry(), material );
16
+ const camera = new PerspectiveCamera();
17
+ const ogShaderErrors = renderer.debug.checkShaderErrors;
18
+ renderer.debug.checkShaderErrors = true;
19
+
20
+ const programs = renderer.info.programs;
21
+ const progLength = programs.length;
22
+ renderer.compile( mesh, camera );
23
+
24
+ renderer.debug.checkShaderErrors = ogShaderErrors;
25
+ mesh.geometry.dispose();
26
+
27
+ if ( programs.length === progLength ) {
28
+
29
+ return {
30
+ detail: null,
31
+ pass: true,
32
+ message: 'Cannot determine shader compilation status if material has already been used.',
33
+ };
34
+
35
+ } else {
36
+
37
+ const program = programs[ programs.length - 1 ];
38
+ const pass = program.diagnostics ? program.diagnostics.runnable : true;
39
+ const message = pass ? '' : `Cannot render ${ material.type } on this device.`;
40
+ return {
41
+ detail: {},
42
+ pass,
43
+ message,
44
+ };
45
+
46
+ }
47
+
48
+ }
49
+
50
+ }