@plastic-software/three 0.179.0 → 0.180.1

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 (173) hide show
  1. package/build/three.cjs +167 -107
  2. package/build/three.core.js +112 -20
  3. package/build/three.core.min.js +1 -1
  4. package/build/three.module.js +56 -90
  5. package/build/three.module.min.js +1 -1
  6. package/build/three.tsl.js +30 -5
  7. package/build/three.tsl.min.js +1 -1
  8. package/build/three.webgpu.js +2896 -1048
  9. package/build/three.webgpu.min.js +1 -1
  10. package/build/three.webgpu.nodes.js +2896 -1048
  11. package/build/three.webgpu.nodes.min.js +1 -1
  12. package/examples/jsm/Addons.js +1 -1
  13. package/examples/jsm/controls/ArcballControls.js +7 -7
  14. package/examples/jsm/controls/DragControls.js +6 -56
  15. package/examples/jsm/controls/FirstPersonControls.js +2 -2
  16. package/examples/jsm/controls/PointerLockControls.js +0 -8
  17. package/examples/jsm/exporters/GLTFExporter.js +30 -22
  18. package/examples/jsm/exporters/KTX2Exporter.js +4 -2
  19. package/examples/jsm/exporters/PLYExporter.js +1 -1
  20. package/examples/jsm/libs/ktx-parse.module.js +1 -1
  21. package/examples/jsm/lines/Line2.js +3 -3
  22. package/examples/jsm/lines/LineGeometry.js +1 -1
  23. package/examples/jsm/lines/LineSegments2.js +2 -2
  24. package/examples/jsm/lines/Wireframe.js +2 -2
  25. package/examples/jsm/lines/WireframeGeometry2.js +1 -1
  26. package/examples/jsm/lines/webgpu/LineSegments2.js +1 -1
  27. package/examples/jsm/lines/webgpu/Wireframe.js +1 -1
  28. package/examples/jsm/loaders/ColladaLoader.js +1 -1
  29. package/examples/jsm/loaders/EXRLoader.js +5 -5
  30. package/examples/jsm/loaders/GLTFLoader.js +9 -5
  31. package/examples/jsm/loaders/HDRCubeTextureLoader.js +5 -5
  32. package/examples/jsm/loaders/HDRLoader.js +486 -0
  33. package/examples/jsm/loaders/KTX2Loader.js +112 -32
  34. package/examples/jsm/loaders/RGBELoader.js +7 -473
  35. package/examples/jsm/loaders/TTFLoader.js +4 -4
  36. package/examples/jsm/loaders/UltraHDRLoader.js +1 -1
  37. package/examples/jsm/loaders/lwo/IFFParser.js +1 -1
  38. package/examples/jsm/materials/WoodNodeMaterial.js +533 -0
  39. package/examples/jsm/math/ColorSpaces.js +19 -1
  40. package/examples/jsm/math/ConvexHull.js +2 -2
  41. package/examples/jsm/math/Lut.js +2 -2
  42. package/examples/jsm/misc/MD2CharacterComplex.js +1 -1
  43. package/examples/jsm/misc/ProgressiveLightMap.js +1 -1
  44. package/examples/jsm/misc/Volume.js +1 -1
  45. package/examples/jsm/postprocessing/OutlinePass.js +1 -1
  46. package/examples/jsm/postprocessing/SSRPass.js +37 -8
  47. package/examples/jsm/shaders/UnpackDepthRGBAShader.js +1 -1
  48. package/examples/jsm/transpiler/GLSLDecoder.js +22 -19
  49. package/examples/jsm/transpiler/TSLEncoder.js +2 -10
  50. package/examples/jsm/transpiler/WGSLEncoder.js +24 -0
  51. package/examples/jsm/tsl/display/AnamorphicNode.js +27 -4
  52. package/examples/jsm/tsl/display/BloomNode.js +3 -3
  53. package/examples/jsm/tsl/display/ChromaticAberrationNode.js +2 -1
  54. package/examples/jsm/tsl/display/DepthOfFieldNode.js +439 -90
  55. package/examples/jsm/tsl/display/GTAONode.js +8 -0
  56. package/examples/jsm/tsl/display/GaussianBlurNode.js +47 -35
  57. package/examples/jsm/tsl/display/OutlineNode.js +2 -2
  58. package/examples/jsm/tsl/display/SSRNode.js +180 -65
  59. package/examples/jsm/tsl/display/TRAANode.js +1 -1
  60. package/examples/jsm/tsl/display/boxBlur.js +64 -0
  61. package/examples/jsm/tsl/display/hashBlur.js +15 -18
  62. package/examples/jsm/utils/BufferGeometryUtils.js +1 -1
  63. package/examples/jsm/utils/ShadowMapViewerGPU.js +12 -5
  64. package/examples/jsm/webxr/OculusHandModel.js +1 -1
  65. package/package.json +1 -1
  66. package/src/Three.Core.js +1 -0
  67. package/src/Three.TSL.js +29 -4
  68. package/src/animation/AnimationClip.js +17 -2
  69. package/src/constants.js +11 -3
  70. package/src/core/BufferGeometry.js +2 -2
  71. package/src/extras/TextureUtils.js +2 -1
  72. package/src/extras/lib/earcut.js +1 -1
  73. package/src/lights/webgpu/ProjectorLight.js +1 -1
  74. package/src/materials/Material.js +12 -0
  75. package/src/materials/MeshDistanceMaterial.js +1 -1
  76. package/src/materials/nodes/PointsNodeMaterial.js +81 -28
  77. package/src/materials/nodes/SpriteNodeMaterial.js +3 -15
  78. package/src/materials/nodes/manager/NodeMaterialObserver.js +1 -1
  79. package/src/math/ColorManagement.js +7 -1
  80. package/src/nodes/Nodes.js +3 -0
  81. package/src/nodes/TSL.js +3 -0
  82. package/src/nodes/accessors/BufferNode.js +1 -1
  83. package/src/nodes/accessors/Camera.js +133 -7
  84. package/src/nodes/accessors/ClippingNode.js +6 -5
  85. package/src/nodes/accessors/CubeTextureNode.js +2 -2
  86. package/src/nodes/accessors/InstanceNode.js +3 -1
  87. package/src/nodes/accessors/Object3DNode.js +1 -1
  88. package/src/nodes/accessors/ReferenceBaseNode.js +1 -1
  89. package/src/nodes/accessors/ReferenceNode.js +1 -1
  90. package/src/nodes/accessors/Texture3DNode.js +13 -0
  91. package/src/nodes/accessors/TextureNode.js +71 -19
  92. package/src/nodes/code/FunctionCallNode.js +19 -0
  93. package/src/nodes/code/FunctionNode.js +23 -0
  94. package/src/nodes/core/AssignNode.js +4 -3
  95. package/src/nodes/core/ContextNode.js +24 -0
  96. package/src/nodes/core/Node.js +16 -20
  97. package/src/nodes/core/NodeBuilder.js +48 -14
  98. package/src/nodes/core/NodeFrame.js +1 -1
  99. package/src/nodes/core/NodeUniform.js +1 -1
  100. package/src/nodes/core/NodeUtils.js +1 -2
  101. package/src/nodes/core/StackNode.js +29 -4
  102. package/src/nodes/core/StructNode.js +5 -5
  103. package/src/nodes/core/StructTypeNode.js +1 -0
  104. package/src/nodes/core/SubBuildNode.js +2 -2
  105. package/src/nodes/core/UniformNode.js +16 -9
  106. package/src/nodes/core/VarNode.js +0 -21
  107. package/src/nodes/display/FrontFacingNode.js +4 -8
  108. package/src/nodes/display/PassNode.js +1 -1
  109. package/src/nodes/display/ScreenNode.js +42 -13
  110. package/src/nodes/display/ViewportDepthTextureNode.js +16 -4
  111. package/src/nodes/display/ViewportSharedTextureNode.js +12 -0
  112. package/src/nodes/display/ViewportTextureNode.js +42 -12
  113. package/src/nodes/gpgpu/SubgroupFunctionNode.js +430 -0
  114. package/src/nodes/lighting/LightsNode.js +1 -1
  115. package/src/nodes/math/BitcastNode.js +156 -0
  116. package/src/nodes/math/ConditionalNode.js +18 -2
  117. package/src/nodes/math/MathNode.js +3 -15
  118. package/src/nodes/math/OperatorNode.js +4 -3
  119. package/src/nodes/tsl/TSLCore.js +432 -152
  120. package/src/nodes/utils/JoinNode.js +3 -1
  121. package/src/nodes/utils/MemberNode.js +58 -7
  122. package/src/nodes/utils/RTTNode.js +1 -1
  123. package/src/nodes/utils/ReflectorNode.js +51 -7
  124. package/src/nodes/utils/SampleNode.js +12 -2
  125. package/src/nodes/utils/SplitNode.js +11 -0
  126. package/src/nodes/utils/Timer.js +0 -47
  127. package/src/objects/BatchedMesh.js +2 -2
  128. package/src/objects/LOD.js +1 -1
  129. package/src/objects/Sprite.js +2 -2
  130. package/src/renderers/WebGLRenderer.js +1 -10
  131. package/src/renderers/common/Attributes.js +1 -1
  132. package/src/renderers/common/Backend.js +19 -1
  133. package/src/renderers/common/Bindings.js +2 -0
  134. package/src/renderers/common/ChainMap.js +1 -1
  135. package/src/renderers/common/DataMap.js +1 -1
  136. package/src/renderers/common/Pipelines.js +1 -1
  137. package/src/renderers/common/RenderContext.js +2 -2
  138. package/src/renderers/common/RenderObject.js +14 -2
  139. package/src/renderers/common/Renderer.js +39 -19
  140. package/src/renderers/common/SampledTexture.js +1 -1
  141. package/src/renderers/common/Sampler.js +25 -13
  142. package/src/renderers/common/Textures.js +34 -12
  143. package/src/renderers/common/TimestampQueryPool.js +3 -3
  144. package/src/renderers/common/XRManager.js +35 -19
  145. package/src/renderers/common/nodes/NodeBuilderState.js +1 -1
  146. package/src/renderers/common/nodes/NodeLibrary.js +5 -5
  147. package/src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl.js +1 -1
  148. package/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl.js +1 -1
  149. package/src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl.js +1 -1
  150. package/src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl.js +1 -1
  151. package/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +7 -15
  152. package/src/renderers/shaders/ShaderLib/depth.glsl.js +1 -1
  153. package/src/renderers/webgl/WebGLProgram.js +4 -4
  154. package/src/renderers/webgl/WebGLShadowMap.js +1 -1
  155. package/src/renderers/webgl/WebGLTextures.js +1 -0
  156. package/src/renderers/webgl/WebGLUtils.js +3 -2
  157. package/src/renderers/webgl-fallback/WebGLBackend.js +186 -135
  158. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +110 -17
  159. package/src/renderers/webgl-fallback/utils/WebGLState.js +1 -1
  160. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +52 -3
  161. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +9 -10
  162. package/src/renderers/webgl-fallback/utils/WebGLUtils.js +3 -2
  163. package/src/renderers/webgpu/WebGPUBackend.js +35 -31
  164. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +124 -34
  165. package/src/renderers/webgpu/utils/WebGPUConstants.js +2 -2
  166. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +9 -18
  167. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +114 -25
  168. package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +3 -3
  169. package/src/renderers/webxr/WebXRManager.js +39 -24
  170. package/src/textures/ExternalTexture.js +15 -4
  171. package/src/textures/Source.js +1 -1
  172. package/src/textures/VideoTexture.js +0 -3
  173. package/examples/jsm/loaders/RGBMLoader.js +0 -1148
