Просмотр исходного кода

TSL: Introduce `attributeArray` and `instancedArray` (#29881)

* Introduce `array` and `instancedArray`

* cleanup

* cleanup

* Update puppeteer.js

* rename `array` -> `attributeArray`

* rename `array` -> `attributeArray`

* cleanup
sunag 1 год назад
Родитель
Сommit
89d1a06fad

+ 3 - 4
examples/jsm/tsl/lighting/TiledLightsNode.js

@@ -1,7 +1,7 @@
 import {
-	storageObject, nodeProxy, int, float, vec2, ivec2, ivec4, uniform, Break, Loop,
+	attributeArray, nodeProxy, int, float, vec2, ivec2, ivec4, uniform, Break, Loop,
 	Fn, If, Return, textureLoad, instanceIndex, screenCoordinate, directPointLight,
-	LightsNode, NodeUpdateType, StorageBufferAttribute
+	LightsNode, NodeUpdateType
 } from 'three/tsl';
 
 import { DataTexture, FloatType, RGBAFormat, Vector2, Vector3 } from 'three';
@@ -290,8 +290,7 @@ class TiledLightsNode extends LightsNode {
 		const lightsTexture = new DataTexture( lightsData, lightsData.length / 8, 2, RGBAFormat, FloatType );
 
 		const lightIndexesArray = new Int32Array( count * 4 * 2 );
-		const lightIndexesAttribute = new StorageBufferAttribute( lightIndexesArray, 4 );
-		const lightIndexes = storageObject( lightIndexesAttribute, 'ivec4', lightIndexesAttribute.count ).label( 'lightIndexes' );
+		const lightIndexes = attributeArray( lightIndexesArray, 'ivec4' ).label( 'lightIndexes' );
 
 		// compute
 

+ 13 - 11
examples/webgpu_compute_audio.html

@@ -29,14 +29,14 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { Fn, uniform, storage, storageObject, instanceIndex, float, texture, screenUV, color } from 'three/tsl';
+			import { Fn, uniform, instanceIndex, instancedArray, float, texture, screenUV, color } from 'three/tsl';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
 			let camera, scene, renderer;
 			let computeNode;
 			let waveBuffer, sampleRate;
-			let waveGPUBuffer;
+			let waveArray;
 			let currentAudio, currentAnalyser;
 			const analyserBuffer = new Uint8Array( 1024 );
 			let analyserTexture;
@@ -51,14 +51,14 @@
 
 				await renderer.computeAsync( computeNode );
 
-				const waveArray = new Float32Array( await renderer.getArrayBufferAsync( waveGPUBuffer ) );
+				const wave = new Float32Array( await renderer.getArrayBufferAsync( waveArray.value ) );
 
 				// play result
 
 				const audioOutputContext = new AudioContext( { sampleRate } );
-				const audioOutputBuffer = audioOutputContext.createBuffer( 1, waveArray.length, sampleRate );
+				const audioOutputBuffer = audioOutputContext.createBuffer( 1, wave.length, sampleRate );
 
-				audioOutputBuffer.copyToChannel( waveArray, 0 );
+				audioOutputBuffer.copyToChannel( wave, 0 );
 
 				const source = audioOutputContext.createBufferSource();
 				source.connect( audioOutputContext.destination );
@@ -92,16 +92,18 @@
 
 				sampleRate = audioBuffer.sampleRate / audioBuffer.numberOfChannels;
 
-
 				// create webgpu buffers
 
-				waveGPUBuffer = new THREE.StorageInstancedBufferAttribute( waveBuffer, 1 );
-
-				const waveStorageNode = storage( waveGPUBuffer, 'float', waveBuffer.length );
+				waveArray = instancedArray( waveBuffer );
 
 				// read-only buffer
 
-				const waveNode = storageObject( new THREE.StorageInstancedBufferAttribute( waveBuffer, 1 ), 'float', waveBuffer.length ).toReadOnly();
+				const waveNode = instancedArray( waveBuffer );
+
+				// The Pixel Buffer Object (PBO) is required to get the GPU computed data to the CPU in the WebGL2 fallback.
+				// As used in `renderer.getArrayBufferAsync( waveArray.value )`.
+
+				waveNode.setPBO( true );
 
 				// params
 
@@ -137,7 +139,7 @@
 
 					// store
 
-					const waveStorageElementNode = waveStorageNode.element( instanceIndex );
+					const waveStorageElementNode = waveArray.element( instanceIndex );
 
 					waveStorageElementNode.assign( wave );
 

+ 11 - 23
examples/webgpu_compute_birds.html

@@ -35,7 +35,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { uniform, varying, vec4, add, sub, max, dot, sin, mat3, uint, negate, cameraProjectionMatrix, cameraViewMatrix, positionLocal, modelWorldMatrix, sqrt, attribute, property, float, storage, storageObject, Fn, If, cos, Loop, Continue, normalize, instanceIndex, length } from 'three/tsl';
+			import { uniform, varying, vec4, add, sub, max, dot, sin, mat3, uint, negate, attributeArray, cameraProjectionMatrix, cameraViewMatrix, positionLocal, modelWorldMatrix, sqrt, attribute, property, float, Fn, If, cos, Loop, Continue, normalize, instanceIndex, length } from 'three/tsl';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
@@ -167,7 +167,7 @@
 
 				//
 
-				renderer = new THREE.WebGPURenderer( { antialiasing: true } );
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
@@ -205,26 +205,12 @@
 
 				}
 
-				// Create storage buffer attributes.
-			
-				const positionBufferAttribute = new THREE.StorageBufferAttribute( positionArray, 3 );
-				const velocityBufferAttribute = new THREE.StorageBufferAttribute( velocityArray, 3 );
-				const phaseBufferAttribute = new THREE.StorageBufferAttribute( phaseArray, 1 );
-
 				// Labels applied to storage nodes and uniform nodes are reflected within the shader output,
 				// and are useful for debugging purposes.
 
-				// Access storage buffer attribute data from within shaders with a StorageNode.
-			
-				const positionStorage = storage( positionBufferAttribute, 'vec3', positionBufferAttribute.count ).label( 'positionStorage' );
-				const velocityStorage = storage( velocityBufferAttribute, 'vec3', velocityBufferAttribute.count ).label( 'velocityStorage' );
-				const phaseStorage = storage( phaseBufferAttribute, 'float', phaseBufferAttribute.count ).label( 'phaseStorage' );
-			
-				// Create read-only storage nodes. Storage nodes can only be accessed outside of compute shaders in a read-only state.
-			
-				const positionRead = storageObject( positionBufferAttribute, 'vec3', positionBufferAttribute.count ).toReadOnly();
-				const velocityRead = storageObject( velocityBufferAttribute, 'vec3', velocityBufferAttribute.count ).toReadOnly();
-				const phaseRead = storageObject( phaseBufferAttribute, 'float', phaseBufferAttribute.count ).toReadOnly();
+				const positionStorage = attributeArray( positionArray, 'vec3' ).label( 'positionStorage' );
+				const velocityStorage = attributeArray( velocityArray, 'vec3' ).label( 'velocityStorage' );
+				const phaseStorage = attributeArray( phaseArray, 'float' ).label( 'phaseStorage' );
 
 				// Define Uniforms. Uniforms only need to be defined once rather than per shader.
 			
@@ -252,8 +238,8 @@
 					const birdVertex = attribute( 'birdVertex' );
 
 					const position = positionLocal.toVar();
-					const newPhase = phaseRead.element( reference ).toVar();
-					const newVelocity = normalize( velocityRead.element( reference ) ).toVar();
+					const newPhase = phaseStorage.element( reference ).toVar();
+					const newVelocity = normalize( velocityStorage.element( reference ) ).toVar();
 			
 					If( birdVertex.equal( 4 ).or( birdVertex.equal( 7 ) ), () => {
 
@@ -289,7 +275,7 @@
 					);
 
 					const finalVert = maty.mul( matz ).mul( newPosition );
-					finalVert.addAssign( positionRead.element( reference ) );
+					finalVert.addAssign( positionStorage.element( reference ) );
 
 					return cameraProjectionMatrix.mul( cameraViewMatrix ).mul( finalVert );
 
@@ -297,9 +283,11 @@
 
 				birdMaterial.vertexNode = birdVertexTSL();
 				birdMaterial.side = THREE.DoubleSide;
+
 				const birdMesh = new THREE.Mesh( birdGeometry, birdMaterial );
 				birdMesh.rotation.y = Math.PI / 2;
 				birdMesh.matrixAutoUpdate = false;
+				birdMesh.frustumCulled = false;
 				birdMesh.updateMatrix();
 
 				// Define GPU Compute shaders.
@@ -417,7 +405,7 @@
 						velocity.assign( normalize( velocity ).mul( limit ) );
 			
 					} );
