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