@@ -1,9 +1,17 @@
1
- import { TempNode, NodeUpdateType } from 'three/webgpu';
2
- import { convertToTexture, nodeObject, Fn, uv, uniform, vec2, vec4, clamp } from 'three/tsl';
1
+ import { TempNode, NodeMaterial, NodeUpdateType, RenderTarget, Vector2, HalfFloatType, RedFormat, QuadMesh, RendererUtils } from 'three/webgpu';
2
+ import { convertToTexture, nodeObject, Fn, uniform, smoothstep, step, texture, max, uniformArray, outputStruct, property, vec4, vec3, uv, Loop, min, mix } from 'three/tsl';
3
+ import { gaussianBlur } from './GaussianBlurNode.js';
4
+
5
+ const _quadMesh = /*@__PURE__*/ new QuadMesh();
6
+ let _rendererState;
3
7
 
4
8
  /**
5
9
  * Post processing node for creating depth of field (DOF) effect.
6
10
  *
11
+ * References:
12
+ * - {@link https://pixelmischiefblog.wordpress.com/2016/11/25/bokeh-depth-of-field/}
13
+ * - {@link https://www.adriancourreges.com/blog/2016/09/09/doom-2016-graphics-study/}
14
+ *
7
15
  * @augments TempNode
8
16
  * @three_import import { dof } from 'three/addons/tsl/display/DepthOfFieldNode.js';
9
17
  */