-			
+
 				} )().compute( BIRDS );
 
 				computePosition = Fn( () => {

+ 1 - 1
examples/webgpu_compute_geometry.html

@@ -61,7 +61,7 @@
 
 				// attributes
 
-				const positionAttribute = storage( positionBaseAttribute, 'vec3', count ).toReadOnly();
+				const positionAttribute = storage( positionBaseAttribute, 'vec3', count );
 				const positionStorageAttribute = storage( positionStorageBufferAttribute, 'vec3', count );
 
 				const speedAttribute = storage( speedBufferAttribute, 'vec3', count );

+ 4 - 6
examples/webgpu_compute_particles.html

@@ -37,7 +37,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { Fn, uniform, texture, instanceIndex, float, hash, vec3, storage, If } from 'three/tsl';
+			import { Fn, uniform, texture, instancedArray, instanceIndex, float, hash, vec3, If } from 'three/tsl';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import Stats from 'three/addons/libs/stats.module.js';
@@ -77,11 +77,9 @@
 
 				//
 
-				const createBuffer = () => storage( new THREE.StorageInstancedBufferAttribute( particleCount, 3 ), 'vec3', particleCount );
-
-				const positionBuffer = createBuffer();
-				const velocityBuffer = createBuffer();
-				const colorBuffer = createBuffer();
+				const positionBuffer = instancedArray( particleCount, 'vec3' );
+				const velocityBuffer = instancedArray( particleCount, 'vec3' );
+				const colorBuffer = instancedArray( particleCount, 'vec3' );
 
 				// compute
 

+ 5 - 7
examples/webgpu_compute_particles_rain.html

@@ -24,7 +24,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { Fn, texture, uv, uint, positionWorld, billboarding, time, hash, deltaTime, vec2, instanceIndex, positionGeometry, storage, If } from 'three/tsl';
+			import { Fn, texture, uv, uint, instancedArray, positionWorld, billboarding, time, hash, deltaTime, vec2, instanceIndex, positionGeometry, If } from 'three/tsl';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
@@ -94,12 +94,10 @@
 
 				//
 
-				const createBuffer = ( type = 'vec3' ) => storage( new THREE.StorageInstancedBufferAttribute( maxParticleCount, 3 ), type, maxParticleCount );
-
-				const positionBuffer = createBuffer();
-				const velocityBuffer = createBuffer();
-				const ripplePositionBuffer = createBuffer();
-				const rippleTimeBuffer = createBuffer();
+				const positionBuffer = instancedArray( maxParticleCount, 'vec3' );
+				const velocityBuffer = instancedArray( maxParticleCount, 'vec3' );
+				const ripplePositionBuffer = instancedArray( maxParticleCount, 'vec3' );
+				const rippleTimeBuffer = instancedArray( maxParticleCount, 'vec3' );
 
 				// compute
 

+ 5 - 7
examples/webgpu_compute_particles_snow.html

@@ -25,7 +25,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { Fn, texture, vec3, pass, color, uint, screenUV, positionWorld, positionLocal, time, vec2, hash, instanceIndex, storage, If } from 'three/tsl';
+			import { Fn, texture, vec3, pass, color, uint, screenUV, instancedArray, positionWorld, positionLocal, time, vec2, hash, instanceIndex, If } from 'three/tsl';
 			import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js';
 
 			import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js';
@@ -94,12 +94,10 @@
 
 				//
 
-				const createBuffer = ( type = 'vec3' ) => storage( new THREE.StorageInstancedBufferAttribute( maxParticleCount, type === 'vec4' ? 4 : 3 ), type, maxParticleCount );
-
-				const positionBuffer = createBuffer();
-				const scaleBuffer = createBuffer();
-				const staticPositionBuffer = createBuffer();
-				const dataBuffer = createBuffer( 'vec4' );
+				const positionBuffer = instancedArray( maxParticleCount, 'vec3' );
+				const scaleBuffer = instancedArray( maxParticleCount, 'vec3' );
+				const staticPositionBuffer = instancedArray( maxParticleCount, 'vec3' );
+				const dataBuffer = instancedArray( maxParticleCount, 'vec4' );
 
 				// compute
 

+ 12 - 21
examples/webgpu_compute_points.html

@@ -24,7 +24,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { Fn, uniform, storage, attribute, float, vec2, vec3, color, instanceIndex } from 'three/tsl';
+			import { Fn, uniform, instancedArray, float, vec2, vec3, color, instanceIndex } from 'three/tsl';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
@@ -45,23 +45,17 @@
 
 				// initialize particles
 
-				const particleNum = 300000;
-				const particleSize = 2; // vec2
+				const particlesCount = 300000;
 
-				// create buffers
-
-				const particleBuffer = new THREE.StorageInstancedBufferAttribute( particleNum, particleSize );
-				const velocityBuffer = new THREE.StorageInstancedBufferAttribute( particleNum, particleSize );
-
-				const particleBufferNode = storage( particleBuffer, 'vec2', particleNum );
-				const velocityBufferNode = storage( velocityBuffer, 'vec2', particleNum );
+				const particleArray = instancedArray( particlesCount, 'vec2' );
+				const velocityArray = instancedArray( particlesCount, 'vec2' );
 
 				// create function
 
 				const computeShaderFn = Fn( () => {
 
-					const particle = particleBufferNode.element( instanceIndex );
-					const velocity = velocityBufferNode.element( instanceIndex );
+					const particle = particleArray.element( instanceIndex );
+					const velocity = velocityArray.element( instanceIndex );
 
 					const pointer = uniform( pointerVector );
 					const limit = uniform( scaleVector );
@@ -82,7 +76,7 @@
 
 				// compute
 
-				computeNode = computeShaderFn().compute( particleNum );
+				computeNode = computeShaderFn().compute( particlesCount );
 				computeNode.onInit( ( { renderer } ) => {
 
 					const precomputeShaderNode = Fn( () => {
@@ -95,31 +89,28 @@
 						const velX = randomAngle.sin().mul( randomSpeed );
 						const velY = randomAngle.cos().mul( randomSpeed );
 
-						const velocity = velocityBufferNode.element( instanceIndex );
+						const velocity = velocityArray.element( instanceIndex );
 
 						velocity.xy = vec2( velX, velY );
 
 					} );
 
-					renderer.computeAsync( precomputeShaderNode().compute( particleNum ) );
+					renderer.computeAsync( precomputeShaderNode().compute( particlesCount ) );
 
 				} );
 
 				// use a compute shader to animate the point cloud's vertex data.
 
-				const particleNode = attribute( 'particle', 'vec2' );
-
 				const pointsGeometry = new THREE.BufferGeometry();
 				pointsGeometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 3 ), 3 ) ); // single vertex ( not triangle )
-				pointsGeometry.setAttribute( 'particle', particleBuffer ); // dummy the position points as instances
 				pointsGeometry.drawRange.count = 1; // force render points as instances ( not triangle )
 
 				const pointsMaterial = new THREE.PointsNodeMaterial();
-				pointsMaterial.colorNode = particleNode.add( color( 0xFFFFFF ) );
-				pointsMaterial.positionNode = particleNode;
+				pointsMaterial.colorNode = particleArray.element( instanceIndex ).add( color( 0xFFFFFF ) );
+				pointsMaterial.positionNode = particleArray.element( instanceIndex );
 
 				const mesh = new THREE.Points( pointsGeometry, pointsMaterial );
-				mesh.count = particleNum;
+				mesh.count = particlesCount;
 				scene.add( mesh );
 
 				renderer = new THREE.WebGPURenderer( { antialias: true } );

+ 5 - 5
examples/webgpu_compute_texture_pingpong.html

@@ -24,7 +24,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { storageTexture, wgslFn, code, instanceIndex, uniform } from 'three/tsl';
+			import { storageTexture, wgslFn, code, instanceIndex, uniform, NodeAccess } from 'three/tsl';
 
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 
@@ -72,10 +72,10 @@
 
 				const wgslFormat = hdr ? 'rgba16float' : 'rgba8unorm';
 			
-				const readPing = storageTexture( pingTexture ).setAccess( 'read-only' );
-				const writePing = storageTexture( pingTexture ).setAccess( 'write-only' );
-				const readPong = storageTexture( pongTexture ).setAccess( 'read-only' );
-				const writePong = storageTexture( pongTexture ).setAccess( 'write-only' );
+				const readPing = storageTexture( pingTexture ).setAccess( NodeAccess.READ_ONLY );
+				const writePing = storageTexture( pingTexture ).setAccess( NodeAccess.WRITE_ONLY );
+				const readPong = storageTexture( pongTexture ).setAccess( NodeAccess.READ_ONLY );
+				const writePong = storageTexture( pongTexture ).setAccess( NodeAccess.WRITE_ONLY );
 
 				// compute init
 

+ 10 - 19
examples/webgpu_compute_water.html

@@ -27,7 +27,7 @@
 
 			import * as THREE from 'three';
 
-			import { color, instanceIndex, If, varyingProperty, uint, int, negate, floor, float, length, clamp, vec2, cos, vec3, vertexIndex, Fn, uniform, storageObject, min, max, positionLocal, transformNormalToView } from 'three/tsl';
+			import { color, instanceIndex, If, varyingProperty, uint, int, negate, floor, float, length, clamp, vec2, cos, vec3, vertexIndex, Fn, uniform, instancedArray, min, max, positionLocal, transformNormalToView } from 'three/tsl';
 			import { SimplexNoise } from 'three/addons/math/SimplexNoise.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import Stats from 'three/addons/libs/stats.module.js';
@@ -126,13 +126,8 @@
 
 				}
 
