|
|
@@ -34,7 +34,7 @@
|
|
|
<script type="module">
|
|
|
|
|
|
import * as THREE from 'three/webgpu';
|
|
|
- import { struct, storage, wgslFn, instanceIndex, time, varyingProperty, attribute } from 'three/tsl';
|
|
|
+ import { struct, storage, sin, cross, normalize, abs, mix, Fn, vec4, max, pow, time, varyingProperty, attribute, uint, atomicStore } from 'three/tsl';
|
|
|
|
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
|
|
|
@@ -138,48 +138,34 @@
|
|
|
offset: 'uint'
|
|
|
}, 'DrawBuffer' );
|
|
|
|
|
|
- const writeDrawBuffer = wgslFn( `
|
|
|
- fn compute(
|
|
|
- index: u32,
|
|
|
- drawBuffer: ptr<storage, DrawBuffer, read_write>,
|
|
|
- instances: f32,
|
|
|
- time: f32,
|
|
|
- ) -> void {
|
|
|
+ const drawStorage = storage( drawBuffer, drawBufferStruct, drawBuffer.count );
|
|
|
|
|
|
- let instanceCount = max( instances * pow( sin( time * 0.5 ) + 1, 4.0 ), 100 );
|
|
|
+ computeDrawBuffer = Fn( () => {
|
|
|
|
|
|
- atomicStore( &drawBuffer.instanceCount, u32( instanceCount ) );
|
|
|
- }
|
|
|
- ` );
|
|
|
+ const halfTime = sin( time.mul( 0.5 ) );
|
|
|
|
|
|
- computeDrawBuffer = writeDrawBuffer( {
|
|
|
- drawBuffer: storage( drawBuffer, drawBufferStruct, drawBuffer.count ),
|
|
|
- instances: instances,
|
|
|
- index: instanceIndex,
|
|
|
- time: time
|
|
|
- } ).compute( instances ); // not necessary in this case but normally one wants to run through all instances
|
|
|
+ const instanceCount = max( ( pow( halfTime.add( 1 ), 4.0 ) ).mul( instances ), 100 ).toVar( 'instanceCount' );
|
|
|
+ atomicStore( drawStorage.get( 'instanceCount' ), instanceCount );
|
|
|
+
|
|
|
+ } )().compute( instances );
|
|
|
|
|
|
- const initDrawBuffer = wgslFn( `
|
|
|
- fn compute(
|
|
|
- drawBuffer: ptr< storage, DrawBuffer, read_write >,
|
|
|
- ) -> void {
|
|
|
+ computeInitDrawBuffer = Fn( () => {
|
|
|
|
|
|
- drawBuffer.vertexCount = 3u;
|
|
|
- atomicStore(&drawBuffer.instanceCount, 0u);
|
|
|
- drawBuffer.firstVertex = 0u;
|
|
|
- drawBuffer.firstInstance = 0u;
|
|
|
- drawBuffer.offset = 0u;
|
|
|
- }
|
|
|
- ` );
|
|
|
+ const drawInfo = drawStorage;
|
|
|
+
|
|
|
+ drawInfo.get( 'vertexCount' ).assign( 3 );
|
|
|
+ atomicStore( drawInfo.get( 'instanceCount' ), uint( 0 ) );
|
|
|
+ drawInfo.get( 'firstVertex' ).assign( 0 );
|
|
|
+ drawInfo.get( 'firstInstance' ).assign( 0 );
|
|
|
+ drawInfo.get( 'offset' ).assign( 0 );
|
|
|
|
|
|
- computeInitDrawBuffer = initDrawBuffer( {
|
|
|
- drawBuffer: storage( drawBuffer, drawBufferStruct, drawBuffer.count ),
|
|
|
- } ).compute( 1 );
|
|
|
+ } )().compute( 1 );
|
|
|
+
|
|
|
|
|
|
- const vPosition = varyingProperty( 'vec3', 'vPosition' );
|
|
|
- const vColor = varyingProperty( 'vec4', 'vColor' );
|
|
|
+ const vPosition = varyingProperty( 'vec3', 'vPosition' );
|
|
|
+ const vColor = varyingProperty( 'vec4', 'vColor' );
|
|
|
|
|
|
- const positionShaderParams = {
|
|
|
+ const positionShaderParams = {
|
|
|
position: attribute( 'position' ),
|
|
|
offset: attribute( 'offset' ),
|
|
|
color: attribute( 'color' ),
|
|
|
@@ -187,52 +173,39 @@
|
|
|
orientationEnd: attribute( 'orientationEnd' ),
|
|
|
time: time
|
|
|
};
|
|
|
+
|
|
|
+
|
|
|
+ const positionFn = Fn( () => {
|
|
|
|
|
|
- const positionShader = wgslFn( `
|
|
|
- fn main_vertex(
|
|
|
- position: vec3<f32>,
|
|
|
- offset: vec3<f32>,
|
|
|
- color: vec4<f32>,
|
|
|
- orientationStart: vec4<f32>,
|
|
|
- orientationEnd: vec4<f32>,
|
|
|
- time: f32
|
|
|
- ) -> vec4<f32> {
|
|
|
+ const { position, offset, color, orientationStart, orientationEnd } = positionShaderParams;
|
|
|
|
|
|
- var vPosition = offset * max( abs( sin( time * 0.5 ) * 2.0 + 1.0 ), 0.5 ) + position;
|
|
|
- var orientation = normalize( mix( orientationStart, orientationEnd, sin( time * 0.5 ) ) );
|
|
|
- var vcV = cross( orientation.xyz, vPosition );
|
|
|
- vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
|
|
|
+ const halfTime = sin( time.mul( 0.5 ) );
|
|
|
|
|
|
- var vColor = color;
|
|
|
+ // Convert slowed sign range of (-1 to 1) to range of (1 -> 0 / 0.5 -> 3)
|
|
|
+ const oscilationRange = max( abs( halfTime.mul( 2.0 ).add( 1.0 ) ), 0.5 );
|
|
|
|
|
|
- var outPosition = vec4f(vPosition, 1);
|
|
|
+ const sphereOscilation = offset.mul( oscilationRange ).add( position ).toVar();
|
|
|
|
|
|
- varyings.vPosition = vPosition;
|
|
|
- varyings.vColor = vColor;
|
|
|
+ const orientation = normalize( mix( orientationStart, orientationEnd, halfTime ) );
|
|
|
+ const vcV = cross( orientation.xyz, sphereOscilation );
|
|
|
+ const crossvcV = cross( orientation.xyz, vcV );
|
|
|
|
|
|
- return outPosition;
|
|
|
- }
|
|
|
- `, [ vPosition, vColor ] );
|
|
|
+ vPosition.assign( vcV.mul( orientation.w.mul( 2.0 ) ).add( crossvcV.mul( 2.0 ).add( sphereOscilation ) ) );
|
|
|
+ vColor.assign( color );
|
|
|
|
|
|
- const fragmentShaderParams = {
|
|
|
- time: time,
|
|
|
- vPosition: vPosition,
|
|
|
- vColor: vColor
|
|
|
- };
|
|
|
+ return vPosition;
|
|
|
+
|
|
|
+ } )();
|
|
|
+
|
|
|
+ const fragmentFn = Fn( () => {
|
|
|
+
|
|
|
+ const color = vec4( vColor ).toVar();
|
|
|
|
|
|
- const fragmentShader = wgslFn( `
|
|
|
- fn main_fragment(
|
|
|
- time: f32,
|
|
|
- vPosition: vec3<f32>,
|
|
|
- vColor: vec4<f32>
|
|
|
- ) -> vec4<f32> {
|
|
|
+ color.r.addAssign( sin( vPosition.x.mul( 10.0 ).add( time ) ).mul( 0.5 ) );
|
|
|
|
|
|
- var color = vec4f( vColor );
|
|
|
- color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
|
|
|
+ return color;
|
|
|
|
|
|
- return color;
|
|
|
- }
|
|
|
- ` );
|
|
|
+ } )();
|
|
|
|
|
|
const material = new THREE.MeshBasicNodeMaterial( {
|
|
|
side: THREE.DoubleSide,
|
|
|
@@ -240,8 +213,8 @@
|
|
|
transparent: true
|
|
|
} );
|
|
|
|
|
|
- material.positionNode = positionShader( positionShaderParams );
|
|
|
- material.fragmentNode = fragmentShader( fragmentShaderParams );
|
|
|
+ material.positionNode = positionFn;
|
|
|
+ material.fragmentNode = fragmentFn;
|
|
|
|
|
|
const mesh = new THREE.Mesh( geometry, material );
|
|
|
scene.add( mesh );
|