@@ -20,11 +28,11 @@ class DepthOfFieldNode extends TempNode {
20
28
  *
21
29
  * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
22
30
  * @param {Node<float>} viewZNode - Represents the viewZ depth values of the scene.
23
- * @param {Node<float>} focusNode - Defines the effect's focus which is the distance along the camera's look direction in world units.
24
- * @param {Node<float>} apertureNode - Defines the effect's aperture.
25
- * @param {Node<float>} maxblurNode - Defines the effect's maximum blur.
31
+ * @param {Node<float>} focusDistanceNode - Defines the effect's focus which is the distance along the camera's look direction in world units.
32
+ * @param {Node<float>} focalLengthNode - How far an object can be from the focal plane before it goes completely out-of-focus in world units.
33
+ * @param {Node<float>} bokehScaleNode - A unitless value for artistic purposes to adjust the size of the bokeh.
26
34
  */
27
- constructor( textureNode, viewZNode, focusNode, apertureNode, maxblurNode ) {
35
+ constructor( textureNode, viewZNode, focusDistanceNode, focalLengthNode, bokehScaleNode ) {
28
36
 
29
37
  super( 'vec4' );
30
38
 
@@ -47,29 +55,164 @@ class DepthOfFieldNode extends TempNode {
47
55
  *
48
56
  * @type {Node<float>}
49
57
  */
50
- this.focusNode = focusNode;
58
+ this.focusDistanceNode = focusDistanceNode;
51
59
 
52
60
  /**
53
- * Defines the effect's aperture.
61
+ * How far an object can be from the focal plane before it goes completely out-of-focus in world units.
54
62
  *
55
63
  * @type {Node<float>}
56
64
  */
57
- this.apertureNode = apertureNode;
65
+ this.focalLengthNode = focalLengthNode;
58
66
 
59
67
  /**
60
- * Defines the effect's maximum blur.
68
+ * A unitless value for artistic purposes to adjust the size of the bokeh.
61
69
  *
62
70
  * @type {Node<float>}
63
71
  */
64
- this.maxblurNode = maxblurNode;
72
+ this.bokehScaleNode = bokehScaleNode;
73
+
74
+ /**
75
+ * The inverse size of the resolution.
76
+ *
77
+ * @private
78
+ * @type {UniformNode<vec2>}
79
+ */
80
+ this._invSize = uniform( new Vector2() );
81
+
82
+ /**
83
+ * The render target used for the near and far field.
84
+ *
85
+ * @private
86
+ * @type {RenderTarget}
87
+ */
88
+ this._CoCRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType, format: RedFormat, count: 2 } );
89
+ this._CoCRT.textures[ 0 ].name = 'DepthOfField.NearField';
90
+ this._CoCRT.textures[ 1 ].name = 'DepthOfField.FarField';
91
+
92
+ /**
93
+ * The render target used for blurring the near field.
94
+ *
95
+ * @private
96
+ * @type {RenderTarget}
97
+ */
98
+ this._CoCBlurredRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType, format: RedFormat } );
99
+ this._CoCBlurredRT.texture.name = 'DepthOfField.NearFieldBlurred';
100
+
101
+ /**
102
+ * The render target used for the first blur pass.
103
+ *
104
+ * @private
105
+ * @type {RenderTarget}
106
+ */
107
+ this._blur64RT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
108
+ this._blur64RT.texture.name = 'DepthOfField.Blur64';
109
+
110
+ /**
111
+ * The render target used for the near field's second blur pass.
112
+ *
113
+ * @private
114
+ * @type {RenderTarget}
115
+ */
116
+ this._blur16NearRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
117
+ this._blur16NearRT.texture.name = 'DepthOfField.Blur16Near';
118
+
119
+ /**
120
+ * The render target used for the far field's second blur pass.
121
+ *
122
+ * @private
123
+ * @type {RenderTarget}
124
+ */
125
+ this._blur16FarRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
126
+ this._blur16FarRT.texture.name = 'DepthOfField.Blur16Far';
127
+
128
+ /**
129
+ * The render target used for the composite
130
+ *
131
+ * @private
132
+ * @type {RenderTarget}
133
+ */
134
+ this._compositeRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
135
+ this._compositeRT.texture.name = 'DepthOfField.Composite';
136
+
137
+ /**
138
+ * The material used for the CoC/near and far fields.
139
+ *
140
+ * @private
141
+ * @type {NodeMaterial}
142
+ */
143
+ this._CoCMaterial = new NodeMaterial();
144
+
145
+ /**
146
+ * The material used for blurring the near field.
147
+ *
148
+ * @private
149
+ * @type {NodeMaterial}
150
+ */
151
+ this._CoCBlurredMaterial = new NodeMaterial();
152
+
153
+ /**
154
+ * The material used for the 64 tap blur.
155
+ *
156
+ * @private
157
+ * @type {NodeMaterial}
158
+ */
159
+ this._blur64Material = new NodeMaterial();
160
+
161
+ /**
162
+ * The material used for the 16 tap blur.
163
+ *
164
+ * @private
165
+ * @type {NodeMaterial}
166
+ */
167
+ this._blur16Material = new NodeMaterial();
168
+
169
+ /**
170
+ * The material used for the final composite.
171
+ *
172
+ * @private
173
+ * @type {NodeMaterial}
174
+ */
175
+ this._compositeMaterial = new NodeMaterial();
176
+
177
+ /**
178
+ * The result of the effect is represented as a separate texture node.
179
+ *
180
+ * @private
181
+ * @type {TextureNode}
182
+ */
183
+ this._textureNode = texture( this._compositeRT.texture );
184
+
185
+ /**
186
+ * The result of the CoC pass as a texture node.
187
+ *
188
+ * @private
189
+ * @type {TextureNode}
190
+ */
191
+ this._CoCTextureNode = texture( this._CoCRT.texture );
192
+
193
+ /**
194
+ * The result of the blur64 pass as a texture node.
195
+ *
196
+ * @private
197
+ * @type {TextureNode}
198
+ */
199
+ this._blur64TextureNode = texture( this._blur64RT.texture );
65
200
 
