|
62 | 62 | scene.background = new THREE.Color( 0x000000 ); |
63 | 63 |
|
64 | 64 | const particleNum = 65000; // 16-bit limit |
65 | | - const particleSize = 3; |
| 65 | + const particleSize = 4; // 16-byte stride align |
66 | 66 |
|
67 | 67 | const particleArray = new Float32Array( particleNum * particleSize ); |
68 | 68 | const velocityArray = new Float32Array( particleNum * particleSize ); |
69 | 69 |
|
70 | | - for ( let i = 0; i < particleArray.length; i += 3 ) { |
| 70 | + for ( let i = 0; i < particleArray.length; i += particleSize ) { |
71 | 71 |
|
72 | 72 | const r = Math.random() * 0.01 + 0.0005; |
73 | 73 | const degree = Math.random() * 360; |
|
76 | 76 |
|
77 | 77 | } |
78 | 78 |
|
79 | | - const particleBuffer = new WebGPUStorageBuffer( 'particle', new THREE.BufferAttribute( particleArray, 3 ) ); |
80 | | - const velocityBuffer = new WebGPUStorageBuffer( 'velocity', new THREE.BufferAttribute( velocityArray, 3 ) ); |
| 79 | + const particleBuffer = new WebGPUStorageBuffer( 'particle', new THREE.BufferAttribute( particleArray, particleSize ) ); |
| 80 | + const velocityBuffer = new WebGPUStorageBuffer( 'velocity', new THREE.BufferAttribute( velocityArray, particleSize ) ); |
81 | 81 |
|
82 | 82 | const scaleUniformLength = WebGPUBufferUtils.getVectorLength( 2, 3 ); // two vector3 for array |
83 | 83 |
|
84 | | - scaleUniformBuffer = new WebGPUUniformBuffer( 'scaleUniform', new Float32Array( scaleUniformLength ) ); |
| 84 | + scaleUniformBuffer = new WebGPUUniformBuffer( 'scaleUniform', new Float32Array( scaleUniformLength ) ); |
85 | 85 |
|
86 | 86 | pointer = new THREE.Vector2( - 10.0, - 10.0 ); // Out of bounds first |
87 | 87 |
|
|
98 | 98 | pointerGroup |
99 | 99 | ]; |
100 | 100 |
|
101 | | - const computeShader = /* glsl */`#version 450 |
102 | | - #define PARTICLE_NUM ${particleNum} |
103 | | - #define PARTICLE_SIZE ${particleSize} |
104 | | - #define ROOM_SIZE 1.0 |
105 | | - #define POINTER_SIZE 0.1 |
| 101 | + const computeShader = ` |
106 | 102 |
|
107 | | - // Limitation for now: the order should be the same as bindings order |
| 103 | + // |
| 104 | + // Buffer |
| 105 | + // |
108 | 106 |
|
109 | | - layout(set = 0, binding = 0) buffer Particle { |
110 | | - float particle[ PARTICLE_NUM * PARTICLE_SIZE ]; |
111 | | - } particle; |
| 107 | + [[ block ]] |
| 108 | + struct Particle { |
| 109 | + value : array< vec4<f32> >; |
| 110 | + }; |
| 111 | + [[ binding( 0 ), group( 0 ) ]] |
| 112 | + var<storage,read_write> particle : Particle; |
112 | 113 |
|
113 | | - layout(set = 0, binding = 1) buffer Velocity { |
114 | | - float velocity[ PARTICLE_NUM * PARTICLE_SIZE ]; |
115 | | - } velocity; |
| 114 | + [[ block ]] |
| 115 | + struct Velocity { |
| 116 | + value : array< vec4<f32> >; |
| 117 | + }; |
| 118 | + [[ binding( 1 ), group( 0 ) ]] |
| 119 | + var<storage,read_write> velocity : Velocity; |
116 | 120 |
|
117 | | - layout(set = 0, binding = 2) uniform Scale { |
118 | | - vec3 value[2]; |
119 | | - } scaleUniform; |
| 121 | + // |
| 122 | + // Uniforms |
| 123 | + // |
120 | 124 |
|
121 | | - layout(set = 0, binding = 3) uniform MouseUniforms { |
122 | | - vec2 pointer; |
123 | | - } mouseUniforms; |
| 125 | + [[ block ]] |
| 126 | + struct Scale { |
| 127 | + value : array< vec3<f32>, 2 >; |
| 128 | + }; |
| 129 | + [[ binding( 2 ), group( 0 ) ]] |
| 130 | + var<uniform> scaleUniform : Scale; |
124 | 131 |
|
125 | | - void main() { |
126 | | - uint index = gl_GlobalInvocationID.x; |
127 | | - if ( index >= PARTICLE_NUM ) { return; } |
| 132 | + [[block]] |
| 133 | + struct MouseUniforms { |
| 134 | + pointer : vec2<f32>; |
| 135 | + }; |
| 136 | + [[ binding( 3 ), group( 0 ) ]] |
| 137 | + var<uniform> mouseUniforms : MouseUniforms; |
128 | 138 |
|
129 | | - vec3 position = vec3( |
130 | | - particle.particle[ index * 3 + 0 ] + velocity.velocity[ index * 3 + 0 ], |
131 | | - particle.particle[ index * 3 + 1 ] + velocity.velocity[ index * 3 + 1 ], |
132 | | - particle.particle[ index * 3 + 2 ] + velocity.velocity[ index * 3 + 2 ] |
133 | | - ); |
| 139 | + [[ stage( compute ), workgroup_size( 64 ) ]] |
| 140 | + fn main( [[builtin(global_invocation_id)]] id : vec3<u32> ) { |
134 | 141 |
|
135 | | - if ( abs( position.x ) >= ROOM_SIZE ) { |
| 142 | + // get particle index |
136 | 143 |
|
137 | | - velocity.velocity[ index * 3 + 0 ] = - velocity.velocity[ index * 3 + 0 ]; |
| 144 | + let index : u32 = id.x * 3u; |
138 | 145 |
|
139 | | - } |
| 146 | + // update speed |
| 147 | +
|
| 148 | + var position : vec4<f32> = particle.value[ index ] + velocity.value[ index ]; |
| 149 | +
|
| 150 | + // update limit |
| 151 | +
|
| 152 | + let limit : vec2<f32> = scaleUniform.value[ 0 ].xy; |
| 153 | +
|
| 154 | + if ( abs( position.x ) >= limit.x ) { |
| 155 | +
|
| 156 | + if ( position.x > 0.0 ) { |
| 157 | +
|
| 158 | + position.x = limit.x; |
| 159 | +
|
| 160 | + } else { |
| 161 | +
|
| 162 | + position.x = -limit.x; |
140 | 163 |
|
141 | | - if ( abs( position.y ) >= ROOM_SIZE ) { |
| 164 | + } |
142 | 165 |
|
143 | | - velocity.velocity[ index * 3 + 1 ] = - velocity.velocity[ index * 3 + 1 ]; |
| 166 | + velocity.value[ index ].x = - velocity.value[ index ].x; |
144 | 167 |
|
145 | 168 | } |
146 | 169 |
|
147 | | - if ( abs( position.z ) >= ROOM_SIZE ) { |
| 170 | + if ( abs( position.y ) >= limit.y ) { |
148 | 171 |
|
149 | | - velocity.velocity[ index * 3 + 2 ] = - velocity.velocity[ index * 3 + 2 ]; |
| 172 | + if ( position.y > 0.0 ) { |
| 173 | +
|
| 174 | + position.y = limit.y; |
| 175 | +
|
| 176 | + } else { |
| 177 | +
|
| 178 | + position.y = -limit.y; |
| 179 | +
|
| 180 | + } |
| 181 | +
|
| 182 | + velocity.value[ index ].y = - velocity.value[ index ].y; |
150 | 183 |
|
151 | 184 | } |
152 | 185 |
|
153 | | - float dx = mouseUniforms.pointer.x - position.x; |
154 | | - float dy = mouseUniforms.pointer.y - position.y; |
155 | | - float distanceFromPointer = sqrt( dx * dx + dy * dy ); |
| 186 | + // update mouse |
| 187 | +
|
| 188 | + let POINTER_SIZE : f32 = .1; |
| 189 | +
|
| 190 | + let dx : f32 = mouseUniforms.pointer.x - position.x; |
| 191 | + let dy : f32 = mouseUniforms.pointer.y - position.y; |
| 192 | + let distanceFromPointer : f32 = sqrt( dx * dx + dy * dy ); |
156 | 193 |
|
157 | 194 | if ( distanceFromPointer <= POINTER_SIZE ) { |
158 | 195 |
|
|
162 | 199 |
|
163 | 200 | } |
164 | 201 |
|
165 | | - particle.particle[ index * 3 + 0 ] = position.x * scaleUniform.value[0].x; |
166 | | - particle.particle[ index * 3 + 1 ] = position.y * scaleUniform.value[0].y; |
167 | | - particle.particle[ index * 3 + 2 ] = position.z * scaleUniform.value[0].z; |
| 202 | + // update buffer |
| 203 | +
|
| 204 | + particle.value[ index ] = position; |
168 | 205 |
|
169 | 206 | } |
| 207 | +
|
170 | 208 | `; |
171 | 209 |
|
172 | 210 | computeParams.push( { |
|
182 | 220 | ); |
183 | 221 |
|
184 | 222 | const pointsMaterial = new Nodes.PointsNodeMaterial(); |
185 | | - pointsMaterial.colorNode = new Nodes.OperatorNode( '+', new Nodes.PositionNode(), new Nodes.ColorNode( new THREE.Color( 0x0000FF ) ) ); |
| 223 | + pointsMaterial.colorNode = new Nodes.OperatorNode( '+', new Nodes.PositionNode(), new Nodes.ColorNode( new THREE.Color( 0xFFFFFF ) ) ); |
186 | 224 |
|
187 | 225 | const mesh = new THREE.Points( pointsGeometry, pointsMaterial ); |
188 | 226 | scene.add( mesh ); |
|
199 | 237 |
|
200 | 238 | const gui = new GUI(); |
201 | 239 |
|
202 | | - gui.add( scaleVector, 'x', 0.9, 1.1, 0.01 ); |
203 | | - gui.add( scaleVector, 'y', 0.9, 1.1, 0.01 ); |
204 | | - gui.add( scaleVector, 'z', 0.9, 1.1, 0.01 ); |
| 240 | + gui.add( scaleVector, 'x', 0, 1, 0.01 ); |
| 241 | + gui.add( scaleVector, 'y', 0, 1, 0.01 ); |
205 | 242 |
|
206 | 243 | return renderer.init(); |
207 | 244 |
|
|
0 commit comments