/* Constants */
let updates_per_frame = 20;
let dt = .005;
let spring_rad = .05;
let spring_const = 30;
let damping = .001;
let w = h = 30;
let gl = canvas.getContext('webgl2');
gl.getExtension("EXT_color_buffer_float");
/* data */
let posvel = new Float32Array(4*w*h);
for (let i = 0; i < w*h; i++) {
posvel[4*i ] = 1 - 2*Math.random(); // x
posvel[4*i+1] = 1 - 2*Math.random(); // y
posvel[4*i+2] = 0; // vx
posvel[4*i+3] = 0; // vy
}
let posvel_tex = [
Texture(0, w,h, gl.RGBA32F, gl.RGBA, gl.FLOAT, posvel),
Texture(1, w,h, gl.RGBA32F, gl.RGBA, gl.FLOAT, posvel)];
let posvel_fbo = [
FrameBuffer(posvel_tex[0]),
FrameBuffer(posvel_tex[1])];
/* Draw Program */
let draw = ProgramBundle(
`in ivec2 pixel;
uniform sampler2D posvel;
void main() {
vec2 pos = texelFetch( posvel, pixel, 0).xy;
gl_PointSize = 7.0;
gl_Position = vec4(pos, 0., 1.);
}`,
`out vec4 color;
void main() {
color = vec4(0.,0.,1.,1.);
}`);
draw.pixel_buf = Buffer(Pixels(w,h));
draw.run = (unit) => {
gl.useProgram(draw.program);
gl.uniform1i( draw.posvel, unit);
gl.bindBuffer(gl.ARRAY_BUFFER, draw.pixel_buf);
gl.vertexAttribIPointer(draw.pixel, 2, gl.INT, false, 0,0);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0,0,canvas.width, canvas.height);
gl.drawArrays(gl.POINTS, 0, w*h);
}
/* update program */
let update = ProgramBundle(
`in vec2 pos;
void main() {
gl_Position = vec4(pos, 0.,1.);}`,
`out vec4 posvel_new;
uniform sampler2D posvel;
void main()
{
float k = ${(spring_const).toFixed(1)};
float R = ${spring_rad};
ivec2 id = ivec2(gl_FragCoord.xy);
vec2 pos = texelFetch(posvel, id, 0).xy;
vec2 vel = texelFetch(posvel, id, 0).zw;
for (int y = 0; y < ${h}; y++) {
for (int x = 0; x < ${w}; x++) {
vec2 posB = texelFetch( posvel, ivec2(x,y), 0 ).xy;
float L = length(pos - posB);
if (L < R) {
vel += k*(posB - pos)*(L - R);
vel -= ${damping}*vel;
}
}
}
vel.y -= .1*${dt};
pos += vel*${dt};
if (pos.y < -1.0) { pos.y = -1.0; vel.y = abs(vel.y); }
if (pos.y > 1.0) { pos.y = 1.0; vel.y =-abs(vel.y); }
if (pos.x < -1.0) { pos.x = -1.0; vel.x = abs(vel.x); }
if (pos.x > 1.0) { pos.x = 1.0; vel.x =-abs(vel.x); }
posvel_new = vec4(pos,vel);
}`);
update.pos_buf = Buffer(new Float32Array([-1,-1, 1,-1, -1,1, 1,1]));
update.run = (in_unit, out_unit) => {
gl.useProgram(update.program);
gl.uniform1i( update.posvel, in_unit);
gl.bindBuffer(gl.ARRAY_BUFFER, update.pos_buf);
gl.vertexAttribPointer(update.pos, 2, gl.FLOAT, false,0, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, posvel_fbo[out_unit]);
gl.viewport(0,0,w,h);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
/* main loop */
let IO = 0;
let count = 0;
(function loop() {
for (let i = 0; i < updates_per_frame; i++) {
update.run(1-IO, IO);
IO = 1 - IO;
}
draw.run(IO);
if (count++ < 500) requestAnimationFrame(loop);
})();
/*--------------------------------------------------------------------------------*/
function Buffer(data) {
let buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
return buf;
}
function ProgramBundle(vertex_code, fragment_code) {
/* Return program and attrib/uniform locations
let pb = ProgramBundle(v_code, f_code)
pb.program == program
pb.(name) == location
pb.(name) == location
...
*/
// Build Shader Program
let pb = { program: gl.createProgram() };
let vertex_shader = gl.createShader(gl.VERTEX_SHADER);
let fragment_shader = gl.createShader(gl.FRAGMENT_SHADER);
vertex_code = "#version 300 es\n precision highp float;\n" + vertex_code;
fragment_code = "#version 300 es\n precision highp float;\n" + fragment_code;
gl.shaderSource( fragment_shader, fragment_code);
gl.shaderSource( vertex_shader, vertex_code);
gl.compileShader(vertex_shader);
gl.compileShader(fragment_shader);
gl.attachShader( pb.program, vertex_shader);
gl.attachShader( pb.program, fragment_shader);
gl.linkProgram( pb.program);
gl.useProgram( pb.program);
// Store Attribute Locations
let attrib_count = gl.getProgramParameter(pb.program, gl.ACTIVE_ATTRIBUTES);
for (let i= 0; i < attrib_count; i++) {
let attrib_name = gl.getActiveAttrib( pb.program, i).name;
let attrib_location = gl.getAttribLocation(pb.program, attrib_name);
gl.enableVertexAttribArray(attrib_location);
pb[attrib_name] = attrib_location;
}
// Store Uniform Locations
let uniform_count = gl.getProgramParameter( pb.program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < uniform_count; i++) {
let uniform_name = gl.getActiveUniform( pb.program, i).name;
let uniform_location = gl.getUniformLocation(pb.program, uniform_name);
pb[uniform_name] = uniform_location;
}
return pb;
}
function Texture(unit, w, h, internal_format, format, type, data) {
let tex = gl.createTexture();
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D,0, internal_format, w,h, 0, format, type, data);
return tex;
}
function FrameBuffer(target_tex) {
let fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target_tex, 0);
return fbo;
}
function Pixels(width, height) {
let UV = [];
for (let x=0; x<width; x++) {
for (let y=0; y<height; y++) {
UV.push(x,y);
}
}
return new Int32Array(UV);
}
Commenti