66
201
  /**
67
- * Represents the input's aspect ratio.
202
+ * The result of the near field's blur16 pass as a texture node.
68
203
  *
69
204
  * @private
70
- * @type {UniformNode<float>}
205
+ * @type {TextureNode}
71
206
  */
72
- this._aspect = uniform( 0 );
207
+ this._blur16NearTextureNode = texture( this._blur16NearRT.texture );
208
+
209
+ /**
210
+ * The result of the far field's blur16 pass as a texture node.
211
+ *
212
+ * @private
213
+ * @type {TextureNode}
214
+ */
215
+ this._blur16FarTextureNode = texture( this._blur16FarRT.texture );
73
216
 
74
217
  /**
75
218
  * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node updates
@@ -82,16 +225,114 @@ class DepthOfFieldNode extends TempNode {
82
225
 
83
226
  }
84
227
 
228
+ /**
229
+ * Sets the size of the effect.
230
+ *
231
+ * @param {number} width - The width of the effect.
232
+ * @param {number} height - The height of the effect.
233
+ */
234
+ setSize( width, height ) {
235
+
236
+ this._invSize.value.set( 1 / width, 1 / height );
237
+
238
+ this._CoCRT.setSize( width, height );
239
+ this._compositeRT.setSize( width, height );
240
+
241
+ // blur runs in half resolution
242
+
243
+ const halfResX = Math.round( width / 2 );
244
+ const halfResY = Math.round( height / 2 );
245
+
246
+ this._CoCBlurredRT.setSize( halfResX, halfResY );
247
+ this._blur64RT.setSize( halfResX, halfResY );
248
+ this._blur16NearRT.setSize( halfResX, halfResY );
249
+ this._blur16FarRT.setSize( halfResX, halfResY );
250
+
251
+ }
252
+
253
+ /**
254
+ * Returns the result of the effect as a texture node.
255
+ *
256
+ * @return {PassTextureNode} A texture node that represents the result of the effect.
257
+ */
258
+ getTextureNode() {
259
+
260
+ return this._textureNode;
261
+
262
+ }
263
+
85
264
  /**
86
265
  * This method is used to update the effect's uniforms once per frame.
87
266
  *
88
267
  * @param {NodeFrame} frame - The current node frame.
89
268
  */
