1import GLWrap from './GLWrap';
2
3// WebGL 2.0 sample - http://webglsamples.org/WebGL2Samples/#transform_feedback_separated_2
4export default GLWrap('WebGL2 - Transform feedback', async (gl) => {
5  const POSITION_LOCATION = 0;
6  const VELOCITY_LOCATION = 1;
7  const SPAWNTIME_LOCATION = 2;
8  const LIFETIME_LOCATION = 3;
9  const ID_LOCATION = 4;
10  const NUM_LOCATIONS = 5;
11  const NUM_PARTICLES = 1000;
12  const ACCELERATION = -1.0;
13
14  const vertexSource = `#version 300 es
15          #define POSITION_LOCATION ${POSITION_LOCATION}
16          #define VELOCITY_LOCATION ${VELOCITY_LOCATION}
17          #define SPAWNTIME_LOCATION ${SPAWNTIME_LOCATION}
18          #define LIFETIME_LOCATION ${LIFETIME_LOCATION}
19          #define ID_LOCATION ${ID_LOCATION}
20
21          precision highp float;
22          precision highp int;
23          precision highp sampler3D;
24
25          uniform float u_time;
26          uniform vec2 u_acceleration;
27
28          layout(location = POSITION_LOCATION) in vec2 a_position;
29          layout(location = VELOCITY_LOCATION) in vec2 a_velocity;
30          layout(location = SPAWNTIME_LOCATION) in float a_spawntime;
31          layout(location = LIFETIME_LOCATION) in float a_lifetime;
32          layout(location = ID_LOCATION) in float a_ID;
33
34          out vec2 v_position;
35          out vec2 v_velocity;
36          out float v_spawntime;
37          out float v_lifetime;
38
39          float rand(vec2 co){
40            return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
41          }
42
43          void main() {
44            if (a_spawntime == 0.0 || (u_time - a_spawntime > a_lifetime) || a_position.y < -0.5) {
45              // Generate a new particle
46              v_position = vec2(0.0, 0.0);
47              v_velocity = vec2(rand(vec2(a_ID, 0.0)) - 0.5, rand(vec2(a_ID, a_ID)));
48              v_spawntime = u_time;
49              v_lifetime = 5000.0;
50            } else {
51              v_velocity = a_velocity + 0.01 * u_acceleration;
52              v_position = a_position + 0.01 * v_velocity;
53              v_spawntime = a_spawntime;
54              v_lifetime = a_lifetime;
55            }
56            gl_Position = vec4(v_position, 0.0, 1.0);
57            gl_PointSize = 2.0;
58          }
59        `;
60
61  const fragmentSource = `#version 300 es
62          precision highp float;
63          precision highp int;
64
65          uniform vec4 u_color;
66
67          out vec4 color;
68
69          void main() {
70            color = u_color;
71          }
72        `;
73
74  const vert = gl.createShader(gl.VERTEX_SHADER)!;
75  gl.shaderSource(vert, vertexSource);
76  gl.compileShader(vert);
77
78  const frag = gl.createShader(gl.FRAGMENT_SHADER)!;
79  gl.shaderSource(frag, fragmentSource);
80  gl.compileShader(frag);
81
82  const program = gl.createProgram()!;
83  gl.attachShader(program, vert);
84  gl.attachShader(program, frag);
85
86  const appStartTime = Date.now();
87  let currentSourceIdx = 0;
88
89  // Get varyings and link program
90  const varyings = ['v_position', 'v_velocity', 'v_spawntime', 'v_lifetime'];
91  gl.transformFeedbackVaryings(program, varyings, gl.SEPARATE_ATTRIBS);
92  gl.linkProgram(program);
93
94  // Get uniform locations for the draw program
95  const drawTimeLocation = gl.getUniformLocation(program, 'u_time');
96  const drawAccelerationLocation = gl.getUniformLocation(program, 'u_acceleration');
97  const drawColorLocation = gl.getUniformLocation(program, 'u_color');
98
99  // Initialize particle data
100  const particlePositions = new Float32Array(NUM_PARTICLES * 2);
101  const particleVelocities = new Float32Array(NUM_PARTICLES * 2);
102  const particleSpawntime = new Float32Array(NUM_PARTICLES);
103  const particleLifetime = new Float32Array(NUM_PARTICLES);
104  const particleIDs = new Float32Array(NUM_PARTICLES);
105
106  for (let p = 0; p < NUM_PARTICLES; ++p) {
107    particlePositions[p * 2] = 0.0;
108    particlePositions[p * 2 + 1] = 0.0;
109    particleVelocities[p * 2] = 0.0;
110    particleVelocities[p * 2 + 1] = 0.0;
111    particleSpawntime[p] = 0.0;
112    particleLifetime[p] = 0.0;
113    particleIDs[p] = p;
114  }
115
116  // Init Vertex Arrays and Buffers
117  const particleVAOs = [gl.createVertexArray(), gl.createVertexArray()];
118
119  // Transform feedback objects track output buffer state
120  const particleTransformFeedbacks = [gl.createTransformFeedback(), gl.createTransformFeedback()];
121
122  const particleVBOs = new Array(particleVAOs.length);
123
124  for (let i = 0; i < particleVAOs.length; ++i) {
125    particleVBOs[i] = new Array(NUM_LOCATIONS);
126    // Set up input
127    gl.bindVertexArray(particleVAOs[i]!);
128
129    particleVBOs[i][POSITION_LOCATION] = gl.createBuffer();
130    gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][POSITION_LOCATION]);
131    gl.bufferData(gl.ARRAY_BUFFER, particlePositions, gl.STREAM_COPY);
132    gl.vertexAttribPointer(POSITION_LOCATION, 2, gl.FLOAT, false, 0, 0);
133    gl.enableVertexAttribArray(POSITION_LOCATION);
134
135    particleVBOs[i][VELOCITY_LOCATION] = gl.createBuffer();
136    gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][VELOCITY_LOCATION]);
137    gl.bufferData(gl.ARRAY_BUFFER, particleVelocities, gl.STREAM_COPY);
138    gl.vertexAttribPointer(VELOCITY_LOCATION, 2, gl.FLOAT, false, 0, 0);
139    gl.enableVertexAttribArray(VELOCITY_LOCATION);
140
141    particleVBOs[i][SPAWNTIME_LOCATION] = gl.createBuffer();
142    gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][SPAWNTIME_LOCATION]);
143    gl.bufferData(gl.ARRAY_BUFFER, particleSpawntime, gl.STREAM_COPY);
144    gl.vertexAttribPointer(SPAWNTIME_LOCATION, 1, gl.FLOAT, false, 0, 0);
145    gl.enableVertexAttribArray(SPAWNTIME_LOCATION);
146
147    particleVBOs[i][LIFETIME_LOCATION] = gl.createBuffer();
148    gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][LIFETIME_LOCATION]);
149    gl.bufferData(gl.ARRAY_BUFFER, particleLifetime, gl.STREAM_COPY);
150    gl.vertexAttribPointer(LIFETIME_LOCATION, 1, gl.FLOAT, false, 0, 0);
151    gl.enableVertexAttribArray(LIFETIME_LOCATION);
152
153    particleVBOs[i][ID_LOCATION] = gl.createBuffer();
154    gl.bindBuffer(gl.ARRAY_BUFFER, particleVBOs[i][ID_LOCATION]);
155    gl.bufferData(gl.ARRAY_BUFFER, particleIDs, gl.STATIC_READ);
156    gl.vertexAttribPointer(ID_LOCATION, 1, gl.FLOAT, false, 0, 0);
157    gl.enableVertexAttribArray(ID_LOCATION);
158
159    gl.bindBuffer(gl.ARRAY_BUFFER, null);
160
161    // Set up output
162    gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, particleTransformFeedbacks[i]);
163    gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, particleVBOs[i][POSITION_LOCATION]);
164    gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, particleVBOs[i][VELOCITY_LOCATION]);
165    gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, particleVBOs[i][SPAWNTIME_LOCATION]);
166    gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 3, particleVBOs[i][LIFETIME_LOCATION]);
167  }
168
169  gl.useProgram(program);
170  gl.uniform4f(drawColorLocation, 0.0, 1.0, 1.0, 1.0);
171  gl.uniform2f(drawAccelerationLocation, 0.0, ACCELERATION);
172
173  gl.enable(gl.BLEND);
174  gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
175
176  return {
177    onTick() {
178      const time = Date.now() - appStartTime;
179      const destinationIdx = (currentSourceIdx + 1) % 2;
180
181      // Clear color buffer
182      gl.clearColor(0.0, 0.0, 0.0, 1.0);
183      gl.clear(gl.COLOR_BUFFER_BIT);
184
185      // Toggle source and destination VBO
186      const sourceVAO = particleVAOs[currentSourceIdx];
187      const destinationTransformFeedback = particleTransformFeedbacks[destinationIdx];
188      gl.bindVertexArray(sourceVAO);
189      gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, destinationTransformFeedback);
190
191      // Set uniforms
192      gl.uniform1f(drawTimeLocation, time);
193
194      // Draw particles using transform feedback
195      gl.beginTransformFeedback(gl.POINTS);
196      gl.drawArrays(gl.POINTS, 0, NUM_PARTICLES);
197      gl.endTransformFeedback();
198      gl.endFrameEXP();
199
200      // Ping pong the buffers
201      currentSourceIdx = (currentSourceIdx + 1) % 2;
202    },
203  };
204});
205