@takram/three-geospatial 0.8.0 → 0.9.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 (64) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/build/index.cjs +1 -1
  3. package/build/index.cjs.map +1 -1
  4. package/build/index.js +580 -598
  5. package/build/index.js.map +1 -1
  6. package/build/r3f.cjs +1 -1
  7. package/build/r3f.js +1 -1
  8. package/build/shared2.cjs +1 -1
  9. package/build/shared2.cjs.map +1 -1
  10. package/build/shared2.js +27 -212
  11. package/build/shared2.js.map +1 -1
  12. package/build/shared3.cjs +1 -1
  13. package/build/shared3.cjs.map +1 -1
  14. package/build/shared3.js +213 -8
  15. package/build/shared3.js.map +1 -1
  16. package/build/webgpu.cjs +6 -1
  17. package/build/webgpu.cjs.map +1 -1
  18. package/build/webgpu.js +1002 -812
  19. package/build/webgpu.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/PointOfView.ts +12 -5
  22. package/src/STBNLoader.ts +41 -19
  23. package/src/webgpu/CascadedShadowMapsNode.ts +48 -0
  24. package/src/webgpu/DualMipmapFilterNode.ts +8 -4
  25. package/src/webgpu/FilterNode.ts +3 -2
  26. package/src/webgpu/FnLayout.ts +17 -16
  27. package/src/webgpu/HighpVelocityNode.ts +2 -2
  28. package/src/webgpu/LensFlareNode.ts +1 -1
  29. package/src/webgpu/LensGlareNode.ts +26 -26
  30. package/src/webgpu/LensHaloNode.ts +2 -1
  31. package/src/webgpu/OutputTexture3DNode.ts +10 -1
  32. package/src/webgpu/OutputTextureNode.ts +10 -1
  33. package/src/webgpu/STBNTextureNode.ts +72 -0
  34. package/src/webgpu/ScreenSpaceShadowNode.ts +30 -35
  35. package/src/webgpu/SeparableFilterNode.ts +8 -5
  36. package/src/webgpu/SingleFilterNode.ts +5 -2
  37. package/src/webgpu/StorageTexture3DNode.ts +30 -0
  38. package/src/webgpu/TemporalAntialiasNode.ts +50 -31
  39. package/src/webgpu/accessors.ts +72 -36
  40. package/src/webgpu/debug.ts +38 -47
  41. package/src/webgpu/events.ts +18 -0
  42. package/src/webgpu/index.ts +4 -0
  43. package/src/webgpu/math.ts +116 -15
  44. package/src/webgpu/sampling.ts +39 -5
  45. package/src/webgpu/transformations.ts +71 -44
  46. package/types/PointOfView.d.ts +1 -1
  47. package/types/STBNLoader.d.ts +3 -4
  48. package/types/webgpu/CascadedShadowMapsNode.d.ts +13 -0
  49. package/types/webgpu/DualMipmapFilterNode.d.ts +1 -2
  50. package/types/webgpu/FnLayout.d.ts +4 -4
  51. package/types/webgpu/LensGlareNode.d.ts +1 -1
  52. package/types/webgpu/STBNTextureNode.d.ts +10 -0
  53. package/types/webgpu/ScreenSpaceShadowNode.d.ts +2 -4
  54. package/types/webgpu/SeparableFilterNode.d.ts +2 -3
  55. package/types/webgpu/SingleFilterNode.d.ts +1 -2
  56. package/types/webgpu/StorageTexture3DNode.d.ts +9 -0
  57. package/types/webgpu/TemporalAntialiasNode.d.ts +1 -1
  58. package/types/webgpu/accessors.d.ts +9 -8
  59. package/types/webgpu/debug.d.ts +4 -3
  60. package/types/webgpu/events.d.ts +3 -0
  61. package/types/webgpu/index.d.ts +4 -0
  62. package/types/webgpu/math.d.ts +3 -0
  63. package/types/webgpu/sampling.d.ts +2 -1
  64. package/types/webgpu/transformations.d.ts +7 -10
@@ -1,17 +1,17 @@
1
1
  import type {
2
2
  ProxiedTuple,
3
3
  ShaderCallNodeInternal,
4
- ShaderNodeFn,
5
- Struct
4
+ ShaderNodeFn
6
5
  } from 'three/src/nodes/TSL.js'
