|
18 | 18 | <p id="description">Using transform feedback in a simple particle system</p> |
19 | 19 |
|
20 | 20 | <!-- WebGL 2 shaders --> |
21 | | - <script id="vs-emit" type="x-shader/x-vertex"> |
| 21 | + <script id="vs-draw" type="x-shader/x-vertex"> |
22 | 22 | #version 300 es |
23 | 23 | #define POSITION_LOCATION 0 |
24 | 24 | #define VELOCITY_LOCATION 1 |
|
62 | 62 | v_spawntime = a_spawntime; |
63 | 63 | v_lifetime = a_lifetime; |
64 | 64 | } |
65 | | - } |
66 | | - </script> |
67 | | - |
68 | | - <!-- Unsused --> |
69 | | - <script id="fs-emit" type="x-shader/x-fragment"> |
70 | | - #version 300 es |
71 | | - precision highp float; |
72 | | - precision highp int; |
73 | | - |
74 | | - out vec4 color; |
75 | | - |
76 | | - void main() |
77 | | - { |
78 | | - color = vec4(1.0); |
79 | | - } |
80 | | - </script> |
81 | | - |
82 | | - <script id="vs-draw" type="x-shader/x-vertex"> |
83 | | - #version 300 es |
84 | | - #define POSITION_LOCATION 0 |
85 | | - #define VELOCITY_LOCATION 1 |
86 | | - #define SPAWNTIME_LOCATION 2 |
87 | | - #define LIFETIME_LOCATION 3 |
88 | | - |
89 | | - precision highp float; |
90 | | - precision highp int; |
91 | | - |
92 | | - uniform float u_time; |
93 | | - uniform vec2 u_acceleration; |
94 | | - |
95 | | - layout(location = POSITION_LOCATION) in vec2 a_position; |
96 | | - layout(location = VELOCITY_LOCATION) in vec2 a_velocity; |
97 | | - layout(location = SPAWNTIME_LOCATION) in float a_spawntime; |
98 | | - layout(location = LIFETIME_LOCATION) in float a_lifetime; |
99 | 65 |
|
100 | | - void main() |
101 | | - { |
102 | | - float deltaTime = u_time - a_spawntime; |
103 | | - if (deltaTime < a_lifetime) { |
104 | | - gl_Position = vec4(a_position, 0.0, 1.0); |
105 | | - } else { |
106 | | - gl_Position = vec4(-100.0, -100.0, 0.0, 1.0); |
107 | | - } |
| 66 | + gl_Position = vec4(v_position, 0.0, 1.0); |
108 | 67 | gl_PointSize = 2.0; |
109 | 68 | } |
110 | 69 | </script> |
|
155 | 114 | var appStartTime = Date.now(); |
156 | 115 | var currentSourceIdx = 0; |
157 | 116 |
|
158 | | - // -- Init Program |
159 | | - |
160 | | - var PROGRAM_EMIT = 0; |
161 | | - var PROGRAM_DRAW = 1; |
162 | | - |
163 | | - var programs = initPrograms(); |
164 | | - |
165 | | - // Get uniform locations for the transform feedback program |
166 | | - var emitTimeLocation = gl.getUniformLocation(programs[PROGRAM_EMIT], 'u_time'); |
167 | | - var emitAccelerationLocation = gl.getUniformLocation(programs[PROGRAM_EMIT], 'u_acceleration'); |
| 117 | + var program = initProgram(); |
168 | 118 |
|
169 | 119 | // Get uniform locations for the draw program |
170 | | - var drawTimeLocation = gl.getUniformLocation(programs[PROGRAM_DRAW], 'u_time'); |
171 | | - var drawAccelerationLocation = gl.getUniformLocation(programs[PROGRAM_DRAW], 'u_acceleration'); |
172 | | - var drawColorLocation = gl.getUniformLocation(programs[PROGRAM_DRAW], 'u_color'); |
| 120 | + var drawTimeLocation = gl.getUniformLocation(program, 'u_time'); |
| 121 | + var drawAccelerationLocation = gl.getUniformLocation(program, 'u_acceleration'); |
| 122 | + var drawColorLocation = gl.getUniformLocation(program, 'u_color'); |
173 | 123 |
|
174 | 124 | // -- Initialize particle data |
175 | 125 |
|
|
179 | 129 | var particleLifetime = new Float32Array(NUM_PARTICLES); |
180 | 130 | var particleIDs = new Float32Array(NUM_PARTICLES); |
181 | 131 |
|
182 | | - var Particle = { |
183 | | - POSITION: 0, |
184 | | - VELOCITY: 1, |
185 | | - SPAWN_TIME: 2, |
186 | | - LIFE_TIME: 3, |
187 | | - ID: 4, |
188 | | - MAX: 5 |
189 | | - }; |
| 132 | + var POSITION_LOCATION = 0; |
| 133 | + var VELOCITY_LOCATION = 1; |
| 134 | + var SPAWNTIME_LOCATION = 2; |
| 135 | + var LIFETIME_LOCATION = 3; |
| 136 | + var ID_LOCATION = 4; |
| 137 | + var NUM_LOCATIONS = 5; |
190 | 138 |
|
191 | 139 | for (var p = 0; p < NUM_PARTICLES; ++p) { |
192 | 140 | particlePositions[p * 2] = 0.0; |
|
198 | 146 | particleIDs[p] = p; |
199 | 147 | } |
200 | 148 |
|
201 | | - // -- Init Buffer |
| 149 | + // -- Init Vertex Arrays and Buffers |
| 150 | + var particleVAOs = [gl.createVertexArray(), gl.createVertexArray()]; |
| 151 | + |
| 152 | + var particleVBOs = new Array(particleVAOs.length); |
202 | 153 |
|
203 | | - var particleVBOs = new Array(2); |
| 154 | + for (var i = 0; i < particleVAOs.length; ++i) { |
| 155 | + particleVBOs[i] = new Array(NUM_LOCATIONS); |
204 | 156 |
|
205 | | - for (var i = 0; i < 2; ++i) { |
206 | | - particleVBOs[i] = new Array(Particle.MAX); |
207 | | - for (var j = 0; j < Particle.MAX; ++j) { |
208 | | - particleVBOs[i][j] = gl.createBuffer(); |
209 | | - } |
| 157 | + gl.bindVertexArray(particleVAOs[i]); |
210 | 158 |
|
211 | | - gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][Particle.POSITION]); |
| 159 | + particleVBOs[i][POSITION_LOCATION] = gl.createBuffer(); |
| 160 | + gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][POSITION_LOCATION]); |
212 | 161 | gl.bufferData(gl.ARRAY_BUFFER, particlePositions, gl.STREAM_COPY); |
| 162 | + gl.vertexAttribPointer(POSITION_LOCATION, 2, gl.FLOAT, false, 0, 0); |
| 163 | + gl.enableVertexAttribArray(POSITION_LOCATION); |
213 | 164 |
|
214 | | - gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][Particle.VELOCITY]); |
| 165 | + particleVBOs[i][VELOCITY_LOCATION] = gl.createBuffer(); |
| 166 | + gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][VELOCITY_LOCATION]); |
215 | 167 | gl.bufferData(gl.ARRAY_BUFFER, particleVelocities, gl.STREAM_COPY); |
| 168 | + gl.vertexAttribPointer(VELOCITY_LOCATION, 2, gl.FLOAT, false, 0, 0); |
| 169 | + gl.enableVertexAttribArray(VELOCITY_LOCATION); |
216 | 170 |
|
217 | | - gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][Particle.SPAWN_TIME]); |
| 171 | + particleVBOs[i][SPAWNTIME_LOCATION] = gl.createBuffer(); |
| 172 | + gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][SPAWNTIME_LOCATION]); |
218 | 173 | gl.bufferData(gl.ARRAY_BUFFER, particleSpawntime, gl.STREAM_COPY); |
| 174 | + gl.vertexAttribPointer(SPAWNTIME_LOCATION, 1, gl.FLOAT, false, 0, 0); |
| 175 | + gl.enableVertexAttribArray(SPAWNTIME_LOCATION); |
219 | 176 |
|
220 | | - gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][Particle.LIFE_TIME]); |
| 177 | + particleVBOs[i][LIFETIME_LOCATION] = gl.createBuffer(); |
| 178 | + gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][LIFETIME_LOCATION]); |
221 | 179 | gl.bufferData(gl.ARRAY_BUFFER, particleLifetime, gl.STREAM_COPY); |
| 180 | + gl.vertexAttribPointer(LIFETIME_LOCATION, 1, gl.FLOAT, false, 0, 0); |
| 181 | + gl.enableVertexAttribArray(LIFETIME_LOCATION); |
222 | 182 |
|
223 | | - gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][Particle.ID]); |
| 183 | + particleVBOs[i][ID_LOCATION] = gl.createBuffer(); |
| 184 | + gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][ID_LOCATION]); |
224 | 185 | gl.bufferData(gl.ARRAY_BUFFER, particleIDs, gl.STATIC_READ); |
| 186 | + gl.vertexAttribPointer(ID_LOCATION, 1, gl.FLOAT, false, 0, 0); |
| 187 | + gl.enableVertexAttribArray(ID_LOCATION); |
225 | 188 | } |
226 | 189 |
|
227 | | - // -- Init Vertex Array |
228 | | - var particleVAOs = [gl.createVertexArray(), gl.createVertexArray()]; |
229 | | - |
230 | | - var POSITION_LOCATION = 0; |
231 | | - var VELOCITY_LOCATION = 1; |
232 | | - var SPAWNTIME_LOCATION = 2; |
233 | | - var LIFETIME_LOCATION = 3; |
234 | | - var ID_LOCATION = 4; |
235 | | - |
236 | | - // -- Init TransformFeedback |
237 | | - var transformFeedback = gl.createTransformFeedback(); |
238 | | - |
239 | | - render(); |
| 190 | + function initProgram() { |
240 | 191 |
|
241 | | - function initPrograms() { |
242 | | - |
243 | | - // Setup program for transform feedback shaders |
| 192 | + // Setup program for transform feedback |
244 | 193 | function createShader(gl, source, type) { |
245 | 194 | var shader = gl.createShader(type); |
246 | 195 | gl.shaderSource(shader, source); |
247 | 196 | gl.compileShader(shader); |
248 | 197 | return shader; |
249 | 198 | } |
250 | 199 |
|
251 | | - var vshaderTransform = createShader(gl, getShaderSource('vs-emit'), gl.VERTEX_SHADER); |
252 | | - var fshaderTransform = createShader(gl, getShaderSource('fs-emit'), gl.FRAGMENT_SHADER); |
| 200 | + var vshader = createShader(gl, getShaderSource('vs-draw'), gl.VERTEX_SHADER); |
| 201 | + var fshader = createShader(gl, getShaderSource('fs-draw'), gl.FRAGMENT_SHADER); |
253 | 202 |
|
254 | | - var programEmit = gl.createProgram(); |
255 | | - gl.attachShader(programEmit, vshaderTransform); |
256 | | - gl.deleteShader(vshaderTransform); |
257 | | - gl.attachShader(programEmit, fshaderTransform); |
258 | | - gl.deleteShader(fshaderTransform); |
| 203 | + var program = gl.createProgram(); |
| 204 | + gl.attachShader(program, vshader); |
| 205 | + gl.attachShader(program, fshader); |
259 | 206 |
|
260 | 207 | var varyings = ['v_position', 'v_velocity', 'v_spawntime', 'v_lifetime']; |
261 | | - gl.transformFeedbackVaryings(programEmit, varyings, gl.SEPARATE_ATTRIBS); |
262 | | - gl.linkProgram(programEmit); |
| 208 | + gl.transformFeedbackVaryings(program, varyings, gl.SEPARATE_ATTRIBS); |
| 209 | + gl.linkProgram(program); |
263 | 210 |
|
264 | 211 | // check |
265 | | - var log = gl.getProgramInfoLog(programEmit); |
| 212 | + var log = gl.getProgramInfoLog(program); |
266 | 213 | if (log) { |
267 | 214 | console.log(log); |
268 | 215 | } |
269 | 216 |
|
270 | | - log = gl.getShaderInfoLog(vshaderTransform); |
| 217 | + log = gl.getShaderInfoLog(vshader); |
271 | 218 | if (log) { |
272 | 219 | console.log(log); |
273 | 220 | } |
274 | 221 |
|
275 | | - // Setup program for draw shader |
276 | | - var programDraw = createProgram(gl, getShaderSource('vs-draw'), getShaderSource('fs-draw')); |
277 | | - |
278 | | - var programs = [programEmit, programDraw]; |
279 | | - return programs; |
280 | | - } |
281 | | - |
282 | | - function setupVertexAttributes(vaoId, vboArray) { |
283 | | - gl.bindVertexArray(vaoId); |
284 | | - |
285 | | - gl.bindBuffer(gl.ARRAY_BUFFER, vboArray[Particle.POSITION]); |
286 | | - gl.vertexAttribPointer(POSITION_LOCATION, 2, gl.FLOAT, false, 0, 0); |
| 222 | + log = gl.getShaderInfoLog(fshader); |
| 223 | + if (log) { |
| 224 | + console.log(log); |
| 225 | + } |
287 | 226 |
|
288 | | - gl.bindBuffer(gl.ARRAY_BUFFER, vboArray[Particle.VELOCITY]); |
289 | | - gl.vertexAttribPointer(VELOCITY_LOCATION, 2, gl.FLOAT, false, 0, 0); |
| 227 | + gl.deleteShader(vshader); |
| 228 | + gl.deleteShader(fshader); |
290 | 229 |
|
291 | | - gl.bindBuffer(gl.ARRAY_BUFFER, vboArray[Particle.SPAWN_TIME]); |
292 | | - gl.vertexAttribPointer(SPAWNTIME_LOCATION, 1, gl.FLOAT, false, 0, 0); |
| 230 | + return program; |
| 231 | + } |
293 | 232 |
|
294 | | - gl.bindBuffer(gl.ARRAY_BUFFER, vboArray[Particle.LIFE_TIME]); |
295 | | - gl.vertexAttribPointer(LIFETIME_LOCATION, 1, gl.FLOAT, false, 0, 0); |
| 233 | + gl.useProgram(program); |
| 234 | + gl.uniform4f(drawColorLocation, 0.0, 1.0, 1.0, 1.0); |
| 235 | + gl.uniform2f(drawAccelerationLocation, 0.0, ACCELERATION); |
| 236 | + |
| 237 | + gl.enable(gl.BLEND); |
| 238 | + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); |
296 | 239 |
|
297 | | - gl.bindBuffer(gl.ARRAY_BUFFER, vboArray[Particle.ID]); |
298 | | - gl.vertexAttribPointer(ID_LOCATION, 1, gl.FLOAT, false, 0, 0); |
| 240 | + // -- Init TransformFeedback |
| 241 | + var transformFeedback = gl.createTransformFeedback(); |
| 242 | + gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); |
299 | 243 |
|
300 | | - gl.enableVertexAttribArray(POSITION_LOCATION); |
301 | | - gl.enableVertexAttribArray(VELOCITY_LOCATION); |
302 | | - gl.enableVertexAttribArray(SPAWNTIME_LOCATION); |
303 | | - gl.enableVertexAttribArray(LIFETIME_LOCATION); |
304 | | - gl.enableVertexAttribArray(ID_LOCATION); |
305 | | - } |
| 244 | + function render() { |
306 | 245 |
|
307 | | - function emitParticles() { |
308 | 246 | var time = Date.now() - appStartTime; |
309 | 247 |
|
310 | | - var programEmit = programs[PROGRAM_EMIT]; |
| 248 | + // Set the viewport |
| 249 | + gl.viewport(0, 0, canvas.width, canvas.height - 10); |
| 250 | + |
| 251 | + // Clear color buffer |
| 252 | + gl.clearColor(0.0, 0.0, 0.0, 1.0); |
| 253 | + gl.clear(gl.COLOR_BUFFER_BIT); |
311 | 254 |
|
312 | 255 | // Toggle source and destination VBO |
313 | | - var sourceVBO = particleVBOs[currentSourceIdx]; |
314 | 256 | var sourceVAO = particleVAOs[currentSourceIdx]; |
315 | | - |
316 | 257 | var destinationVBO = particleVBOs[(currentSourceIdx + 1) % 2]; |
317 | 258 |
|
318 | | - gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); |
319 | | - |
320 | | - gl.useProgram(programEmit); |
321 | | - |
322 | | - setupVertexAttributes(sourceVAO, sourceVBO); |
| 259 | + gl.bindVertexArray(sourceVAO); |
323 | 260 |
|
324 | 261 | // Set transform feedback buffer |
325 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, destinationVBO[Particle.POSITION]); |
326 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, destinationVBO[Particle.VELOCITY]); |
327 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, destinationVBO[Particle.SPAWN_TIME]); |
328 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 3, destinationVBO[Particle.LIFE_TIME]); |
329 | | - |
330 | | - // Turn off rasterization - we are not drawing |
331 | | - gl.enable(gl.RASTERIZER_DISCARD); |
| 262 | + gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, destinationVBO[POSITION_LOCATION]); |
| 263 | + gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, destinationVBO[VELOCITY_LOCATION]); |
| 264 | + gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, destinationVBO[SPAWNTIME_LOCATION]); |
| 265 | + gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 3, destinationVBO[LIFETIME_LOCATION]); |
332 | 266 |
|
333 | 267 | // Set uniforms |
334 | | - gl.uniform1f(emitTimeLocation, time); |
335 | | - gl.uniform2f(emitAccelerationLocation, 0.0, ACCELERATION); |
| 268 | + gl.uniform1f(drawTimeLocation, time); |
336 | 269 |
|
337 | | - // Emit particles using transform feedback |
| 270 | + // Draw particles using transform feedback |
338 | 271 | gl.beginTransformFeedback(gl.POINTS); |
339 | 272 | gl.drawArrays(gl.POINTS, 0, NUM_PARTICLES); |
340 | 273 | gl.endTransformFeedback(); |
341 | 274 |
|
342 | | - // Restore state |
343 | | - gl.disable(gl.RASTERIZER_DISCARD); |
344 | | - gl.useProgram(null); |
345 | | - gl.bindBuffer(gl.ARRAY_BUFFER, null); |
346 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null); |
347 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null); |
348 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, null); |
349 | | - gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 3, null); |
350 | | - gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); |
351 | | - |
352 | 275 | // Ping pong the buffers |
353 | 276 | currentSourceIdx = (currentSourceIdx + 1) % 2; |
354 | | - } |
355 | | - |
356 | | - function render() { |
357 | | - // Spawn particles |
358 | | - emitParticles(); |
359 | | - |
360 | | - var time = Date.now() - appStartTime; |
361 | | - |
362 | | - // Set the viewport |
363 | | - gl.viewport(0, 0, canvas.width, canvas.height - 10); |
364 | | - |
365 | | - // Clear color buffer |
366 | | - gl.clearColor(0.0, 0.0, 0.0, 1.0); |
367 | | - gl.clear(gl.COLOR_BUFFER_BIT); |
368 | | - |
369 | | - setupVertexAttributes(particleVAOs[currentSourceIdx], particleVBOs[currentSourceIdx]); |
370 | | - |
371 | | - gl.useProgram(programs[PROGRAM_DRAW]); |
372 | | - |
373 | | - // Set uniforms |
374 | | - gl.uniform1f(drawTimeLocation, time); |
375 | | - gl.uniform4f(drawColorLocation, 0.0, 1.0, 1.0, 1.0); |
376 | | - gl.uniform2f(drawAccelerationLocation, 0.0, ACCELERATION); |
377 | | - |
378 | | - // Enable blending |
379 | | - gl.enable(gl.BLEND); |
380 | | - gl.blendFunc(gl.SRC_ALPHA, gl.ONE); |
381 | | - |
382 | | - gl.drawArrays(gl.POINTS, 0, NUM_PARTICLES); |
383 | 277 |
|
384 | 278 | requestAnimationFrame(render); |
385 | 279 | } |
386 | 280 |
|
| 281 | + requestAnimationFrame(render); |
| 282 | + |
387 | 283 | // If you have a long-running page, and need to delete WebGL resources, use: |
388 | 284 | // |
389 | 285 | // gl.deleteTransformFeedback(transformFeedback); |
390 | | - // gl.deleteProgram(programs[PROGRAM_EMIT]); |
391 | | - // gl.deleteProgram(programs[PROGRAM_DRAW]); |
392 | | - // for (var i = 0; i < 2; ++i) { |
393 | | - // for (var j = 0; j < Particle.MAX; ++j) { |
| 286 | + // gl.deleteProgram(program); |
| 287 | + // for (var i = 0; i < particleVAOs.length; ++i) { |
| 288 | + // gl.deleteVertexArray(i); |
| 289 | + // for (var j = 0; j < NUM_LOCATIONS; ++j) { |
394 | 290 | // gl.deleteBuffer(particleVBOs[i][j]); |
395 | 291 | // } |
396 | 292 | // } |
397 | | - // gl.deleteVertexArray(particleVAOs[PROGRAM_EMIT]); |
398 | | - // gl.deleteVertexArray(particleVAOs[PROGRAM_DRAW]); |
399 | 293 | })(); |
400 | 294 | </script> |
401 | | - <div id="highlightedLines" style="display: none">#L318-L350</div> |
| 295 | + <div id="highlightedLines" style="display: none">#L246-L278</div> |
402 | 296 |
|
403 | 297 | </body> |
404 | 298 |
|
|
0 commit comments