-				const heightBufferAttribute = new THREE.StorageBufferAttribute( heightArray, 1 );
-				const prevHeightBufferAttribute = new THREE.StorageBufferAttribute( prevHeightArray, 1 );
-
-				const heightStorage = storageObject( heightBufferAttribute, 'float', heightBufferAttribute.count ).label( 'Height' );
-				const prevHeightStorage = storageObject( prevHeightBufferAttribute, 'float', prevHeightBufferAttribute.count ).label( 'PrevHeight' );
-
-				const heightRead = storageObject( heightBufferAttribute, 'float', heightBufferAttribute.count ).toReadOnly().label( 'HeightRead' );
+				const heightStorage = instancedArray( heightArray ).label( 'Height' );
+				const prevHeightStorage = instancedArray( prevHeightArray ).label( 'PrevHeight' );
 
 				// Get Indices of Neighbor Values of an Index in the Simulation Grid
 				const getNeighborIndicesTSL = ( index ) => {
@@ -255,11 +250,11 @@
 				waterMaterial.positionNode = Fn( () => {
 
 					// To correct the lighting as our mesh undulates, we have to reassign the normals in the position shader.
-					const { normalX, normalY } = getNormalsFromHeightTSL( vertexIndex, heightRead );
+					const { normalX, normalY } = getNormalsFromHeightTSL( vertexIndex, heightStorage );
 
 					varyingProperty( 'vec3', 'v_normalView' ).assign( transformNormalToView( vec3( normalX, negate( normalY ), 1.0 ) ) );
 
-					return vec3( positionLocal.x, positionLocal.y, heightRead.element( vertexIndex ) );
+					return vec3( positionLocal.x, positionLocal.y, heightStorage.element( vertexIndex ) );
 
 				} )();
 
@@ -300,12 +295,8 @@
 				sphereVelocityArray.fill( 0.0 );
 
 				// Sphere Instance Storage
-				const sphereInstancePositionAttribute = new THREE.StorageInstancedBufferAttribute( spherePositionArray, 3 );
-				const sphereInstancePositionStorage = storageObject( sphereInstancePositionAttribute, 'vec3', sphereInstancePositionAttribute.count ).label( 'SpherePosition' );
-				const sphereInstancePositionRead = storageObject( sphereInstancePositionAttribute, 'vec3', sphereInstancePositionAttribute.count ).toReadOnly();
-
-				const sphereVelocityAttribute = new THREE.StorageInstancedBufferAttribute( sphereVelocityArray, 2 );
-				const sphereVelocityStorage = storageObject( sphereVelocityAttribute, 'vec2', sphereVelocityAttribute.count ).label( 'SphereVelocity' );
+				const sphereInstancePositionStorage = instancedArray( spherePositionArray, 'vec3' ).label( 'SpherePosition' );
+				const sphereVelocityStorage = instancedArray( sphereVelocityArray, 'vec2' ).label( 'SphereVelocity' );
 
 				computeSphere = Fn( () => {
 
@@ -330,13 +321,13 @@
 					const heightInstanceIndex = zCoord.mul( WIDTH ).add( xCoord );
 
 					// Set to read-only to be safe, even if it's not strictly necessary for compute access.
-					const height = heightRead.element( heightInstanceIndex );
+					const height = heightStorage.element( heightInstanceIndex );
 
 					// Assign height to sphere position
 					instancePosition.y.assign( height );
 
 					// Calculate normal of the water mesh at this location.
-					const { normalX, normalY } = getNormalsFromHeightTSL( heightInstanceIndex, heightRead );
+					const { normalX, normalY } = getNormalsFromHeightTSL( heightInstanceIndex, heightStorage );
 
 					normalX.mulAssign( 0.1 );
 					normalY.mulAssign( 0.1 );
@@ -380,7 +371,7 @@
 
 				sphereMaterial.positionNode = Fn( () => {
 
-					const instancePosition = sphereInstancePositionRead.element( instanceIndex );
+					const instancePosition = sphereInstancePositionStorage.element( instanceIndex );
 
 					const newPosition = positionLocal.add( instancePosition );
 

+ 3 - 3
examples/webgpu_tsl_compute_attractors_particles.html

@@ -25,7 +25,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { float, If, PI, color, cos, instanceIndex, Loop, mix, mod, sin, storage, Fn, uint, uniform, uniformArray, hash, vec3, vec4 } from 'three/tsl';
+			import { float, If, PI, color, cos, instanceIndex, Loop, mix, mod, sin, instancedArray, Fn, uint, uniform, uniformArray, hash, vec3, vec4 } from 'three/tsl';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
@@ -154,8 +154,8 @@
 				const colorA = uniform( color( '#5900ff' ) );
 				const colorB = uniform( color( '#ffa575' ) );
 
-				const positionBuffer = storage( new THREE.StorageInstancedBufferAttribute( count, 3 ), 'vec3', count );
-				const velocityBuffer = storage( new THREE.StorageInstancedBufferAttribute( count, 3 ), 'vec3', count );
+				const positionBuffer = instancedArray( count, 'vec3' );
+				const velocityBuffer = instancedArray( count, 'vec3' );
 
 				const sphericalToVec3 = Fn( ( [ phi, theta ] ) => {
 

+ 1 - 0
src/nodes/TSL.js

@@ -47,6 +47,7 @@ export * from './tsl/TSLBase.js';
 
 // accessors
 export * from './accessors/AccessorsUtils.js';
+export * from './accessors/Arrays.js';
 export * from './accessors/UniformArrayNode.js';
 export * from './accessors/Bitangent.js';
 export * from './accessors/BufferAttributeNode.js';

+ 27 - 0
src/nodes/accessors/Arrays.js

@@ -0,0 +1,27 @@
+import StorageInstancedBufferAttribute from '../../renderers/common/StorageInstancedBufferAttribute.js';
+import StorageBufferAttribute from '../../renderers/common/StorageBufferAttribute.js';
+import { storage } from './StorageBufferNode.js';
+import { getLengthFromType } from '../core/NodeUtils.js';
+
+export const attributeArray = ( count, type = 'float' ) => {
+
+	const itemSize = getLengthFromType( type );
+
+	const buffer = new StorageBufferAttribute( count, itemSize );
+	const node = storage( buffer, type, count );
+
+	return node;
+
+};
+
+
+export const instancedArray = ( count, type = 'float' ) => {
+
+	const itemSize = getLengthFromType( type );
+
+	const buffer = new StorageInstancedBufferAttribute( count, itemSize );
+	const node = storage( buffer, type, count );
+
+	return node;
+
+};

+ 12 - 4
src/nodes/accessors/StorageBufferNode.js

@@ -2,7 +2,8 @@ import BufferNode from './BufferNode.js';
 import { bufferAttribute } from './BufferAttributeNode.js';
 import { nodeObject, varying } from '../tsl/TSLBase.js';
 import { storageElement } from '../utils/StorageArrayElementNode.js';
-import { GPUBufferBindingType } from '../../renderers/webgpu/utils/WebGPUConstants.js';
+import { NodeAccess } from '../core/constants.js';
+import { getTypeFromLength } from '../core/NodeUtils.js';
 
 class StorageBufferNode extends BufferNode {
 
@@ -12,13 +13,20 @@ class StorageBufferNode extends BufferNode {
 
 	}
 
-	constructor( value, bufferType, bufferCount = 0 ) {
+	constructor( value, bufferType = null, bufferCount = 0 ) {
+
+		if ( bufferType === null && ( value.isStorageBufferAttribute || value.isStorageInstancedBufferAttribute ) ) {
+
+			bufferType = getTypeFromLength( value.itemSize );
+			bufferCount = value.count;
+
+		}
 
 		super( value, bufferType, bufferCount );
 
 		this.isStorageBufferNode = true;
 
-		this.access = GPUBufferBindingType.Storage;
+		this.access = NodeAccess.READ_WRITE;
 		this.isAtomic = false;
 
 		this.bufferObject = false;
@@ -94,7 +102,7 @@ class StorageBufferNode extends BufferNode {
 
 	toReadOnly() {
 
-		return this.setAccess( GPUBufferBindingType.ReadOnlyStorage );
+		return this.setAccess( NodeAccess.READ_ONLY );
 
 	}
 

+ 10 - 4
src/nodes/accessors/StorageTextureNode.js

@@ -1,6 +1,6 @@
 import TextureNode from './TextureNode.js';
 import { nodeProxy } from '../tsl/TSLBase.js';
-import { GPUStorageTextureAccess } from '../../renderers/webgpu/utils/WebGPUConstants.js';
+import { NodeAccess } from '../core/constants.js';
 
 class StorageTextureNode extends TextureNode {
 
@@ -18,7 +18,7 @@ class StorageTextureNode extends TextureNode {
 
 		this.isStorageTextureNode = true;
 
-		this.access = GPUStorageTextureAccess.WriteOnly;
+		this.access = NodeAccess.WRITE_ONLY;
 
 	}
 
@@ -62,15 +62,21 @@ class StorageTextureNode extends TextureNode {
 
 	}
 
+	toReadWrite() {
+
+		return this.setAccess( NodeAccess.READ_WRITE );
+
+	}
+
 	toReadOnly() {
 
-		return this.setAccess( GPUStorageTextureAccess.ReadOnly );
+		return this.setAccess( NodeAccess.READ_ONLY );
 
 	}
 
 	toWriteOnly() {
 
-		return this.setAccess( GPUStorageTextureAccess.WriteOnly );
+		return this.setAccess( NodeAccess.WRITE_ONLY );
 
 	}
 

+ 2 - 9
src/nodes/core/NodeBuilder.js

@@ -7,6 +7,7 @@ import NodeCache from './NodeCache.js';
 import ParameterNode from './ParameterNode.js';
 import FunctionNode from '../code/FunctionNode.js';
 import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
+import { getTypeFromLength } from './NodeUtils.js';
 import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js';
 
 import {
@@ -35,14 +36,6 @@ import { IntType, UnsignedIntType, LinearFilter, LinearMipmapNearestFilter, Near
 
 const rendererCache = new WeakMap();
 
-const typeFromLength = new Map( [
-	[ 2, 'vec2' ],
-	[ 3, 'vec3' ],
-	[ 4, 'vec4' ],
-	[ 9, 'mat3' ],
-	[ 16, 'mat4' ]
-] );
-
 const typeFromArray = new Map( [
 	[ Int8Array, 'int' ],
 	[ Int16Array, 'int' ],
@@ -708,7 +701,7 @@ class NodeBuilder {
 
 		if ( length === 1 ) return componentType;
 
-		const baseType = typeFromLength.get( length );
+		const baseType = getTypeFromLength( length );
 		const prefix = componentType === 'float' ? '' : componentType[ 0 ];
 
 		return prefix + baseType;

+ 28 - 0
src/nodes/core/NodeUtils.js

@@ -117,6 +117,34 @@ export function* getNodeChildren( node, toJSON = false ) {
 
 }
 
+const typeFromLength = /*@__PURE__*/ new Map( [
+	[ 1, 'float' ],
+	[ 2, 'vec2' ],
+	[ 3, 'vec3' ],
+	[ 4, 'vec4' ],
+	[ 9, 'mat3' ],
+	[ 16, 'mat4' ]
+] );
+
+export function getTypeFromLength( length ) {
+
+	return typeFromLength.get( length );
+
+}
+
+export function getLengthFromType( type ) {
+
+	if ( /float|int|uint/.test( type ) ) return 1;
+	if ( /vec2/.test( type ) ) return 2;
+	if ( /vec3/.test( type ) ) return 3;
+	if ( /vec4/.test( type ) ) return 4;
+	if ( /mat3/.test( type ) ) return 9;
+	if ( /mat4/.test( type ) ) return 16;
+
+	console.error( 'THREE.TSL: Unsupported type:', type );
+
+}
+
 export function getValueType( value ) {
 
 	if ( value === undefined || value === null ) return null;

+ 6 - 0
src/nodes/core/constants.js

@@ -22,6 +22,12 @@ export const NodeType = {
 	MATRIX4: 'mat4'
 };
 
+export const NodeAccess = {
+	READ_ONLY: 'readOnly',
+	WRITE_ONLY: 'writeOnly',
+	READ_WRITE: 'readWrite',
+};
+
 export const defaultShaderStages = [ 'fragment', 'vertex' ];
 export const defaultBuildStages = [ 'setup', 'analyze', 'generate' ];
 export const shaderStages = [ ...defaultShaderStages, 'compute' ];

+ 2 - 3
src/renderers/common/nodes/NodeStorageBuffer.js

@@ -1,5 +1,5 @@
 import StorageBuffer from '../StorageBuffer.js';
-import { GPUBufferBindingType } from '../../webgpu/utils/WebGPUConstants.js';
+import { NodeAccess } from '../../../nodes/core/constants.js';
 
 let _id = 0;
 
@@ -10,10 +10,9 @@ class NodeStorageBuffer extends StorageBuffer {
 		super( 'StorageBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
 
 		this.nodeUniform = nodeUniform;
-		this.access = nodeUniform ? nodeUniform.access : GPUBufferBindingType.Storage;
+		this.access = nodeUniform ? nodeUniform.access : NodeAccess.READ_WRITE;
 		this.groupNode = groupNode;
 
-
 	}
 
 	get buffer() {

+ 0 - 2
src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js

@@ -124,7 +124,6 @@ ${ flowData.code }
 
 			let format = isInteger ? RedIntegerFormat : RedFormat;
 
-
 			if ( itemSize === 2 ) {
 
 				format = isInteger ? RGIntegerFormat : RGFormat;
@@ -202,7 +201,6 @@ ${ flowData.code }
 
 		}
 
-
 		const nodeUniform = this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label );
 		const textureName = this.getPropertyName( nodeUniform );
 

+ 21 - 41
src/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -11,7 +11,7 @@ import { NodeBuilder, CodeNode } from '../../../nodes/Nodes.js';
 import { getFormat } from '../utils/WebGPUTextureUtils.js';
 
 import WGSLNodeParser from './WGSLNodeParser.js';
-import { GPUBufferBindingType, GPUStorageTextureAccess } from '../utils/WebGPUConstants.js';
+import { NodeAccess } from '../../../nodes/core/constants.js';
 
 import VarNode from '../../../nodes/core/VarNode.js';
 import ExpressionNode from '../../../nodes/code/ExpressionNode.js';
@@ -21,6 +21,12 @@ import { NoColorSpace, FloatType, RepeatWrapping, ClampToEdgeWrapping, MirroredR
 // GPUShaderStage is not defined in browsers not supporting WebGPU
 const GPUShaderStage = self.GPUShaderStage;
 
+const accessNames = {
+	[ NodeAccess.READ_ONLY ]: 'read',
+	[ NodeAccess.WRITE_ONLY ]: 'write',
+	[ NodeAccess.READ_WRITE ]: 'read_write'
+};
+
 const wrapNames = {
 	[ RepeatWrapping ]: 'repeat',
 	[ ClampToEdgeWrapping ]: 'clamp',
@@ -528,46 +534,18 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 	}
 
-	getStorageAccess( node ) {
-
-		if ( node.isStorageTextureNode ) {
-
-			switch ( node.access ) {
-
-				case GPUStorageTextureAccess.ReadOnly:
-
-					return 'read';
+	getNodeAccess( node, shaderStage ) {
 
-				case GPUStorageTextureAccess.WriteOnly:
+		if ( shaderStage !== 'compute' )
+			return NodeAccess.READ_ONLY;
 
-					return 'write';
-
-				default:
-
-					return 'read_write';
-
-			}
-
-		} else {
+		return node.access;
 
-			switch ( node.access ) {
-
-				case GPUBufferBindingType.Storage:
-
-					return 'read_write';
-
-
-				case GPUBufferBindingType.ReadOnlyStorage:
-
-					return 'read';
-
-				default:
+	}
 
-					return 'write';
+	getStorageAccess( node, shaderStage ) {
 
-			}
-
-		}
+		return accessNames[ this.getNodeAccess( node, shaderStage ) ];
 
 	}
 
@@ -589,17 +567,19 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 				let texture = null;
 
+				const access = this.getNodeAccess( node, shaderStage );
+
 				if ( type === 'texture' || type === 'storageTexture' ) {
 
-					texture = new NodeSampledTexture( uniformNode.name, uniformNode.node, group, node.access ? node.access : null );
+					texture = new NodeSampledTexture( uniformNode.name, uniformNode.node, group, access );
 
 				} else if ( type === 'cubeTexture' ) {
 
-					texture = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node, group, node.access ? node.access : null );
+					texture = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node, group, access );
 
 				} else if ( type === 'texture3D' ) {
 
-					texture = new NodeSampledTexture3D( uniformNode.name, uniformNode.node, group, node.access ? node.access : null );
+					texture = new NodeSampledTexture3D( uniformNode.name, uniformNode.node, group, access );
 
 				}
 
@@ -1165,7 +1145,7 @@ ${ flowData.code }
 				} else if ( uniform.node.isStorageTextureNode === true ) {
 
 					const format = getFormat( texture );
-					const access = this.getStorageAccess( uniform.node );
+					const access = this.getStorageAccess( uniform.node, shaderStage );
 
 					textureType = `texture_storage_2d<${ format }, ${ access }>`;
 
@@ -1188,7 +1168,7 @@ ${ flowData.code }
 				const bufferCountSnippet = bufferCount > 0 && uniform.type === 'buffer' ? ', ' + bufferCount : '';
 				const bufferTypeSnippet = bufferNode.isAtomic ? `atomic<${bufferType}>` : `${bufferType}`;
 				const bufferSnippet = `\t${ uniform.name } : array< ${ bufferTypeSnippet }${ bufferCountSnippet } >\n`;
-				const bufferAccessMode = bufferNode.isStorageBufferNode ? `storage, ${ this.getStorageAccess( bufferNode ) }` : 'uniform';
+				const bufferAccessMode = bufferNode.isStorageBufferNode ? `storage, ${ this.getStorageAccess( bufferNode, shaderStage ) }` : 'uniform';
 
 				bufferSnippets.push( this._getWGSLStructBinding( 'NodeBuffer_' + bufferNode.id, bufferSnippet, bufferAccessMode, uniformIndexes.binding ++, uniformIndexes.group ) );
 

+ 39 - 4
src/renderers/webgpu/utils/WebGPUBindingUtils.js

@@ -1,8 +1,9 @@
 import {
-	GPUTextureAspect, GPUTextureViewDimension, GPUTextureSampleType
+	GPUTextureAspect, GPUTextureViewDimension, GPUTextureSampleType, GPUBufferBindingType, GPUStorageTextureAccess
 } from './WebGPUConstants.js';
 
 import { FloatType, IntType, UnsignedIntType } from '../../../constants.js';
+import { NodeAccess } from '../../../nodes/core/constants.js';
 
 class WebGPUBindingUtils {
 
@@ -35,7 +36,25 @@ class WebGPUBindingUtils {
 
 				if ( binding.isStorageBuffer ) {
 
-					buffer.type = binding.access;
+					if ( binding.visibility & 4 ) {
+
+						// compute
+
+						if ( binding.access === NodeAccess.READ_WRITE || binding.access === NodeAccess.WRITE_ONLY ) {
+
+							buffer.type = GPUBufferBindingType.Storage;
+
+						} else {
+
+							buffer.type = GPUBufferBindingType.ReadOnlyStorage;
+
+						}
+
+					} else {
+
+						buffer.type = GPUBufferBindingType.ReadOnlyStorage;
+
+					}
 
 				}
 
@@ -63,10 +82,26 @@ class WebGPUBindingUtils {
 
 			} else if ( binding.isSampledTexture && binding.store ) {
 
-				const format = this.backend.get( binding.texture ).texture.format;
+				const storageTexture = {}; // GPUStorageTextureBindingLayout
+				storageTexture.format = this.backend.get( binding.texture ).texture.format;
+
 				const access = binding.access;
 
-				bindingGPU.storageTexture = { format, access }; // GPUStorageTextureBindingLayout
+				if ( access === NodeAccess.READ_WRITE ) {
+
+					storageTexture.access = GPUStorageTextureAccess.ReadWrite;
+
+				} else if ( access === NodeAccess.WRITE_ONLY ) {
+
+					storageTexture.access = GPUStorageTextureAccess.WriteOnly;
+
+				} else {
+
+					storageTexture.access = GPUStorageTextureAccess.ReadOnly;
+
+				}
+
+				bindingGPU.storageTexture = storageTexture;
 
 			} else if ( binding.isSampledTexture ) {
 

+ 1 - 0
test/e2e/puppeteer.js

@@ -113,6 +113,7 @@ const exceptionList = [
 	// Awaiting for WebGL backend support
 	'webgpu_clearcoat',
 	'webgpu_compute_audio',
+	"webgpu_compute_birds",
 	'webgpu_compute_texture',
 	'webgpu_compute_texture_pingpong',
 	"webgpu_compute_water",

粤ICP备19079148号