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