7
6
  import { Fn } from 'three/tsl'
8
- import type { NodeBuilder, Texture3DNode, TextureNode } from 'three/webgpu'
7
+ import { StructTypeNode, type NodeBuilder } from 'three/webgpu'
9
8
 
10
9
  import type { Node, NodeType } from './node'
11
10
 
12
- // Note that "texture" and "texture3D" are just placeholders until TSL supports
13
- // texture types.
14
- type FnLayoutType = NodeType | Struct | 'texture' | 'texture3D'
11
+ type FnLayoutType =
12
+ | NodeType
13
+ | (new (...args: any[]) => any)
14
+ | ((...args: any[]) => any)
15
15
 
16
16
  export interface FnLayoutInput<T extends FnLayoutType = FnLayoutType> {
17
17
  name: string
@@ -30,13 +30,11 @@ export interface FnLayout<
30
30
 
31
31
  type InferNodeObject<T extends FnLayoutType> = T extends NodeType
32
32
  ? Node<T>
33
- : T extends Struct
34
- ? ReturnType<T>
35
- : T extends 'texture'
36
- ? TextureNode
37
- : T extends 'texture3D'
38
- ? Texture3DNode
39
- : never
33
+ : T extends new (...args: any[]) => any
34
+ ? InstanceType<T>
35
+ : T extends (...args: any[]) => any
36
+ ? ReturnType<T>
37
+ : never
40
38
 
41
39
  type InferNodeObjects<Inputs extends readonly FnLayoutInput[]> = {
42
40
  [K in keyof Inputs]: Inputs[K] extends FnLayoutInput<infer T>
@@ -58,10 +56,13 @@ function transformType(type: FnLayoutType): string {
58
56
  if (typeof type === 'string') {
59
57
  return type
60
58
  }
61
- if (type.layout.name == null) {
62
- throw new Error('Struct name is required.')
59
+ if ('layout' in type && type.layout instanceof StructTypeNode) {
60
+ if (type.layout.name == null) {
61
+ throw new Error('Struct name is required.')
62
+ }
63
+ return type.layout.name
63
64
  }
64
- return type.layout.name
65
+ throw new Error(`Unsupported layout type: ${type}`)
65
66
  }
66
67
 
67
68
  export function FnLayout<
@@ -20,10 +20,10 @@ export class HighpVelocityNode extends TempNode {
20
20
 
21
21
  projectionMatrix?: Matrix4 | null
22
22
 
23
- private readonly currentProjectionMatrix = uniform(new Matrix4())
23
+ private readonly currentProjectionMatrix = uniform('mat4')
24
24
  private readonly previousProjectionMatrix = uniform('mat4')
25
25
 
26
- private readonly currentModelViewMatrix = uniform(new Matrix4())
26
+ private readonly currentModelViewMatrix = uniform('mat4')
27
27
  private readonly previousModelViewMatrix = uniform('mat4')
28
28
  private readonly objectModelViewMatrices = new WeakMap<Object3D, Matrix4>()
29
29
 
@@ -45,7 +45,7 @@ export class LensFlareNode extends TempNode {
45
45
  this.glareNode = new LensGlareNode()
46
46
 
47
47
  this.featuresNode = rtt(add(this.ghostNode, this.haloNode))
48
- this.featuresNode.value.name = 'LensFlareNode.Features'
48
+ this.featuresNode.value.name = 'LensFlare_features'
49
49
  this.featuresNode.pixelRatio = 0.5
50
50
 
51
51
  // Use the full resolution because the thresholdNode already downsamples the
@@ -29,9 +29,9 @@ import {
29
29
  vec4
30
30
  } from 'three/tsl'
31
31
  import {
32
+ IndirectStorageBufferAttribute,
32
33
  MeshBasicNodeMaterial,
33
34
  RendererUtils,
34
- StorageBufferAttribute,
35
35
  type ComputeNode,
36
36
  type NodeBuilder,
37
37
  type NodeFrame,
@@ -92,7 +92,12 @@ export class LensGlareNode extends FilterNode {
92
92
 
93
93
  private computeNode?: ComputeNode
94
94
 
95
- private readonly counterBuffer = new StorageBufferAttribute(1, 1)
95
+ // drawIndexedIndirect format:
96
+ // [indexCount, instanceCount, firstIndex, baseVertex, firstInstance]
97
+ private readonly indirectBuffer = new IndirectStorageBufferAttribute(
98
+ new Uint32Array([6, 0, 0, 0, 0]),
99
+ 1
100
+ )
96
101
  private instanceBuffer = instancedArray(1, instanceStruct)
97
102
 
98
103
  private readonly renderTarget = this.createRenderTarget()
@@ -106,16 +111,19 @@ export class LensGlareNode extends FilterNode {
106
111
  private readonly camera = new PerspectiveCamera()
107
112
  private rendererState?: RendererUtils.RendererState
108
113
 
109
- private readonly inputTexelSize = uniform(new Vector2())
110
- private readonly outputTexelSize = uniform(new Vector2())
111
- private readonly geometryRatio = uniform(new Vector2())
114
+ private readonly inputTexelSize = uniform('vec2')
115
+ private readonly outputTexelSize = uniform('vec2')
116
+ private readonly geometryRatio = uniform('vec2')
112
117
 
113
118
  constructor(inputNode?: TextureNode | null) {
114
119
  super(inputNode)
120
+ this.material.name = 'LensGlare'
121
+
115
122
  this.inputNode = inputNode
116
123
  this.resolutionScale = 0.5
117
124
 
118
125
  this.outputTexture = this.renderTarget.texture
126
+ this.mesh.geometry.indirect = this.indirectBuffer
119
127
  }
120
128
 
121
129
  override customCacheKey(): number {
@@ -151,7 +159,7 @@ export class LensGlareNode extends FilterNode {
151
159
  const { width: inputWidth, height: inputHeight } = inputNode.value
152
160
  this.setSize(inputWidth, inputHeight) // Compute node is initialized here.
153
161
 
154
- const { computeNode, counterBuffer, renderTarget } = this
162
+ const { computeNode, indirectBuffer, renderTarget } = this
155
163
  invariant(computeNode != null)
156
164
 
157
165
  this.inputTexelSize.value.set(1 / inputWidth, 1 / inputHeight)
@@ -165,24 +173,12 @@ export class LensGlareNode extends FilterNode {
165
173
  const { width: outputWidth, height: outputHeight } = renderTarget
166
174
  this.outputTexelSize.value.set(1 / outputWidth, 1 / outputHeight)
167
175
 
168
- // Reset the counter:
169
- counterBuffer.array[0] = 0
170
- counterBuffer.needsUpdate = true
176
+ // Reset instanceCount in the indirect buffer:
177
+ indirectBuffer.array[1] = 0
178
+ indirectBuffer.needsUpdate = true
171
179
 
172
180
  void renderer.compute(computeNode)
173
181
 
174
- renderer
175
- .getArrayBufferAsync(counterBuffer)
176
- .then(arrayBuffer => {
177
- // TODO: This is indeed a couple of frames behind, thus the number of
178
- // computed instances above and the number of instances to be drawn by
179
- // the mesh differ.
180
- this.mesh.count = new Uint32Array(arrayBuffer)[0]
181
- })
182
- .catch((error: unknown) => {
183
- console.error(error)
184
- })
185
-
186
182
  this.rendererState = resetRendererState(renderer, this.rendererState)
187
183
 
188
184
  renderer.setRenderTarget(renderTarget)
@@ -195,16 +191,16 @@ export class LensGlareNode extends FilterNode {
195
191
  const {
196
192
  spikePairCount,
197
193
  inputNode,
198
- counterBuffer,
194
+ indirectBuffer,
199
195
  instanceBuffer,
200
196
  outputTexelSize
201
197
  } = this
202
198
  invariant(inputNode != null)
203
199
 
204
- const counterStorage = storage(
205
- counterBuffer,
200
+ const indirectStorage = storage(
201
+ indirectBuffer,
206
202
  'uint',
207
- counterBuffer.count
203
+ indirectBuffer.count
208
204
  ).toAtomic()
209
205
 
210
206
  this.computeNode = Fn(() => {
@@ -218,7 +214,11 @@ export class LensGlareNode extends FilterNode {
218
214
  const inputLuminance = inputColor.a // Alpha channel stores luminance
219
215
 
220
216
  If(inputLuminance.greaterThan(0.1), () => {
221
- const countBefore = atomicAdd(counterStorage.element(0), spikePairCount)
217
+ // The first element is instanceCount in the drawIndexedIndirect buffer.
218
+ const countBefore = atomicAdd(
219
+ indirectStorage.element(1),
220
+ spikePairCount
221
+ )
222
222
  for (let i = 0; i < spikePairCount; ++i) {
223
223
  const instance = instanceBuffer.element(countBefore.add(i))
224
224
  instance.get('color').assign(inputColor.rgb)
@@ -38,8 +38,9 @@ export class LensHaloNode extends TempNode {
38
38
 
39
39
  constructor(inputNode?: TextureNode | null) {
40
40
  super('vec3')
41
- this.inputNode = inputNode
42
41
  this.updateBeforeType = NodeUpdateType.FRAME
42
+
43
+ this.inputNode = inputNode
43
44
  }
44
45
 
45
46
  override updateBefore({ renderer }: NodeFrame): void {
@@ -31,7 +31,16 @@ export class OutputTexture3DNode extends Texture3DNode {
31
31
 
32
32
  override clone(): this {
33
33
  // @ts-expect-error Ignore
34
- return new this.constructor(this.owner, this.value)
34
+ const copy = new this.constructor(this.owner, this.value)
35
+ copy.uvNode = this.uvNode
36
+ copy.levelNode = this.levelNode
37
+ copy.biasNode = this.biasNode
38
+ copy.sampler = this.sampler
39
+ copy.depthNode = this.depthNode
40
+ copy.compareNode = this.compareNode
41
+ copy.gradNode = this.gradNode
42
+ copy.offsetNode = this.offsetNode
43
+ return copy
35
44
  }
36
45
  }
37
46
 
@@ -31,7 +31,16 @@ export class OutputTextureNode extends TextureNode {
31
31
 
32
32
  override clone(): this {
33
33
  // @ts-expect-error Ignore
34
- return new this.constructor(this.owner, this.value)
34
+ const copy = new this.constructor(this.owner, this.value)
35
+ copy.uvNode = this.uvNode
36
+ copy.levelNode = this.levelNode
37
+ copy.biasNode = this.biasNode
38
+ copy.sampler = this.sampler
39
+ copy.depthNode = this.depthNode
40
+ copy.compareNode = this.compareNode
41
+ copy.gradNode = this.gradNode
42
+ copy.offsetNode = this.offsetNode
43
+ return copy
35
44
  }
36
45
  }
37
46
 
@@ -0,0 +1,72 @@
1
+ import { hashString } from 'three/src/nodes/core/NodeUtils.js'
2
+ import { Fn, frameId, nodeImmutable, screenCoordinate, vec3 } from 'three/tsl'
3
+ import {
4
+ Data3DTexture,
5
+ NearestFilter,
6
+ RedFormat,
7
+ RepeatWrapping,
8
+ Texture3DNode,
9
+ type NodeBuilder
10
+ } from 'three/webgpu'
11
+
12
+ import { DEFAULT_STBN_URL } from '../constants'
13
+ import { STBNLoader } from '../STBNLoader'
14
+
15
+ const emptyTexture3D = /*#__PURE__*/ (() => {
16
+ const texture = new Data3DTexture(new Uint8Array(1))
17
+ texture.format = RedFormat
18
+ // BUG: TextureNode doesn't update these when the texture is swapped.
19
+ texture.minFilter = NearestFilter
20
+ texture.magFilter = NearestFilter
21
+ texture.wrapS = RepeatWrapping
22
+ texture.wrapT = RepeatWrapping
23
+ texture.wrapR = RepeatWrapping
24
+ texture.needsUpdate = true
25
+ return texture
26
+ })()
27
+
28
+ export class STBNTextureNode extends Texture3DNode {
29
+ url = DEFAULT_STBN_URL
30
+
31
+ constructor() {
32
+ super(emptyTexture3D)
33
+ }
34
+
35
+ override customCacheKey(): number {
36
+ return hashString(this.url)
37
+ }
38
+
39
+ override setup(builder: NodeBuilder): unknown {
40
+ new STBNLoader()
41
+ .loadAsync(this.url)
42
+ .then(texture => {
43
+ this.value = texture
44
+ })
45
+ .catch((error: unknown) => {
46
+ console.error(error)
47
+ })
48
+ return super.setup(builder)
49
+ }
50
+
51
+ override clone(): this {
52
+ // @ts-expect-error Ignore
53
+ const copy = new this.constructor()
54
+ copy.uvNode = this.uvNode
55
+ copy.levelNode = this.levelNode
56
+ copy.biasNode = this.biasNode
57
+ copy.sampler = this.sampler
58
+ copy.depthNode = this.depthNode
59
+ copy.compareNode = this.compareNode
60
+ copy.gradNode = this.gradNode
61
+ copy.offsetNode = this.offsetNode
62
+ return copy
63
+ }
64
+ }
65
+
66
+ export const stbnTexture = /*#__PURE__*/ nodeImmutable(STBNTextureNode)
67
+
68
+ export const stbn = /*#__PURE__*/ Fn(() => {
69
+ return stbnTexture
70
+ .sample(vec3(screenCoordinate.xy, frameId.mod(64)).div(vec3(128, 128, 64)))
71
+ .r.toConst('stbn')
72
+ }).once()()
@@ -14,6 +14,8 @@
14
14
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
15
  * License for the specific language governing permissions and limitations under
16
16
  * the License.
17
+ *
18
+ * Modified from the original source code.
17
19
  */
18
20
 
19
21
  import {
@@ -38,7 +40,6 @@ import {
38
40
  ivec2,
39
41
  min,
40
42
  mix,
41
- textureSize,
42
43
  textureStore,
43
44
  uniform,
44
45
  vec2,
@@ -56,7 +57,6 @@ import {
56
57
  type NodeFrame,
57
58
  type TextureNode
58
59
  } from 'three/webgpu'
59
- import invariant from 'tiny-invariant'
60
60
 
61
61
  import { cameraFar, cameraNear } from './accessors'
62
62
  import type { Node } from './node'
@@ -107,14 +107,12 @@ export class ScreenSpaceShadowNode extends TempNode {
107
107
  shadowContrast = uniform(4)
108
108
  shadowIntensity = uniform(1)
109
109
  bilinearThreshold = uniform(0.02)
110
- nearDepth = uniform(0)
111
- farDepth = uniform(1)
112
110
 
113
111
  // xy: Screen coordinate
114
112
  // z: Normalized Z
115
113
  // w: Direction sign
116
- private readonly lightCoordinate = uniform(new Vector4())
117
- private readonly dispatchOffset = uniform(new Vector2(), 'ivec2')
114
+ private readonly lightCoordinate = uniform('vec4')
115
+ private readonly dispatchOffset = uniform('ivec2')
118
116
  private readonly dispatchIndex = uniform(0)
119
117
 
120
118
  private readonly dispatches: readonly Dispatch[] = Array.from(
@@ -124,7 +122,7 @@ export class ScreenSpaceShadowNode extends TempNode {
124
122
  )
125
123
  private dispatchCount = 0
126
124
 
127
- private computeNode?: ComputeNode
125
+ private readonly computeNode: ComputeNode
128
126
 
129
127
  constructor(
130
128
  depthNode: TextureNode,
@@ -132,6 +130,8 @@ export class ScreenSpaceShadowNode extends TempNode {
132
130
  mainLight: DirectionalLight
133
131
  ) {
134
132
  super('float')
133
+ this.updateBeforeType = NodeUpdateType.FRAME
134
+
135
135
  this.depthNode = depthNode
136
136
  this.camera = camera
137
137
  this.mainLight = mainLight
@@ -141,12 +141,11 @@ export class ScreenSpaceShadowNode extends TempNode {
141
141
  texture.minFilter = LinearFilter
142
142
  texture.magFilter = LinearFilter
143
143
  texture.generateMipmaps = false
144
- texture.name = 'ScreenSpaceShadowNode'
144
+ texture.name = 'ScreenSpaceShadow'
145
145
 
146
146
  this.outputTexture = texture
147
147
  this.textureNode = outputTexture(this, texture)
148
-
149
- this.updateBeforeType = NodeUpdateType.FRAME
148
+ this.computeNode = this.createComputeNode()
150
149
  }
151
150
 
152
151
  override customCacheKey(): number {
@@ -198,7 +197,6 @@ export class ScreenSpaceShadowNode extends TempNode {
198
197
 
199
198
  this.updateDispatchList(lightProjection, size)
200
199
 
201
- invariant(this.computeNode != null)
202
200
  for (let index = 0; index < this.dispatchCount; ++index) {
203
201
  const dispatch = this.dispatches[index]
204
202
  this.dispatchOffset.value.set(dispatch.offset.x, dispatch.offset.y)
@@ -341,7 +339,7 @@ export class ScreenSpaceShadowNode extends TempNode {
341
339
  }
342
340
 
343
341
  // See bend_sss_gpu.h
344
- private setupCompute(builder: NodeBuilder): void {
342
+ private createComputeNode(): ComputeNode {
345
343
  const {
346
344
  depthNode,
347
345
  camera,
@@ -353,8 +351,6 @@ export class ScreenSpaceShadowNode extends TempNode {
353
351
  shadowContrast,
354
352
  shadowIntensity,
355
353
  bilinearThreshold,
356
- farDepth,
357
- nearDepth,
358
354
  lightCoordinate,
359
355
  dispatchOffset
360
356
  } = this
@@ -442,27 +438,30 @@ export class ScreenSpaceShadowNode extends TempNode {
442
438
  return { pixelXY, pixelDistance, xyDelta, xAxisMajor }
443
439
  }
444
440
 
445
- const loadDepth = (coord: Node<'ivec2'>): Node<'float'> => {
446
- const depth = depthNode.load(coord).toVar()
447
- if (builder.renderer.logarithmicDepthBuffer) {
448
- depth.assign(
449
- logarithmicToPerspectiveDepth(
441
+ return Fn(builder => {
442
+ const [nearDepth, farDepth] = builder.renderer.reversedDepthBuffer
443
+ ? [float(1), float(0)]
444
+ : [float(0), float(1)]
445
+
446
+ const loadDepth = (coord: Node<'ivec2'>): Node<'float'> => {
447
+ let depth: Node = depthNode.load(coord)
448
+ if (builder.renderer.logarithmicDepthBuffer) {
449
+ depth = logarithmicToPerspectiveDepth(
450
450
  depth,
451
451
  cameraNear(camera),
452
452
  cameraFar(camera)
453
453
  )
454
- )
454
+ }
455
+ depth = depth.toConst()
456
+
457
+ // Emulate a point sampler in bend_sss_gpu.h, with Wrap Mode set to
458
+ // Clamp-To-Border-Color, and Border Color set to farDepth.
459
+ return and(
460
+ coord.greaterThanEqual(0).all(),
461
+ coord.lessThan(depthNode.size()).all()
462
+ ).select(depth, farDepth)
455
463
  }
456
464
 
457
- // Emulate a point sampler in bend_sss_gpu.h, with Wrap Mode set to
458
- // Clamp-To-Border-Color, and Border Color set to farDepth.
459
- return and(
460
- coord.greaterThanEqual(0).all(),
461
- coord.lessThan(textureSize(depthNode)).all()
462
- ).select(depth, farDepth)
463
- }
464
-
465
- this.computeNode = Fn(() => {
466
465
  const { pixelXY, xyDelta, pixelDistance, xAxisMajor } =
467
466
  getWorkgroupExtents()
468
467
 
@@ -656,7 +655,7 @@ export class ScreenSpaceShadowNode extends TempNode {
656
655
  shadowValue.mul(shadowContrast).add(contrastOffset).saturate()
657
656
  )
658
657
 
659
- const result = float().toVar()
658
+ const result = float(0).toVar()
660
659
 
661
660
  // Take the average of 4 samples, this is useful to reduce aliasing noise
662
661
  // in the source depth, especially with long shadows.
@@ -669,14 +668,10 @@ export class ScreenSpaceShadowNode extends TempNode {
669
668
  // Asking the GPU to write scattered single-byte pixels isn't great,
670
669
  // But thankfully the latency is hidden by all the work we're doing...
671
670
  textureStore(outputTexture, writeXY, mix(1, result, shadowIntensity))
672
- })().compute(
673
- 0, // Determine this later
674
- [GROUP_SIZE, 1, 1]
675
- )
671
+ })().computeKernel([GROUP_SIZE, 1, 1])
676
672
  }
677
673
 
678
674
  override setup(builder: NodeBuilder): unknown {
679
- this.setupCompute(builder)
680
675
  return this.textureNode
681
676
  }
682
677
 
@@ -1,4 +1,4 @@
1
- import { Vector2, type RenderTarget } from 'three'
1
+ import type { RenderTarget } from 'three'
2
2
  import { uniform } from 'three/tsl'
3
3
  import {
4
4
  NodeMaterial,
@@ -24,14 +24,17 @@ export abstract class SeparableFilterNode extends FilterNode {
24
24
  private readonly mesh = new QuadMesh(this.material)
25
25
  private rendererState?: RendererUtils.RendererState
26
26
 
27
- protected readonly inputTexelSize = uniform(new Vector2())
28
- protected readonly direction = uniform(new Vector2())
27
+ protected readonly inputTexelSize = uniform('vec2')
28
+ protected readonly direction = uniform('vec2')
29
29
 
30
30
  constructor(inputNode?: TextureNode | null) {
31
31
  super(inputNode)
32
+ const typeName = (this.constructor as typeof Node).type.replace(/Node$/, '')
33
+ this.material.name = typeName
34
+ this.mesh.name = typeName
32
35
 
33
- this.horizontalRT = this.createRenderTarget('Horizontal')
34
- this.verticalRT = this.createRenderTarget('Vertical')
36
+ this.horizontalRT = this.createRenderTarget('horizontal')
37
+ this.verticalRT = this.createRenderTarget('vertical')
35
38
  this.outputTexture = this.verticalRT.texture
36
39
  }
37
40
 
@@ -1,4 +1,4 @@
1
- import { Vector2, type RenderTarget } from 'three'
1
+ import type { RenderTarget } from 'three'
2
2
  import { uniform } from 'three/tsl'
3
3
  import {
4
4
  NodeMaterial,
@@ -21,10 +21,13 @@ export abstract class SingleFilterNode extends FilterNode {
21
21
  private readonly mesh = new QuadMesh(this.material)
22
22
  private rendererState?: RendererUtils.RendererState
23
23
 
24
- protected readonly inputTexelSize = uniform(new Vector2())
24
+ protected readonly inputTexelSize = uniform('vec2')
25
25
 
26
26
  constructor(inputNode?: TextureNode | null) {
27
27
  super(inputNode)
28
+ const typeName = (this.constructor as typeof Node).type.replace(/Node$/, '')
29
+ this.material.name = typeName
30
+ this.mesh.name = typeName
28
31
 
29
32
  this.renderTarget = this.createRenderTarget()
30
33
  this.outputTexture = this.renderTarget.texture
@@ -0,0 +1,30 @@
1
+ /* eslint-disable @typescript-eslint/class-methods-use-this */
2
+
3
+ import { vec3 } from 'three/tsl'
4
+ import { StorageTextureNode, type Node, type NodeBuilder } from 'three/webgpu'
5
+
6
+ // WORKAROUND: StorageTextureNode on Storage3DTexture breaks UV.
7
+ // TODO: File a PR in the upstream.
8
+ export class StorageTexture3DNode extends StorageTextureNode {
9
+ static override get type(): string {
10
+ return 'StorageTexture3DNode'
11
+ }
12
+
13
+ override getDefaultUV(): Node {
14
+ return vec3(0.5, 0.5, 0.5)
15
+ }
16
+
17
+ setUpdateMatrix(_value: boolean): void {}
18
+
19
+ generateUV(builder: NodeBuilder, uvNode: Node): string {
20
+ return uvNode.build(builder, this.sampler ? 'vec3' : 'ivec3') as string
21
+ }
22
+
23
+ generateOffset(builder: NodeBuilder, offsetNode: Node): string {
24
+ return offsetNode.build(builder, 'ivec3') as string
25
+ }
26
+ }
27
+
28
+ export const storageTexture3D = (
29
+ ...args: ConstructorParameters<typeof StorageTexture3DNode>
30
+ ): StorageTexture3DNode => new StorageTexture3DNode(...args)