90
- updateBefore() {
269
+ updateBefore( frame ) {
270
+
271
+ const { renderer } = frame;
272
+
273
+ // resize
91
274
 
92
275
  const map = this.textureNode.value;
276
+ this.setSize( map.image.width, map.image.height );
277
+
278
+ // save state
279
+
280
+ _rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
281
+
282
+ renderer.setClearColor( 0x000000, 0 );
283
+
284
+ // coc
285
+
286
+ _quadMesh.material = this._CoCMaterial;
287
+ renderer.setRenderTarget( this._CoCRT );
288
+ _quadMesh.render( renderer );
289
+
290
+ // blur near field to avoid visible aliased edges when the near field
291
+ // is blended with the background
292
+
293
+ this._CoCTextureNode.value = this._CoCRT.textures[ 0 ];
294
+
295
+ _quadMesh.material = this._CoCBlurredMaterial;
296
+ renderer.setRenderTarget( this._CoCBlurredRT );
297
+ _quadMesh.render( renderer );
298
+
299
+ // blur64 near
300
+
301
+ this._CoCTextureNode.value = this._CoCBlurredRT.texture;
302
+
303
+ _quadMesh.material = this._blur64Material;
304
+ renderer.setRenderTarget( this._blur64RT );
305
+ _quadMesh.render( renderer );
306
+
307
+ // blur16 near
308
+
309
+ _quadMesh.material = this._blur16Material;
310
+ renderer.setRenderTarget( this._blur16NearRT );
311
+ _quadMesh.render( renderer );
312
+
313
+ // blur64 far
314
+
315
+ this._CoCTextureNode.value = this._CoCRT.textures[ 1 ];
316
+
317
+ _quadMesh.material = this._blur64Material;
318
+ renderer.setRenderTarget( this._blur64RT );
319
+ _quadMesh.render( renderer );
320
+
321
+ // blur16 far
322
+
323
+ _quadMesh.material = this._blur16Material;
324
+ renderer.setRenderTarget( this._blur16FarRT );
325
+ _quadMesh.render( renderer );
93
326
 
94
- this._aspect.value = map.image.width / map.image.height;
327
+ // composite
328
+
329
+ _quadMesh.material = this._compositeMaterial;
330
+ renderer.setRenderTarget( this._compositeRT );
331
+ _quadMesh.render( renderer );
332
+
333
+ // restore
334
+
335
+ RendererUtils.restoreRendererState( renderer, _rendererState );
95
336
 
96
337
  }
97
338
 
@@ -101,81 +342,189 @@ class DepthOfFieldNode extends TempNode {
101
342
  * @param {NodeBuilder} builder - The current node builder.
102
343
  * @return {ShaderCallNodeInternal}
103
344
  */
104
- setup() {
105
-
106
- const textureNode = this.textureNode;
107
- const uvNode = textureNode.uvNode || uv();
108
-
109
- const sampleTexture = ( uv ) => textureNode.sample( uv );
110
-
111
- const dof = Fn( () => {
112
-
113
- const aspectcorrect = vec2( 1.0, this._aspect );
114
-
115
- const factor = this.focusNode.add( this.viewZNode );
116
-
117
- const dofblur = vec2( clamp( factor.mul( this.apertureNode ), this.maxblurNode.negate(), this.maxblurNode ) );
118
-
119
- const dofblur9 = dofblur.mul( 0.9 );
120
- const dofblur7 = dofblur.mul( 0.7 );
121
- const dofblur4 = dofblur.mul( 0.4 );
122
-
123
- let col = vec4( 0.0 );
124
-
125
- col = col.add( sampleTexture( uvNode ) );
126
-
127
- col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
128
- col = col.add( sampleTexture( uvNode.add( vec2( 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
129
- col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
130
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
131
- col = col.add( sampleTexture( uvNode.add( vec2( 0.40, 0.0 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
132
- col = col.add( sampleTexture( uvNode.add( vec2( 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
133
- col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
134
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
135
- col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
136
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
137
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
138
- col = col.add( sampleTexture( uvNode.add( vec2( 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
139
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
140
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
141
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
142
- col = col.add( sampleTexture( uvNode.add( vec2( 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
143
- col = col.add( sampleTexture( uvNode.add( vec2( 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
144
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
145
- col = col.add( sampleTexture( uvNode.add( vec2( 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
146
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
147
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
148
- col = col.add( sampleTexture( uvNode.add( vec2( 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
149
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
150
- col = col.add( sampleTexture( uvNode.add( vec2( 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
151
- col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
152
- col = col.add( sampleTexture( uvNode.add( vec2( 0.40, 0.0 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
153
- col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
154
- col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
155
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
156
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
157
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
158
- col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
159
- col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
160
- col = col.add( sampleTexture( uvNode.add( vec2( 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
161
- col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
162
- col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
163
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
164
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
165
- col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
166
- col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
167
-
168
- col = col.div( 41 );
169
- col.a = 1;
170
-
171
- return vec4( col );
345
+ setup( builder ) {
346
+
347
+ const kernels = this._generateKernels();
348
+
349
+ // CoC, near and far fields
350
+
351
+ const nearField = property( 'float' );
352
+ const farField = property( 'float' );
353
+
354
+ const outputNode = outputStruct( nearField, farField );
172
355
 
356
+ const CoC = Fn( () => {
357
+
358
+ const signedDist = this.viewZNode.negate().sub( this.focusDistanceNode );
359
+ const CoC = smoothstep( 0, this.focalLengthNode, signedDist.abs() );
360
+
361
+ nearField.assign( step( signedDist, 0 ).mul( CoC ) );
362
+ farField.assign( step( 0, signedDist ).mul( CoC ) );
363
+
364
+ return vec4( 0 );
173
365
 
174
366
  } );
175
367
 
176
- const outputNode = dof();
368
+ this._CoCMaterial.colorNode = CoC().context( builder.getSharedContext() );
369
+ this._CoCMaterial.outputNode = outputNode;
370
+ this._CoCMaterial.needsUpdate = true;
371
+
372
+ // blurred CoC for near field
373
+
374
+ this._CoCBlurredMaterial.colorNode = gaussianBlur( this._CoCTextureNode, 1, 2 );
375
+ this._CoCBlurredMaterial.needsUpdate = true;
376
+
377
+ // bokeh 64 blur pass
378
+
379
+ const bokeh64 = uniformArray( kernels.points64 );
380
+
381
+ const blur64 = Fn( () => {
382
+
383
+ const acc = vec3();
384
+ const uvNode = uv();
385
+
386
+ const CoC = this._CoCTextureNode.sample( uvNode ).r;
387
+ const sampleStep = this._invSize.mul( this.bokehScaleNode ).mul( CoC );
388
+
389
+ Loop( 64, ( { i } ) => {
390
+
391
+ const sUV = uvNode.add( sampleStep.mul( bokeh64.element( i ) ) );
392
+ const tap = this.textureNode.sample( sUV );
393
+
394
+ acc.addAssign( tap.rgb );
395
+
396
+ } );
397
+
398
+ acc.divAssign( 64 );
399
+
400
+ return vec4( acc, CoC );
401
+
402
+ } );
403
+
404
+ this._blur64Material.fragmentNode = blur64().context( builder.getSharedContext() );
405
+ this._blur64Material.needsUpdate = true;
406
+
407
+ // bokeh 16 blur pass
408
+
409
+ const bokeh16 = uniformArray( kernels.points16 );
410
+
411
+ const blur16 = Fn( () => {
412
+
413
+ const uvNode = uv();
414
+
415
+ const col = this._blur64TextureNode.sample( uvNode ).toVar();
416
+ const maxVal = col.rgb;
417
+ const CoC = col.a;
418
+ const sampleStep = this._invSize.mul( this.bokehScaleNode ).mul( CoC );
419
+
420
+ Loop( 16, ( { i } ) => {
421
+
422
+ const sUV = uvNode.add( sampleStep.mul( bokeh16.element( i ) ) );
423
+ const tap = this._blur64TextureNode.sample( sUV );
424
+
425
+ maxVal.assign( max( tap.rgb, maxVal ) );
426
+
427
+ } );
428
+
429
+ return vec4( maxVal, CoC );
430
+
431
+ } );
432
+
433
+ this._blur16Material.fragmentNode = blur16().context( builder.getSharedContext() );
434
+ this._blur16Material.needsUpdate = true;
435
+
436
+ // composite
437
+
438
+ const composite = Fn( () => {
439
+
440
+ const uvNode = uv();
441
+
442
+ const near = this._blur16NearTextureNode.sample( uvNode );
443
+ const far = this._blur16FarTextureNode.sample( uvNode );
444
+ const beauty = this.textureNode.sample( uvNode );
445
+
446
+ // TODO: applying the bokeh scale to the near field CoC value introduces blending
447
+ // issues around edges of blurred foreground objects when their are rendered above
448
+ // the background. for now, don't apply the bokeh scale to the blend factors. that
449
+ // will cause less blur for objects which are partly out-of-focus (CoC between 0 and 1).
450
+
451
+ const blendNear = min( near.a, 0.5 ).mul( 2 );
452
+ const blendFar = min( far.a, 0.5 ).mul( 2 );
453
+
454
+ const result = vec4( 0, 0, 0, 1 ).toVar();
455
+ result.rgb = mix( beauty.rgb, far.rgb, blendFar );
456
+ result.rgb = mix( result.rgb, near.rgb, blendNear );
457
+
458
+ return result;
459
+
460
+ } );
461
+
462
+ this._compositeMaterial.fragmentNode = composite().context( builder.getSharedContext() );
463
+ this._compositeMaterial.needsUpdate = true;
464
+
465
+ return this._textureNode;
466
+
467
+ }
468
+
469
+ _generateKernels() {
470
+
471
+ // Vogel's method, see https://www.shadertoy.com/view/4fBXRG
472
+ // this approach allows to generate uniformly distributed sample
473
+ // points in a disc-shaped pattern. Blurring with these samples
474
+ // produces a typical optical lens blur
475
+
476
+ const GOLDEN_ANGLE = 2.39996323;
477
+ const SAMPLES = 80;
478
+
479
+ const points64 = [];
480
+ const points16 = [];
481
+
482
+ let idx64 = 0;
483
+ let idx16 = 0;
484
+
485
+ for ( let i = 0; i < SAMPLES; i ++ ) {
486
+
487
+ const theta = i * GOLDEN_ANGLE;
488
+ const r = Math.sqrt( i ) / Math.sqrt( SAMPLES );
489
+
490
+ const p = new Vector2( r * Math.cos( theta ), r * Math.sin( theta ) );
491
+
492
+ if ( i % 5 === 0 ) {
493
+
494
+ points16[ idx16 ] = p;
495
+ idx16 ++;
496
+
497
+ } else {
498
+
499
+ points64[ idx64 ] = p;
500
+ idx64 ++;
501
+
502
+ }
503
+
504
+ }
505
+
506
+ return { points16, points64 };
507
+
508
+ }
509
+
510
+ /**
511
+ * Frees internal resources. This method should be called
512
+ * when the effect is no longer required.
513
+ */
514
+ dispose() {
515
+
516
+ this._CoCRT.dispose();
517
+ this._CoCBlurredRT.dispose();
518
+ this._blur64RT.dispose();
519
+ this._blur16NearRT.dispose();
520
+ this._blur16FarRT.dispose();
521
+ this._compositeRT.dispose();
177
522
 
178
- return outputNode;
523
+ this._CoCMaterial.dispose();
524
+ this._CoCBlurredMaterial.dispose();
525
+ this._blur64Material.dispose();
526
+ this._blur16Material.dispose();
527
+ this._compositeMaterial.dispose();
179
528
 
180
529
  }
181
530
 
@@ -190,9 +539,9 @@ export default DepthOfFieldNode;
190
539
  * @function
191
540
  * @param {Node<vec4>} node - The node that represents the input of the effect.
192
541
  * @param {Node<float>} viewZNode - Represents the viewZ depth values of the scene.
193
- * @param {Node<float> | number} focus - Defines the effect's focus which is the distance along the camera's look direction in world units.
194
- * @param {Node<float> | number} aperture - Defines the effect's aperture.
195
- * @param {Node<float> | number} maxblur - Defines the effect's maximum blur.
542
+ * @param {Node<float> | number} focusDistance - Defines the effect's focus which is the distance along the camera's look direction in world units.
543
+ * @param {Node<float> | number} focalLength - How far an object can be from the focal plane before it goes completely out-of-focus in world units.
544
+ * @param {Node<float> | number} bokehScale - A unitless value for artistic purposes to adjust the size of the bokeh.
196
545
  * @returns {DepthOfFieldNode}
197
546
  */
198
- export const dof = ( node, viewZNode, focus = 1, aperture = 0.025, maxblur = 1 ) => nodeObject( new DepthOfFieldNode( convertToTexture( node ), nodeObject( viewZNode ), nodeObject( focus ), nodeObject( aperture ), nodeObject( maxblur ) ) );
547
+ export const dof = ( node, viewZNode, focusDistance = 1, focalLength = 1, bokehScale = 1 ) => nodeObject( new DepthOfFieldNode( convertToTexture( node ), nodeObject( viewZNode ), nodeObject( focusDistance ), nodeObject( focalLength ), nodeObject( bokehScale ) ) );
@@ -323,6 +323,8 @@ class GTAONode extends TempNode {
323
323
 
324
324
  const ao = float( 0 ).toVar();
325
325
 
326
+ // Each iteration analyzes one vertical "slice" of the 3D space around the fragment.
327
+
326
328
  Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {
327
329
 
328
330
  const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).toVar();
@@ -337,10 +339,14 @@ class GTAONode extends TempNode {
337
339
  const tangentToNormalInSlice = cross( normalInSlice, sliceBitangent ).toVar();
338
340
  const cosHorizons = vec2( dot( viewDir, tangentToNormalInSlice ), dot( viewDir, tangentToNormalInSlice.negate() ) ).toVar();
339
341
 
342
+ // For each slice, the inner loop performs ray marching to find the horizons.
343
+
340
344
  Loop( { end: STEPS, type: 'int', name: 'j', condition: '<' }, ( { j } ) => {
341
345
 
342
346
  const sampleViewOffset = sampleDir.xyz.mul( radiusToUse ).mul( sampleDir.w ).mul( pow( div( float( j ).add( 1.0 ), float( STEPS ) ), this.distanceExponent ) );
343
347
 
348
+ // The loop marches in two opposite directions (x and y) along the slice's line to find the horizon on both sides.
349
+
344
350
  // x
345
351
 
346
352
  const sampleScreenPositionX = getScreenPosition( viewPosition.add( sampleViewOffset ), this._cameraProjectionMatrix ).toVar();
@@ -371,6 +377,8 @@ class GTAONode extends TempNode {
371
377
 
372
378
  } );
373
379
 
380
+ // After the horizons are found for a given slice, their contribution to the total occlusion is calculated.
381
+
374
382
  const sinHorizons = sqrt( sub( 1.0, cosHorizons.mul( cosHorizons ) ) ).toVar();
375
383
  const nx = dot( normalInSlice, sliceTangent );
376
384
  const ny = dot( normalInSlice, viewDir );