1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+ < head >
4+ < title > three.js webgpu - struct drawIndirect</ title >
5+ < meta charset ="utf-8 ">
6+ < meta name ="viewport " content ="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0 ">
7+ < link type ="text/css " rel ="stylesheet " href ="main.css ">
8+ </ head >
9+ < body >
10+
11+ < div id ="info ">
12+ < a href ="https://threejs.org " target ="_blank " rel ="noopener "> three.js</ a > webgpu - struct drawIndirect< br />
13+ </ div >
14+
15+ < script type ="importmap ">
16+ {
17+ "imports" : {
18+ "three" : "../src/three.webgpu.js" ,
19+ "three/webgpu" : "../src/three.webgpu.js" ,
20+ "three/tsl" : "../src/three.tsl.js" ,
21+ "three/addons/" : "./jsm/"
22+ }
23+ }
24+ </ script >
25+
26+ < script type ="module ">
27+
28+ import * as THREE from 'three' ;
29+ import { struct , storage , wgslFn , instanceIndex , time , varyingProperty , attribute } from 'three/tsl' ;
30+
31+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js' ;
32+
33+ const renderer = new THREE . WebGPURenderer ( { antialias : true } ) ;
34+ renderer . outputColorSpace = THREE . SRGBColorSpace ;
35+ renderer . setPixelRatio ( window . devicePixelRatio ) ;
36+ renderer . setSize ( window . innerWidth , window . innerHeight ) ;
37+ renderer . setClearColor ( 0x000000 ) ;
38+ renderer . setClearAlpha ( 0 ) ;
39+ document . body . appendChild ( renderer . domElement ) ;
40+
41+ const aspect = window . innerWidth / window . innerHeight ;
42+
43+ const camera = new THREE . PerspectiveCamera ( 50.0 , aspect , 0.1 , 10000 ) ;
44+ const scene = new THREE . Scene ( ) ;
45+
46+ scene . background = new THREE . Color ( 0x00001f ) ;
47+ camera . position . set ( 1 , 1 , 1 ) ;
48+ const controls = new OrbitControls ( camera , renderer . domElement ) ;
49+
50+ let computeDrawBuffer , computeInitDrawBuffer ;
51+
52+ init ( ) ;
53+
54+ async function init ( ) {
55+
56+ await renderer . init ( ) ;
57+
58+ // geometry
59+
60+ const vector = new THREE . Vector4 ( ) ;
61+
62+ const instances = 100000 ;
63+
64+ const positions = [ ] ;
65+ const offsets = [ ] ;
66+ const colors = [ ] ;
67+ const orientationsStart = [ ] ;
68+ const orientationsEnd = [ ] ;
69+
70+ positions . push ( 0.025 , - 0.025 , 0 ) ;
71+ positions . push ( - 0.025 , 0.025 , 0 ) ;
72+ positions . push ( 0 , 0 , 0.025 ) ;
73+
74+ // instanced attributes
75+
76+ for ( let i = 0 ; i < instances ; i ++ ) {
77+
78+ // offsets
79+
80+ offsets . push ( Math . random ( ) - 0.5 , Math . random ( ) - 0.5 , Math . random ( ) - 0.5 ) ;
81+
82+ // colors
83+
84+ colors . push ( Math . random ( ) , Math . random ( ) , Math . random ( ) , Math . random ( ) ) ;
85+
86+ // orientation start
87+
88+ vector . set ( Math . random ( ) * 2 - 1 , Math . random ( ) * 2 - 1 , Math . random ( ) * 2 - 1 , Math . random ( ) * 2 - 1 ) ;
89+ vector . normalize ( ) ;
90+
91+ orientationsStart . push ( vector . x , vector . y , vector . z , vector . w ) ;
92+
93+ // orientation end
94+
95+ vector . set ( Math . random ( ) * 2 - 1 , Math . random ( ) * 2 - 1 , Math . random ( ) * 2 - 1 , Math . random ( ) * 2 - 1 ) ;
96+ vector . normalize ( ) ;
97+
98+ orientationsEnd . push ( vector . x , vector . y , vector . z , vector . w ) ;
99+
100+ }
101+
102+ const geometry = new THREE . InstancedBufferGeometry ( ) ;
103+ geometry . instanceCount = instances ;
104+
105+ geometry . setAttribute ( 'position' , new THREE . Float32BufferAttribute ( positions , 3 ) ) ;
106+ geometry . setAttribute ( 'offset' , new THREE . InstancedBufferAttribute ( new Float32Array ( offsets ) , 3 ) ) ;
107+ geometry . setAttribute ( 'color' , new THREE . InstancedBufferAttribute ( new Float32Array ( colors ) , 4 ) ) ;
108+ geometry . setAttribute ( 'orientationStart' , new THREE . InstancedBufferAttribute ( new Float32Array ( orientationsStart ) , 4 ) ) ;
109+ geometry . setAttribute ( 'orientationEnd' , new THREE . InstancedBufferAttribute ( new Float32Array ( orientationsEnd ) , 4 ) ) ;
110+
111+ const drawBuffer = new THREE . IndirectStorageBufferAttribute ( new Uint32Array ( 5 ) , 5 ) ;
112+ geometry . setIndirect ( drawBuffer ) ;
113+
114+ const drawBufferStruct = struct ( {
115+ vertexCount : 'uint' ,
116+ instanceCount : { type : 'uint' , atomic : true } ,
117+ firstVertex : 'uint' ,
118+ firstInstance : 'uint' ,
119+ offset : 'uint'
120+ } , 'DrawBuffer' ) ;
121+
122+ const writeDrawBuffer = wgslFn ( `
123+ fn compute(
124+ index: u32,
125+ drawBuffer: ptr<storage, DrawBuffer, read_write>,
126+ instances: f32,
127+ time: f32,
128+ ) -> void {
129+
130+ let instanceCount = max( instances * pow( sin( time * 0.5 ) + 1, 4.0 ), 100 );
131+
132+ atomicStore( &drawBuffer.instanceCount, u32( instanceCount ) );
133+ }
134+ ` ) ;
135+
136+ computeDrawBuffer = writeDrawBuffer ( {
137+ drawBuffer : storage ( drawBuffer , drawBufferStruct , drawBuffer . count ) ,
138+ instances : instances ,
139+ index : instanceIndex ,
140+ time : time
141+ } ) . compute ( instances ) ; // not neccessary in this case but normally one wants to run through all instances
142+
143+ const initDrawBuffer = wgslFn ( `
144+ fn compute(
145+ drawBuffer: ptr< storage, DrawBuffer, read_write >,
146+ ) -> void {
147+
148+ drawBuffer.vertexCount = 3u;
149+ atomicStore(&drawBuffer.instanceCount, 0u);
150+ drawBuffer.firstVertex = 0u;
151+ drawBuffer.firstInstance = 0u;
152+ drawBuffer.offset = 0u;
153+ }
154+ ` ) ;
155+
156+ computeInitDrawBuffer = initDrawBuffer ( {
157+ drawBuffer : storage ( drawBuffer , drawBufferStruct , drawBuffer . count ) ,
158+ } ) . compute ( 1 ) ;
159+
160+ const vPosition = varyingProperty ( 'vec3' , 'vPosition' ) ;
161+ const vColor = varyingProperty ( 'vec4' , 'vColor' ) ;
162+
163+ const positionShaderParams = {
164+ position : attribute ( 'position' ) ,
165+ offset : attribute ( 'offset' ) ,
166+ color : attribute ( 'color' ) ,
167+ orientationStart : attribute ( 'orientationStart' ) ,
168+ orientationEnd : attribute ( 'orientationEnd' ) ,
169+ time : time
170+ } ;
171+
172+ const positionShader = wgslFn ( `
173+ fn main_vertex(
174+ position: vec3<f32>,
175+ offset: vec3<f32>,
176+ color: vec4<f32>,
177+ orientationStart: vec4<f32>,
178+ orientationEnd: vec4<f32>,
179+ time: f32
180+ ) -> vec4<f32> {
181+
182+ var vPosition = offset * max( abs( sin( time * 0.5 ) * 2.0 + 1.0 ), 0.5 ) + position;
183+ var orientation = normalize( mix( orientationStart, orientationEnd, sin( time * 0.5 ) ) );
184+ var vcV = cross( orientation.xyz, vPosition );
185+ vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
186+
187+ var vColor = color;
188+
189+ var outPosition = vec4f(vPosition, 1);
190+
191+ varyings.vPosition = vPosition;
192+ varyings.vColor = vColor;
193+
194+ return outPosition;
195+ }
196+ ` , [ vPosition , vColor ] ) ;
197+
198+ const fragmentShaderParams = {
199+ time : time ,
200+ vPosition : vPosition ,
201+ vColor : vColor
202+ } ;
203+
204+ const fragmentShader = wgslFn ( `
205+ fn main_fragment(
206+ time: f32,
207+ vPosition: vec3<f32>,
208+ vColor: vec4<f32>
209+ ) -> vec4<f32> {
210+
211+ var color = vec4f( vColor );
212+ color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
213+
214+ return color;
215+ }
216+ ` ) ;
217+
218+ const material = new THREE . MeshBasicNodeMaterial ( {
219+ side : THREE . DoubleSide ,
220+ forceSinglePass : true ,
221+ transparent : true
222+ } ) ;
223+
224+ material . positionNode = positionShader ( positionShaderParams ) ;
225+ material . fragmentNode = fragmentShader ( fragmentShaderParams ) ;
226+
227+ const mesh = new THREE . Mesh ( geometry , material ) ;
228+ scene . add ( mesh ) ;
229+
230+ renderer . setAnimationLoop ( render ) ;
231+
232+ window . addEventListener ( 'resize' , onWindowResize , false ) ;
233+
234+ }
235+
236+ function render ( ) {
237+
238+ controls . update ( ) ;
239+
240+ renderer . render ( scene , camera ) ;
241+
242+ renderer . compute ( computeInitDrawBuffer ) ;
243+ renderer . compute ( computeDrawBuffer ) ;
244+
245+ }
246+
247+ function onWindowResize ( ) {
248+
249+ camera . aspect = window . innerWidth / window . innerHeight ;
250+ camera . updateProjectionMatrix ( ) ;
251+ renderer . setSize ( window . innerWidth , window . innerHeight ) ;
252+
253+ }
254+
255+ </ script >
256+
257+ </ body >
258+ </ html >
0 commit comments