|
34 | 34 | <script type="module"> |
35 | 35 |
|
36 | 36 | import * as THREE from 'three/webgpu'; |
37 | | - import { struct, storage, wgslFn, instanceIndex, time, varyingProperty, attribute } from 'three/tsl'; |
| 37 | + import { struct, storage, sin, cross, normalize, abs, mix, Fn, vec4, max, pow, time, varyingProperty, attribute, uint, atomicStore } from 'three/tsl'; |
38 | 38 |
|
39 | 39 | import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; |
40 | 40 |
|
|
138 | 138 | offset: 'uint' |
139 | 139 | }, 'DrawBuffer' ); |
140 | 140 |
|
141 | | - const writeDrawBuffer = wgslFn( ` |
142 | | - fn compute( |
143 | | - index: u32, |
144 | | - drawBuffer: ptr<storage, DrawBuffer, read_write>, |
145 | | - instances: f32, |
146 | | - time: f32, |
147 | | - ) -> void { |
| 141 | + const drawStorage = storage( drawBuffer, drawBufferStruct, drawBuffer.count ); |
148 | 142 |
|
149 | | - let instanceCount = max( instances * pow( sin( time * 0.5 ) + 1, 4.0 ), 100 ); |
| 143 | + computeDrawBuffer = Fn( () => { |
150 | 144 |
|
151 | | - atomicStore( &drawBuffer.instanceCount, u32( instanceCount ) ); |
152 | | - } |
153 | | - ` ); |
| 145 | + const halfTime = sin( time.mul( 0.5 ) ); |
154 | 146 |
|
155 | | - computeDrawBuffer = writeDrawBuffer( { |
156 | | - drawBuffer: storage( drawBuffer, drawBufferStruct, drawBuffer.count ), |
157 | | - instances: instances, |
158 | | - index: instanceIndex, |
159 | | - time: time |
160 | | - } ).compute( instances ); // not necessary in this case but normally one wants to run through all instances |
| 147 | + const instanceCount = max( ( pow( halfTime.add( 1 ), 4.0 ) ).mul( instances ), 100 ).toVar( 'instanceCount' ); |
| 148 | + atomicStore( drawStorage.get( 'instanceCount' ), instanceCount ); |
| 149 | + |
| 150 | + } )().compute( instances ); |
161 | 151 |
|
162 | | - const initDrawBuffer = wgslFn( ` |
163 | | - fn compute( |
164 | | - drawBuffer: ptr< storage, DrawBuffer, read_write >, |
165 | | - ) -> void { |
| 152 | + computeInitDrawBuffer = Fn( () => { |
166 | 153 |
|
167 | | - drawBuffer.vertexCount = 3u; |
168 | | - atomicStore(&drawBuffer.instanceCount, 0u); |
169 | | - drawBuffer.firstVertex = 0u; |
170 | | - drawBuffer.firstInstance = 0u; |
171 | | - drawBuffer.offset = 0u; |
172 | | - } |
173 | | - ` ); |
| 154 | + const drawInfo = drawStorage; |
| 155 | + |
| 156 | + drawInfo.get( 'vertexCount' ).assign( 3 ); |
| 157 | + atomicStore( drawInfo.get( 'instanceCount' ), uint( 0 ) ); |
| 158 | + drawInfo.get( 'firstVertex' ).assign( 0 ); |
| 159 | + drawInfo.get( 'firstInstance' ).assign( 0 ); |
| 160 | + drawInfo.get( 'offset' ).assign( 0 ); |
174 | 161 |
|
175 | | - computeInitDrawBuffer = initDrawBuffer( { |
176 | | - drawBuffer: storage( drawBuffer, drawBufferStruct, drawBuffer.count ), |
177 | | - } ).compute( 1 ); |
| 162 | + } )().compute( 1 ); |
| 163 | + |
178 | 164 |
|
179 | | - const vPosition = varyingProperty( 'vec3', 'vPosition' ); |
180 | | - const vColor = varyingProperty( 'vec4', 'vColor' ); |
| 165 | + const vPosition = varyingProperty( 'vec3', 'vPosition' ); |
| 166 | + const vColor = varyingProperty( 'vec4', 'vColor' ); |
181 | 167 |
|
182 | | - const positionShaderParams = { |
| 168 | + const positionShaderParams = { |
183 | 169 | position: attribute( 'position' ), |
184 | 170 | offset: attribute( 'offset' ), |
185 | 171 | color: attribute( 'color' ), |
186 | 172 | orientationStart: attribute( 'orientationStart' ), |
187 | 173 | orientationEnd: attribute( 'orientationEnd' ), |
188 | 174 | time: time |
189 | 175 | }; |
| 176 | + |
| 177 | + |
| 178 | + const positionFn = Fn( () => { |
190 | 179 |
|
191 | | - const positionShader = wgslFn( ` |
192 | | - fn main_vertex( |
193 | | - position: vec3<f32>, |
194 | | - offset: vec3<f32>, |
195 | | - color: vec4<f32>, |
196 | | - orientationStart: vec4<f32>, |
197 | | - orientationEnd: vec4<f32>, |
198 | | - time: f32 |
199 | | - ) -> vec4<f32> { |
| 180 | + const { position, offset, color, orientationStart, orientationEnd } = positionShaderParams; |
200 | 181 |
|
201 | | - var vPosition = offset * max( abs( sin( time * 0.5 ) * 2.0 + 1.0 ), 0.5 ) + position; |
202 | | - var orientation = normalize( mix( orientationStart, orientationEnd, sin( time * 0.5 ) ) ); |
203 | | - var vcV = cross( orientation.xyz, vPosition ); |
204 | | - vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition ); |
| 182 | + const halfTime = sin( time.mul( 0.5 ) ); |
205 | 183 |
|
206 | | - var vColor = color; |
| 184 | + // Convert slowed sign range of (-1 to 1) to range of (1 -> 0 / 0.5 -> 3) |
| 185 | + const oscilationRange = max( abs( halfTime.mul( 2.0 ).add( 1.0 ) ), 0.5 ); |
207 | 186 |
|
208 | | - var outPosition = vec4f(vPosition, 1); |
| 187 | + const sphereOscilation = offset.mul( oscilationRange ).add( position ).toVar(); |
209 | 188 |
|
210 | | - varyings.vPosition = vPosition; |
211 | | - varyings.vColor = vColor; |
| 189 | + const orientation = normalize( mix( orientationStart, orientationEnd, halfTime ) ); |
| 190 | + const vcV = cross( orientation.xyz, sphereOscilation ); |
| 191 | + const crossvcV = cross( orientation.xyz, vcV ); |
212 | 192 |
|
213 | | - return outPosition; |
214 | | - } |
215 | | - `, [ vPosition, vColor ] ); |
| 193 | + vPosition.assign( vcV.mul( orientation.w.mul( 2.0 ) ).add( crossvcV.mul( 2.0 ).add( sphereOscilation ) ) ); |
| 194 | + vColor.assign( color ); |
216 | 195 |
|
217 | | - const fragmentShaderParams = { |
218 | | - time: time, |
219 | | - vPosition: vPosition, |
220 | | - vColor: vColor |
221 | | - }; |
| 196 | + return vPosition; |
| 197 | + |
| 198 | + } )(); |
| 199 | + |
| 200 | + const fragmentFn = Fn( () => { |
| 201 | + |
| 202 | + const color = vec4( vColor ).toVar(); |
222 | 203 |
|
223 | | - const fragmentShader = wgslFn( ` |
224 | | - fn main_fragment( |
225 | | - time: f32, |
226 | | - vPosition: vec3<f32>, |
227 | | - vColor: vec4<f32> |
228 | | - ) -> vec4<f32> { |
| 204 | + color.r.addAssign( sin( vPosition.x.mul( 10.0 ).add( time ) ).mul( 0.5 ) ); |
229 | 205 |
|
230 | | - var color = vec4f( vColor ); |
231 | | - color.r += sin( vPosition.x * 10.0 + time ) * 0.5; |
| 206 | + return color; |
232 | 207 |
|
233 | | - return color; |
234 | | - } |
235 | | - ` ); |
| 208 | + } )(); |
236 | 209 |
|
237 | 210 | const material = new THREE.MeshBasicNodeMaterial( { |
238 | 211 | side: THREE.DoubleSide, |
239 | 212 | forceSinglePass: true, |
240 | 213 | transparent: true |
241 | 214 | } ); |
242 | 215 |
|
243 | | - material.positionNode = positionShader( positionShaderParams ); |
244 | | - material.fragmentNode = fragmentShader( fragmentShaderParams ); |
| 216 | + material.positionNode = positionFn; |
| 217 | + material.fragmentNode = fragmentFn; |
245 | 218 |
|
246 | 219 | const mesh = new THREE.Mesh( geometry, material ); |
247 | 220 | scene.add( mesh ); |
|
0 commit comments