virtual-webgl/example/example2.html
Gregg Tavares 3a287fb80c Try to emulate WebGL1 on WebGL2
Add some testing so we can see it works.
2022-03-29 23:43:54 -07:00

1463 lines
44 KiB
HTML

<html lang="en">
<head>
<title>Look Ma, Just one WebGL2 Context</title>
<style>
canvas {
border: 1px solid red;
background-color: #999;
background-image:
linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
#map {
width: 600px;
height: 500px;
}
.samples>div {
border: 1px solid black;
padding: 5px;
display: inline-block;
}
</style>
</head>
<body>
<h1>Look Ma, Just one WebGL2 Context</h1>
<div class="samples">
<div><canvas id="c21" width="100" height="100"></canvas><div>generic webgl</div></div>
<div><canvas id="c22"></canvas><div>generic webgl no size</div></div>
<div><canvas id="c23" width="64" height="64"></canvas><div>red</div></div>
<div><canvas id="c24" width="95" height="64"></canvas><div>blue</div></div>
<div><canvas id="c25" width="64" height="64"></canvas><div>purple with alpha .5 but alpha:false</div></div>
<div><canvas id="c26" width="64" height="64"></canvas><div>green .5 with alpha .5 alpha true (default)</div></div>
<div><canvas id="c27" width="64" height="64"></canvas><div>green 1 with alpha .5 premultipliedAlpha: false</div></div>
<div><canvas id="c28" width="400" height="300"></canvas><div>preserveDrawingBuffer: true</div></div>
<div><canvas id="c29" width="400" height="300"></canvas><div>three.js</div></div>
<div><canvas id="c210" width="128" height="128"></canvas><div>multiple render targets</div></div>
<div><canvas id="c211" width="150" height="150"></canvas><div>Instanced Drawing &amp; VertexArray</div></div>
<div><canvas id="c212" width="400" height="300"></canvas><div>GPGPU</div></div>
</div>
<div id="map"></div>
</body>
<script src="../src/virtual-webgl2.js"></script>
<script src="js/twgl-full.min.js"></script>
<script src="js/chroma.min.js"></script>
<!--
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.45.0/mapbox-gl.css' rel='stylesheet' />
-->
</head>
<body>
<!-- demo 1 -->
<script>
function main1() {
"use strict";
const vs = `
uniform mat4 u_matrix;
uniform vec4 u_offsets;
uniform vec4 u_centers;
uniform vec4 u_mult;
attribute vec2 a_position;
attribute vec4 a_color;
varying vec4 v_color;
#define PI 3.14159
void main() {
vec2 offset = mix(u_offsets.xz, u_offsets.yw, a_position.y);
float a = u_mult.x * a_position.x * PI * 2.0 + offset.x;//mix(u_offsets.x, u_offsets.y, a_position.y);
float c = cos(a * u_mult.y);
vec2 xy = vec2(
cos(a),
sin(a)) * c * offset.y +
mix(u_centers.xy, u_centers.zw, a_position.y);
gl_Position = u_matrix * vec4(xy, 0, 1);
v_color = a_color;
}
`;
const fs = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
const m4 = twgl.m4;
twgl.setDefaults({attribPrefix: "a_"});
const gl = document.querySelector("#c21").getContext("webgl2");
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const numLines = 100;
const arrays = {
position: twgl.primitives.createAugmentedTypedArray(2, numLines * 2),
color: twgl.primitives.createAugmentedTypedArray(3, numLines * 2, Uint8Array),
};
function rand(min, max) {
return min + Math.random() * (max - min);
}
const hue = rand(0, 360);
for (let ii = 0; ii < numLines; ++ii) {
const u = ii / numLines;
const h = (360 + hue + (Math.abs(u - 0.5) * 100)) % 360;
const s = Math.sin(u * Math.PI * 2) * 0.25 + 0.75;
const v = 1;
const color = chroma.hsv(h, s, v);
arrays.position.push(u, 1);
arrays.color.push(color.rgb());
arrays.position.push(u, 0);
arrays.color.push(color.brighten().desaturate().rgb());
}
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
const offsets = [0, 0, 0, 1];
const centers = [0, 0, 0, 0];
const mult = [1, 2, 0, 0];
const uniforms = {
u_matrix: m4.identity(),
u_offsets: offsets,
u_centers: centers,
u_mult: mult,
};
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
offsets[0] = Math.sin(time);
offsets[1] = Math.sin(time * 0.13) * Math.PI * 2;
offsets[2] = Math.sin(time * 0.43) * 0.5 + 1.0;
offsets[3] = Math.cos(time * 0.17) * 0.5 + 0.5;
centers[0] = Math.sin(time * 0.163) * 0.5;
centers[1] = Math.cos(time * 0.267) * 0.5;
centers[2] = Math.sin(time * 0.367) * 0.5;
centers[3] = Math.cos(time * 0.497) * 0.5;
mult[1] = (Math.sin(time * 0.1) * 0.5 + 0.5) * 3;
gl.lineWidth(25);
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
m4.ortho(-aspect, aspect, 1, -1, -1, 1, uniforms.u_matrix);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo, gl.LINES);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main1();
</script>
<!-- demo 2 -->
<script>
function main2() {
"use strict";
const vs = `
uniform mat4 u_worldViewProjection;
uniform vec3 u_lightWorldPos;
uniform mat4 u_world;
uniform mat4 u_viewInverse;
uniform mat4 u_worldInverseTranspose;
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texcoord;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
void main() {
v_texCoord = texcoord;
v_position = u_worldViewProjection * position;
v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
v_surfaceToLight = u_lightWorldPos - (u_world * position).xyz;
v_surfaceToView = (u_viewInverse[3] - (u_world * position)).xyz;
gl_Position = v_position;
}
`;
const fs = `
precision mediump float;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
uniform vec4 u_lightColor;
uniform vec4 u_ambient;
uniform sampler2D u_diffuse;
uniform vec4 u_specular;
uniform float u_shininess;
uniform float u_specularFactor;
vec4 lit(float l ,float h, float m) {
return vec4(1.0,
max(l, 0.0),
(l > 0.0) ? pow(max(0.0, h), m) : 0.0,
1.0);
}
void main() {
vec4 diffuseColor = texture2D(u_diffuse, v_texCoord);
vec3 a_normal = normalize(v_normal);
vec3 surfaceToLight = normalize(v_surfaceToLight);
vec3 surfaceToView = normalize(v_surfaceToView);
vec3 halfVector = normalize(surfaceToLight + surfaceToView);
vec4 litR = lit(dot(a_normal, surfaceToLight),
dot(a_normal, halfVector), u_shininess);
vec4 outColor = vec4((
u_lightColor * (diffuseColor * litR.y + diffuseColor * u_ambient +
u_specular * litR.z * u_specularFactor)).rgb,
diffuseColor.a);
gl_FragColor = outColor;
}
`;
const m4 = twgl.m4;
const gl = document.querySelector("#c22").getContext("webgl2");
twgl.setDefaults({attribPrefix: ""});
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
const tex = twgl.createTexture(gl, {
min: gl.NEAREST,
mag: gl.NEAREST,
src: [
255, 255, 255, 255,
192, 192, 192, 255,
192, 192, 192, 255,
255, 255, 255, 255,
],
});
const uniforms = {
u_lightWorldPos: [1, 8, -10],
u_lightColor: [1, 0.8, 0.8, 1],
u_ambient: [0, 0, 0, 1],
u_specular: [1, 1, 1, 1],
u_shininess: 50,
u_specularFactor: 1,
u_diffuse: tex,
};
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 10;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [1, 1, -3];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
const world = m4.rotationY(time);
uniforms.u_viewInverse = camera;
uniforms.u_world = world;
uniforms.u_worldInverseTranspose = m4.transpose(m4.inverse(world));
uniforms.u_worldViewProjection = m4.multiply(viewProjection, world);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main2();
</script>
<script>
test('#c23', [1, 0, 0, 1]);
test('#c24', [0, 0, 1, 1]);
test('#c25', [1, 0, 1, .5], {alpha: false});
test('#c26', [0, .5, 0,.5]);
test('#c27', [0, 1, 0,.5], {premultipliedAlpha: false});
function test(selector, color, options) {
const gl = document.querySelector(selector).getContext("webgl2", options);
gl.clearColor(...color);
gl.clear(gl.COLOR_BUFFER_BIT);
}
{
const gl = document.querySelector('#c28').getContext('webgl2', {preserveDrawingBuffer: true});
gl.enable(gl.SCISSOR_TEST);
setInterval(() => {
gl.scissor(Math.random() * (gl.canvas.width - 10), Math.random() * (gl.canvas.height - 10), 10, 10);
gl.clearColor(Math.random(), Math.random(), Math.random(), 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}, 500);
}
</script>
<script type="ignore">
mapboxgl.accessToken = 'undefined';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v9', // stylesheet location
center: [-74.50, 40], // starting position [lng, lat]
zoom: 9 // starting zoom
});
</script>
<script id="fragment_shader4" type="x-shader/x-fragment">
uniform float time;
varying vec2 vUv;
void main( void ) {
vec2 position = - 1.0 + 2.0 * vUv;
float red = abs( sin( position.x * position.y + time / 5.0 ) );
float green = abs( sin( position.x * position.y + time / 4.0 ) );
float blue = abs( sin( position.x * position.y + time / 3.0 ) );
gl_FragColor = vec4( red, green, blue, 1.0 );
}
</script>
<script id="fragment_shader3" type="x-shader/x-fragment">
uniform float time;
varying vec2 vUv;
void main( void ) {
vec2 position = vUv;
float color = 0.0;
color += sin( position.x * cos( time / 15.0 ) * 80.0 ) + cos( position.y * cos( time / 15.0 ) * 10.0 );
color += sin( position.y * sin( time / 10.0 ) * 40.0 ) + cos( position.x * sin( time / 25.0 ) * 40.0 );
color += sin( position.x * sin( time / 5.0 ) * 10.0 ) + sin( position.y * sin( time / 35.0 ) * 80.0 );
color *= sin( time / 10.0 ) * 0.5;
gl_FragColor = vec4( vec3( color, color * 0.5, sin( color + time / 3.0 ) * 0.75 ), 1.0 );
}
</script>
<script id="fragment_shader2" type="x-shader/x-fragment">
uniform float time;
uniform sampler2D texture2;
varying vec2 vUv;
void main( void ) {
vec2 position = - 1.0 + 2.0 * vUv;
float a = atan( position.y, position.x );
float r = sqrt( dot( position, position ) );
vec2 uv;
uv.x = cos( a ) / r;
uv.y = sin( a ) / r;
uv /= 10.0;
uv += time * 0.05;
vec3 color = texture2D( texture2, uv ).rgb;
gl_FragColor = vec4( color * r * 1.5, 1.0 );
}
</script>
<script id="fragment_shader1" type="x-shader/x-fragment">
uniform float time;
varying vec2 vUv;
void main(void) {
vec2 p = - 1.0 + 2.0 * vUv;
float a = time * 40.0;
float d, e, f, g = 1.0 / 40.0 ,h ,i ,r ,q;
e = 400.0 * ( p.x * 0.5 + 0.5 );
f = 400.0 * ( p.y * 0.5 + 0.5 );
i = 200.0 + sin( e * g + a / 150.0 ) * 20.0;
d = 200.0 + cos( f * g / 2.0 ) * 18.0 + cos( e * g ) * 7.0;
r = sqrt( pow( abs( i - e ), 2.0 ) + pow( abs( d - f ), 2.0 ) );
q = f / r;
e = ( r * cos( q ) ) - a / 2.0;
f = ( r * sin( q ) ) - a / 2.0;
d = sin( e * g ) * 176.0 + sin( e * g ) * 164.0 + r;
h = ( ( f + d ) + a / 2.0 ) * g;
i = cos( h + r * p.x / 1.3 ) * ( e + e + a ) + cos( q * g * 6.0 ) * ( r + h / 3.0 );
h = sin( f * g ) * 144.0 - sin( e * g ) * 212.0 * p.x;
h = ( h + ( f - e ) * q + sin( r - ( a + h ) / 7.0 ) * 10.0 + i / 4.0 ) * g;
i += cos( h * 2.3 * sin( a / 350.0 - q ) ) * 184.0 * sin( q - ( r * 4.3 + a / 12.0 ) * g ) + tan( r * g + h ) * 184.0 * cos( r * g + h );
i = mod( i / 5.6, 256.0 ) / 64.0;
if ( i < 0.0 ) i += 4.0;
if ( i >= 2.0 ) i = 4.0 - i;
d = r / 350.0;
d += sin( d * d * 8.0 ) * 0.52;
f = ( sin( a * g ) + 1.0 ) / 2.0;
gl_FragColor = vec4( vec3( f * i / 1.6, i / 2.0 + d / 13.0, i ) * d * p.x + vec3( i / 1.3 + d / 8.0, i / 2.0 + d / 18.0, i ) * d * ( 1.0 - p.x ), 1.0 );
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="module">
import * as THREE from './js/three.module.js';
// copied from https://threejs.org/examples/webgl_shader2.html
function main9() {
const canvas = document.querySelector('#c29');
const camera = new THREE.PerspectiveCamera(40, 1, 1, 3000);
camera.position.z = 4;
const scene = new THREE.Scene();
const geometry = new THREE.BoxBufferGeometry(0.75, 0.75, 0.75);
const uniforms1 = {
time: { value: 1.0 },
};
const uniforms2 = {
time: { value: 1.0 },
texture2: { value: new THREE.TextureLoader().load('textures/disturb.jpg') },
};
uniforms2.texture2.value.wrapS = uniforms2.texture2.value.wrapT = THREE.RepeatWrapping;
const params = [
['fragment_shader1', uniforms1],
['fragment_shader2', uniforms2],
['fragment_shader3', uniforms1],
['fragment_shader4', uniforms1],
];
for(let i = 0; i < params.length; ++i) {
const material = new THREE.ShaderMaterial({
uniforms: params[i][1],
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById(params[i][0]).textContent,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = i - (params.length - 1) / 2;
mesh.position.y = i % 2 - 0.5;
scene.add(mesh);
}
const renderer = new THREE.WebGLRenderer({canvas: canvas});
function resize() {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function render(time) {
time *= 0.001;
resize();
uniforms1.time.value = time * 5;
uniforms2.time.value = time;
for (let i = 0; i < scene.children.length; ++i) {
const object = scene.children[i];
object.rotation.y = time * 0.5 * (i % 2 ? 1 : -1);
object.rotation.x = time * 0.5 * (i % 2 ? -1 : 1);
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main9();
</script>
<script>
function main10() {
const gl = document.querySelector("#c210").getContext("webgl2");
const vs = `#version 300 es
uniform float u_pointSize;
void main() {
gl_Position = vec4(0, 0, 0, 1);
gl_PointSize = u_pointSize;
}
`;
const colorFS = `#version 300 es
precision mediump float;
uniform vec4 u_colors[4];
out vec4 fragData[4];
void main() {
fragData[0] = u_colors[0];
fragData[1] = u_colors[1];
fragData[2] = u_colors[2];
fragData[3] = u_colors[3];
}
`;
const arrayFS = `#version 300 es
precision mediump float;
uniform sampler2D u_texture[4];
out vec4 fragColor;
void main() {
vec4 color = vec4(0);
float s = floor(mod(gl_FragCoord.x / 16., 4.));
vec4 tt[4];
tt[0] = texture(u_texture[0], vec2(0));
tt[1] = texture(u_texture[1], vec2(0));
tt[2] = texture(u_texture[2], vec2(0));
tt[3] = texture(u_texture[3], vec2(0));
for(int i = 0; i < 4; ++i) {
float t = float(i);
vec4 c = tt[i];
color = mix(color, c, step(t - .5, s) * step(s, t + .5));
}
fragColor = color;
}
`;
const colorProgramInfo = twgl.createProgramInfo(gl, [vs, colorFS]);
const arrayProgramInfo = twgl.createProgramInfo(gl, [vs, arrayFS]);
const fb = gl.createFramebuffer()
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
const textures = [];
for (let i = 0; i < 4; ++i) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, tex, 0);
textures.push(tex);
}
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status !== gl.FRAMEBUFFER_COMPLETE) {
console.error("frame buffer not complete");
}
function renderPart1(time) {
time *= 0.001;
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.drawBuffers([
gl.COLOR_ATTACHMENT0 + 0,
gl.COLOR_ATTACHMENT0 + 1,
gl.COLOR_ATTACHMENT0 + 2,
gl.COLOR_ATTACHMENT0 + 3,
]);
gl.viewport(0, 0, 1, 1);
gl.useProgram(colorProgramInfo.program);
twgl.setUniforms(colorProgramInfo, {
u_pointSize: 64,
u_colors: [
1, time % 1, 0, 1,
0, 1, time * 1.1 % 1, 1,
time * 1.2 % 1, 0, 1, 1,
1, 1, time * 1.3 % 1, 1,
],
});
gl.drawArrays(gl.POINTS, 0, 1);
// split this to leave a multi-attachment framebuffer
// attached to check that state is saved
requestAnimationFrame(renderPart2);
}
function renderPart2(time) {
time *= 0.001;
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.drawBuffers([
gl.BACK,
]);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(arrayProgramInfo.program);
twgl.setUniforms(arrayProgramInfo, {
u_pointSize: 64 + Math.sin(time) * 64,
u_texture: textures,
});
gl.drawArrays(gl.POINTS, 0, 1);
requestAnimationFrame(renderPart1);
}
requestAnimationFrame(renderPart1);
}
main10();
</script>
<script>
function main11() {
const vs = `
uniform mat4 u_viewProjection;
uniform vec3 u_lightWorldPos;
uniform mat4 u_viewInverse;
attribute vec4 instanceColor;
attribute mat4 instanceWorld;
attribute vec4 position;
attribute vec3 normal;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
varying vec4 v_color;
void main() {
v_color = instanceColor;
vec4 worldPosition = instanceWorld * position;
v_position = u_viewProjection * worldPosition;
v_normal = (instanceWorld * vec4(normal, 0)).xyz;
v_surfaceToLight = u_lightWorldPos - worldPosition.xyz;
v_surfaceToView = u_viewInverse[3].xyz - worldPosition.xyz;
gl_Position = v_position;
}
`;
const fs = `
precision mediump float;
varying vec4 v_position;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
varying vec4 v_color;
uniform vec4 u_lightColor;
uniform vec4 u_ambient;
uniform vec4 u_specular;
uniform float u_shininess;
uniform float u_specularFactor;
vec4 lit(float l ,float h, float m) {
return vec4(1.0,
max(l, 0.0),
(l > 0.0) ? pow(max(0.0, h), m) : 0.0,
1.0);
}
void main() {
vec4 diffuseColor = v_color;
vec3 a_normal = normalize(v_normal);
vec3 surfaceToLight = normalize(v_surfaceToLight);
vec3 surfaceToView = normalize(v_surfaceToView);
vec3 halfVector = normalize(surfaceToLight + surfaceToView);
vec4 litR = lit(dot(a_normal, surfaceToLight),
dot(a_normal, halfVector), u_shininess);
vec4 outColor = vec4((
u_lightColor * (diffuseColor * litR.y + diffuseColor * u_ambient +
u_specular * litR.z * u_specularFactor)).rgb,
diffuseColor.a);
gl_FragColor = outColor;
}
`;
const m4 = twgl.m4;
const gl = document.querySelector("#c211").getContext("webgl2");
twgl.addExtensionsToContext(gl);
if (!gl.drawArraysInstanced || !gl.createVertexArray) {
alert("need drawArraysInstanced and createVertexArray"); // eslint-disable-line
return;
}
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
function rand(min, max) {
if (max === undefined) {
max = min;
min = 0;
}
return min + Math.random() * (max - min);
}
const numInstances = 1000;
const instanceWorlds = new Float32Array(numInstances * 16);
const instanceColors = [];
const r = 20;
for (let i = 0; i < numInstances; ++i) {
const mat = new Float32Array(instanceWorlds.buffer, i * 16 * 4, 16);
m4.translation([rand(-r, r), rand(-r, r), rand(-r, r)], mat);
m4.rotateZ(mat, rand(0, Math.PI * 2), mat);
m4.rotateX(mat, rand(0, Math.PI * 2), mat);
instanceColors.push(rand(1), rand(1), rand(1));
}
const arrays = twgl.primitives.createCubeVertices();
Object.assign(arrays, {
instanceWorld: {
numComponents: 16,
data: instanceWorlds,
divisor: 1,
},
instanceColor: {
numComponents: 3,
data: instanceColors,
divisor: 1,
},
});
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
const vertexArrayInfo = twgl.createVertexArrayInfo(gl, programInfo, bufferInfo);
const uniforms = {
u_lightWorldPos: [1, 8, -30],
u_lightColor: [1, 1, 1, 1],
u_ambient: [0, 0, 0, 1],
u_specular: [1, 1, 1, 1],
u_shininess: 50,
u_specularFactor: 1,
};
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 500;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const radius = 25;
const speed = time * .1;
const eye = [Math.sin(speed) * radius, Math.sin(speed * .7) * 10, Math.cos(speed) * radius];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
uniforms.u_viewProjection = m4.multiply(projection, view);
uniforms.u_viewInverse = camera;
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, vertexArrayInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, vertexArrayInfo, gl.TRIANGLES, vertexArrayInfo.numElements, 0, numInstances);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main11();
</script>
<script>
'use strict';
/* eslint no-alert: 0 */
function main12() {
const m4 = twgl.m4;
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = width !== canvas.width || height !== canvas.height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
const updatePositionVS = `#version 300 es
in vec2 oldPosition;
in vec2 velocity;
uniform float deltaTime;
uniform vec2 canvasDimensions;
out vec2 newPosition;
vec2 euclideanModulo(vec2 n, vec2 m) {
return mod(mod(n, m) + m, m);
}
void main() {
newPosition = euclideanModulo(
oldPosition + velocity * deltaTime,
canvasDimensions);
}
`;
const updatePositionFS = `#version 300 es
precision highp float;
void main() {
}
`;
const updateLinesVS = `#version 300 es
in vec4 position;
void main() {
gl_Position = position;
}
`;
const updateLinesFS = `#version 300 es
precision highp float;
uniform sampler2D linesTex;
uniform sampler2D velocityTex;
uniform vec2 canvasDimensions;
uniform float deltaTime;
out vec4 outColor;
vec2 euclideanModulo(vec2 n, vec2 m) {
return mod(mod(n, m) + m, m);
}
void main() {
// compute texel coord from gl_FragCoord;
ivec2 texelCoord = ivec2(gl_FragCoord.xy);
vec2 position = texelFetch(linesTex, texelCoord, 0).xy;
vec2 velocity = texelFetch(velocityTex, texelCoord, 0).xy;
vec2 newPosition = euclideanModulo(position + velocity * deltaTime, canvasDimensions);
outColor = vec4(newPosition, 0, 1);
}
`;
const closestLineVS = `#version 300 es
in vec3 point;
uniform sampler2D linesTex;
uniform int numLineSegments;
flat out int closestNdx;
vec4 getAs1D(sampler2D tex, ivec2 dimensions, int index) {
int y = index / dimensions.x;
int x = index % dimensions.x;
return texelFetch(tex, ivec2(x, y), 0);
}
// from https://stackoverflow.com/a/6853926/128511
// a is the point, b,c is the line segment
float distanceFromPointToLine(in vec3 a, in vec3 b, in vec3 c) {
vec3 ba = a - b;
vec3 bc = c - b;
float d = dot(ba, bc);
float len = length(bc);
float param = 0.0;
if (len != 0.0) {
param = clamp(d / (len * len), 0.0, 1.0);
}
vec3 r = b + bc * param;
return distance(a, r);
}
void main() {
ivec2 linesTexDimensions = textureSize(linesTex, 0);
// find the closest line segment
float minDist = 10000000.0;
int minIndex = -1;
for (int i = 0; i < numLineSegments; ++i) {
vec3 lineStart = getAs1D(linesTex, linesTexDimensions, i * 2).xyz;
vec3 lineEnd = getAs1D(linesTex, linesTexDimensions, i * 2 + 1).xyz;
float dist = distanceFromPointToLine(point, lineStart, lineEnd);
if (dist < minDist) {
minDist = dist;
minIndex = i;
}
}
closestNdx = minIndex;
}
`;
const closestLineFS = `#version 300 es
precision highp float;
void main() {
}
`;
const drawClosestLinesVS = `#version 300 es
in int closestNdx;
uniform float numPoints;
uniform sampler2D linesTex;
uniform mat4 matrix;
out vec4 v_color;
vec4 getAs1D(sampler2D tex, ivec2 dimensions, int index) {
int y = index / dimensions.x;
int x = index % dimensions.x;
return texelFetch(tex, ivec2(x, y), 0);
}
vec3 hsv2rgb(vec3 c) {
c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
ivec2 linesTexDimensions = textureSize(linesTex, 0);
// pull the position from the texture
int linePointId = closestNdx * 2 + gl_VertexID % 2;
vec4 position = getAs1D(linesTex, linesTexDimensions, linePointId);
// do the common matrix math
gl_Position = matrix * vec4(position.xy, 0, 1);
int pointId = gl_InstanceID;
float hue = float(pointId) / numPoints;
v_color = vec4(hsv2rgb(vec3(hue, 1, 1)), 1);
}
`;
const drawPointsVS = `#version 300 es
in vec4 point;
uniform float numPoints;
uniform mat4 matrix;
out vec4 v_color;
vec3 hsv2rgb(vec3 c) {
c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
gl_Position = matrix * point;
gl_PointSize = 10.0;
float hue = float(gl_VertexID) / numPoints;
v_color = vec4(hsv2rgb(vec3(hue, 1, 1)), 1);
}
`;
const drawClosestLinesPointsFS = `#version 300 es
precision highp float;
in vec4 v_color;
out vec4 outColor;
void main() {
outColor = v_color;
}`;
const drawLinesVS = `#version 300 es
uniform sampler2D linesTex;
uniform mat4 matrix;
out vec4 v_color;
vec4 getAs1D(sampler2D tex, ivec2 dimensions, int index) {
int y = index / dimensions.x;
int x = index % dimensions.x;
return texelFetch(tex, ivec2(x, y), 0);
}
void main() {
ivec2 linesTexDimensions = textureSize(linesTex, 0);
// pull the position from the texture
vec4 position = getAs1D(linesTex, linesTexDimensions, gl_VertexID);
// do the common matrix math
gl_Position = matrix * vec4(position.xy, 0, 1);
// just so we can use the same fragment shader
v_color = vec4(0.8, 0.8, 0.8, 1);
}
`;
// Get A WebGL context
/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("#c212");
const gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
const ext = gl.getExtension('EXT_color_buffer_float');
if (!ext) {
alert('need EXT_color_buffer_float');
return;
}
function createShader(gl, type, src) {
const shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
}
function createProgram(gl, shaderSources, transformFeedbackVaryings) {
const program = gl.createProgram();
[gl.VERTEX_SHADER, gl.FRAGMENT_SHADER].forEach((type, ndx) => {
const shader = createShader(gl, type, shaderSources[ndx]);
gl.attachShader(program, shader);
});
if (transformFeedbackVaryings) {
gl.transformFeedbackVaryings(
program,
transformFeedbackVaryings,
gl.SEPARATE_ATTRIBS,
);
}
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error(gl.getProgramParameter(program));
}
return program;
}
// we're going to base the initial positions on the size
// of the canvas so lets update the size of the canvas
// to the initial size we want
resizeCanvasToDisplaySize(gl.canvas);
function createPoints(numPoints, ranges) {
const points = [];
for (let i = 0; i < numPoints; ++i) {
points.push(...ranges.map(range => r(...range)));
}
return points;
}
const r = (min, max) => min + Math.random() * (max - min);
const points = createPoints(8, [[0, gl.canvas.width], [0, gl.canvas.height]]);
const lines = createPoints(125 * 2, [[0, gl.canvas.width], [0, gl.canvas.height]]);
const numPoints = points.length / 2;
const numLineSegments = lines.length / 2 / 2;
const pointVelocities = createPoints(numPoints, [[-20, 20], [-20, 20]]);
const lineVelocities = createPoints(numLineSegments * 2, [[-20, 20], [-20, 20]]);
function makeBuffer(gl, sizeOrData, usage) {
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, sizeOrData, usage);
return buf;
}
const closestNdxBuffer = makeBuffer(gl, points.length * 4, gl.STATIC_DRAW);
const pointsBuffer1 = makeBuffer(gl, new Float32Array(points), gl.DYNAMIC_DRAW);
const pointsBuffer2 = makeBuffer(gl, new Float32Array(points), gl.DYNAMIC_DRAW);
const pointVelocitiesBuffer = makeBuffer(gl, new Float32Array(pointVelocities), gl.STATIC_DRAW);
const quadBuffer = makeBuffer(gl, new Float32Array([
-1, -1,
1, -1,
-1, 1,
-1, 1,
1, -1,
1, 1,
]), gl.STATIC_DRAW);
function createDataTexture(gl, data, numComponents, internalFormat, format, type) {
const numElements = data.length / numComponents;
// compute a size that will hold all of our data
const width = Math.ceil(Math.sqrt(numElements));
const height = Math.ceil(numElements / width);
const bin = new Float32Array(width * height * numComponents);
bin.set(data);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D,
0, // mip level
internalFormat,
width,
height,
0, // border
format,
type,
bin,
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
return {tex, dimensions: [width, height]};
}
const {tex: linesTex1, dimensions: linesTexDimensions1} =
createDataTexture(gl, lines, 2, gl.RG32F, gl.RG, gl.FLOAT);
const {tex: linesTex2, dimensions: linesTexDimensions2} =
createDataTexture(gl, lines, 2, gl.RG32F, gl.RG, gl.FLOAT);
const {tex: lineVelocitiesTex, dimensions: lineVelocitiesTexDimensions} =
createDataTexture(gl, lineVelocities, 2, gl.RG32F, gl.RG, gl.FLOAT);
const updatePositionPrg = createProgram(
gl, [updatePositionVS, updatePositionFS], ['newPosition']);
const updateLinesPrg = createProgram(
gl, [updateLinesVS, updateLinesFS]);
const closestLinePrg = createProgram(
gl, [closestLineVS, closestLineFS], ['closestNdx']);
const drawLinesPrg = createProgram(
gl, [drawLinesVS, drawClosestLinesPointsFS]);
const drawClosestLinesPrg = createProgram(
gl, [drawClosestLinesVS, drawClosestLinesPointsFS]);
const drawPointsPrg = createProgram(
gl, [drawPointsVS, drawClosestLinesPointsFS]);
const updatePositionPrgLocs = {
oldPosition: gl.getAttribLocation(updatePositionPrg, 'oldPosition'),
velocity: gl.getAttribLocation(updatePositionPrg, 'velocity'),
canvasDimensions: gl.getUniformLocation(updatePositionPrg, 'canvasDimensions'),
deltaTime: gl.getUniformLocation(updatePositionPrg, 'deltaTime'),
};
const updateLinesPrgLocs = {
position: gl.getAttribLocation(updateLinesPrg, 'position'),
linesTex: gl.getUniformLocation(updateLinesPrg, 'linesTex'),
velocityTex: gl.getUniformLocation(updateLinesPrg, 'velocityTex'),
canvasDimensions: gl.getUniformLocation(updateLinesPrg, 'canvasDimensions'),
deltaTime: gl.getUniformLocation(updateLinesPrg, 'deltaTime'),
};
const closestLinePrgLocs = {
point: gl.getAttribLocation(closestLinePrg, 'point'),
linesTex: gl.getUniformLocation(closestLinePrg, 'linesTex'),
numLineSegments: gl.getUniformLocation(closestLinePrg, 'numLineSegments'),
};
const drawLinesPrgLocs = {
linesTex: gl.getUniformLocation(drawLinesPrg, 'linesTex'),
matrix: gl.getUniformLocation(drawLinesPrg, 'matrix'),
};
const drawClosestLinesPrgLocs = {
closestNdx: gl.getAttribLocation(drawClosestLinesPrg, 'closestNdx'),
linesTex: gl.getUniformLocation(drawClosestLinesPrg, 'linesTex'),
matrix: gl.getUniformLocation(drawClosestLinesPrg, 'matrix'),
numPoints: gl.getUniformLocation(drawClosestLinesPrg, 'numPoints'),
};
const drawPointsPrgLocs = {
point: gl.getAttribLocation(drawPointsPrg, 'point'),
matrix: gl.getUniformLocation(drawPointsPrg, 'matrix'),
numPoints: gl.getUniformLocation(drawPointsPrg, 'numPoints'),
};
function makeVertexArray(gl, bufLocPairs) {
const va = gl.createVertexArray();
gl.bindVertexArray(va);
for (const [buffer, loc] of bufLocPairs) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(
loc, // attribute location
2, // number of elements
gl.FLOAT, // type of data
false, // normalize
0, // stride (0 = auto)
0, // offset
);
}
return va;
}
const updatePositionVA1 = makeVertexArray(gl, [
[pointsBuffer1, updatePositionPrgLocs.oldPosition],
[pointVelocitiesBuffer, updatePositionPrgLocs.velocity],
]);
const updatePositionVA2 = makeVertexArray(gl, [
[pointsBuffer2, updatePositionPrgLocs.oldPosition],
[pointVelocitiesBuffer, updatePositionPrgLocs.velocity],
]);
const updateLinesVA = makeVertexArray(gl, [
[quadBuffer, updateLinesPrgLocs.position],
]);
const closestLinesVA1 = makeVertexArray(gl, [
[pointsBuffer1, closestLinePrgLocs.point],
]);
const closestLinesVA2 = makeVertexArray(gl, [
[pointsBuffer2, closestLinePrgLocs.point],
]);
const drawClosestLinesVA = gl.createVertexArray();
gl.bindVertexArray(drawClosestLinesVA);
gl.bindBuffer(gl.ARRAY_BUFFER, closestNdxBuffer);
gl.enableVertexAttribArray(drawClosestLinesPrgLocs.closestNdx);
gl.vertexAttribIPointer(drawClosestLinesPrgLocs.closestNdx, 1, gl.INT, 0, 0);
gl.vertexAttribDivisor(drawClosestLinesPrgLocs.closestNdx, 1);
const drawPointsVA1 = makeVertexArray(gl, [
[pointsBuffer1, drawPointsPrgLocs.point],
]);
const drawPointsVA2 = makeVertexArray(gl, [
[pointsBuffer2, drawPointsPrgLocs.point],
]);
function makeTransformFeedback(gl, buffer) {
const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer);
return tf;
}
const pointsTF1 = makeTransformFeedback(gl, pointsBuffer1);
const pointsTF2 = makeTransformFeedback(gl, pointsBuffer2);
const closestNdxTF = makeTransformFeedback(gl, closestNdxBuffer);
function createFramebuffer(gl, tex) {
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
return fb;
}
const linesFB1 = createFramebuffer(gl, linesTex1);
const linesFB2 = createFramebuffer(gl, linesTex2);
function drawArraysWithTransformFeedback(gl, tf, primitiveType, count) {
// turn of using the fragment shader
gl.enable(gl.RASTERIZER_DISCARD);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(primitiveType, 0, count);
gl.endTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
// turn on using fragment shaders again
gl.disable(gl.RASTERIZER_DISCARD);
}
let current = {
// for updating points
updatePositionVA: updatePositionVA1, // read from points1
pointsTF: pointsTF2, // write to points2
// for updating line endings
linesTex: linesTex1, // read from linesTex1
linesFB: linesFB2, // write to linesTex2
// for computing closest lines
closestLinesVA: closestLinesVA2, // read from points2
// for drawing all lines and closest lines
allLinesTex: linesTex2, // read from linesTex2
// for drawing points
drawPointsVA: drawPointsVA2, // read form points2
};
let next = {
// for updating points
updatePositionVA: updatePositionVA2, // read from points2
pointsTF: pointsTF1, // write to points1
// for updating line endings
linesTex: linesTex2, // read from linesTex2
linesFB: linesFB1, // write to linesTex1
// for computing closest lines
closestLinesVA: closestLinesVA1, // read from points1
// for drawing all lines and closest lines
allLinesTex: linesTex1, // read from linesTex1
// for drawing points
drawPointsVA: drawPointsVA1, // read form points1
};
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
function updatePointPositions(deltaTime) {
gl.bindVertexArray(current.updatePositionVA);
gl.useProgram(updatePositionPrg);
gl.uniform1f(updatePositionPrgLocs.deltaTime, deltaTime);
gl.uniform2f(updatePositionPrgLocs.canvasDimensions, gl.canvas.width, gl.canvas.height);
drawArraysWithTransformFeedback(gl, current.pointsTF, gl.POINTS, numPoints);
}
function updateLineEndPoints(deltaTime) {
// Update the line endpoint positions ---------------------
gl.bindVertexArray(updateLinesVA); // just a quad
gl.useProgram(updateLinesPrg);
// bind texture to texture units 0 and 1
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, current.linesTex);
gl.activeTexture(gl.TEXTURE0 + 1);
gl.bindTexture(gl.TEXTURE_2D, lineVelocitiesTex);
// tell the shader to look at the textures on texture units 0 and 1
gl.uniform1i(updateLinesPrgLocs.linesTex, 0);
gl.uniform1i(updateLinesPrgLocs.velocityTex, 1);
gl.uniform1f(updateLinesPrgLocs.deltaTime, deltaTime);
gl.uniform2f(updateLinesPrgLocs.canvasDimensions, gl.canvas.width, gl.canvas.height);
// write to the other lines texture
gl.bindFramebuffer(gl.FRAMEBUFFER, current.linesFB);
gl.viewport(0, 0, ...lineVelocitiesTexDimensions);
// drawing a clip space -1 to +1 quad = map over entire destination array
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
function computeClosestLines() {
gl.bindVertexArray(current.closestLinesVA);
gl.useProgram(closestLinePrg);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, current.linesTex);
gl.uniform1i(closestLinePrgLocs.linesTex, 0);
gl.uniform1i(closestLinePrgLocs.numLineSegments, numLineSegments);
drawArraysWithTransformFeedback(gl, closestNdxTF, gl.POINTS, numPoints);
}
function drawAllLines(matrix) {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.bindVertexArray(null);
gl.useProgram(drawLinesPrg);
// bind the lines texture to texture unit 0
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, current.allLinesTex);
// Tell the shader to use texture on texture unit 0
gl.uniform1i(drawLinesPrgLocs.linesTex, 0);
gl.uniformMatrix4fv(drawLinesPrgLocs.matrix, false, matrix);
gl.drawArrays(gl.LINES, 0, numLineSegments * 2);
}
function drawClosestLines(matrix) {
gl.bindVertexArray(drawClosestLinesVA);
gl.useProgram(drawClosestLinesPrg);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, current.allLinesTex);
gl.uniform1i(drawClosestLinesPrgLocs.linesTex, 0);
gl.uniform1f(drawClosestLinesPrgLocs.numPoints, numPoints);
gl.uniformMatrix4fv(drawClosestLinesPrgLocs.matrix, false, matrix);
gl.drawArraysInstanced(gl.LINES, 0, 2, numPoints);
}
function drawPoints(matrix) {
gl.bindVertexArray(current.drawPointsVA);
gl.useProgram(drawPointsPrg);
gl.uniform1f(drawPointsPrgLocs.numPoints, numPoints);
gl.uniformMatrix4fv(drawPointsPrgLocs.matrix, false, matrix);
gl.drawArrays(gl.POINTS, 0, numPoints);
}
let then = 0;
function render(time) {
// convert to seconds
time *= 0.001;
// Subtract the previous time from the current time
const deltaTime = time - then;
// Remember the current time for the next frame.
then = time;
resizeCanvasToDisplaySize(gl.canvas);
gl.clear(gl.COLOR_BUFFER_BIT);
updatePointPositions(deltaTime);
updateLineEndPoints(deltaTime);
computeClosestLines();
const matrix = m4.ortho(0, gl.canvas.width, 0, gl.canvas.height, -1, 1);
drawAllLines(matrix);
drawClosestLines(matrix);
drawPoints(matrix);
// swap
{
const temp = current;
current = next;
next = temp;
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main12();
</script>
</html>