Spaces:
Running
Running
Update test.js
Browse files
test.js
CHANGED
@@ -1,205 +1,280 @@
|
|
1 |
-
|
2 |
-
<html lang="en">
|
3 |
-
<head>
|
4 |
-
<meta charset="UTF-8" />
|
5 |
-
<title>3D Cymatic Display in WebGL2</title>
|
6 |
-
<style>
|
7 |
-
body, html { margin: 0; height: 100%; overflow: hidden; background: #000; }
|
8 |
-
canvas { width: 100%; height: 100%; display: block; }
|
9 |
-
</style>
|
10 |
-
</head>
|
11 |
-
<body>
|
12 |
-
<canvas id="canvas"></canvas>
|
13 |
-
<!-- Include glMatrix and webgl-utils scripts -->
|
14 |
-
<script src="https://cdn.jsdelivr.net/npm/[email protected]/gl-matrix-min.js"></script>
|
15 |
-
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
|
16 |
-
<script type="text/javascript">
|
17 |
-
"use strict";
|
18 |
-
function main() {
|
19 |
-
const canvas = document.querySelector("#canvas");
|
20 |
-
const gl = canvas.getContext("webgl2");
|
21 |
-
if (!gl) {
|
22 |
-
console.error("WebGL 2 not supported");
|
23 |
-
return;
|
24 |
-
}
|
25 |
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
out vec3 v_position;
|
34 |
-
const float PI = 3.14159;
|
35 |
-
void main() {
|
36 |
-
// Amplitudes for two modes
|
37 |
-
float A1 = 0.1;
|
38 |
-
float A2 = 0.05;
|
39 |
-
// Mode 1: Fundamental vibration (nodal lines at the boundaries)
|
40 |
-
float mode1 = A1 * sin(PI * (a_position.x + 1.0) / 2.0)
|
41 |
-
* sin(PI * (a_position.y + 1.0) / 2.0)
|
42 |
-
* cos(3.0 * u_time);
|
43 |
-
// Mode 2: A higher-order vibration for additional detail
|
44 |
-
float mode2 = A2 * sin(2.0 * PI * (a_position.x + 1.0) / 2.0)
|
45 |
-
* sin(PI * (a_position.y + 1.0) / 2.0)
|
46 |
-
* cos(5.0 * u_time);
|
47 |
-
float z = mode1 + mode2;
|
48 |
-
|
49 |
-
// Compute partial derivatives for normal calculation
|
50 |
-
float dx1 = A1 * (PI/2.0) * cos(PI*(a_position.x+1.0)/2.0)
|
51 |
-
* sin(PI*(a_position.y+1.0)/2.0) * cos(3.0 * u_time);
|
52 |
-
float dx2 = A2 * (2.0*PI/2.0) * cos(2.0*PI*(a_position.x+1.0)/2.0)
|
53 |
-
* sin(PI*(a_position.y+1.0)/2.0) * cos(5.0 * u_time);
|
54 |
-
float dfdx = dx1 + dx2;
|
55 |
-
|
56 |
-
float dy1 = A1 * (PI/2.0) * sin(PI*(a_position.x+1.0)/2.0)
|
57 |
-
* cos(PI*(a_position.y+1.0)/2.0) * cos(3.0 * u_time);
|
58 |
-
float dy2 = A2 * (PI/2.0) * sin(2.0*PI*(a_position.x+1.0)/2.0)
|
59 |
-
* cos(PI*(a_position.y+1.0)/2.0) * cos(5.0 * u_time);
|
60 |
-
float dfdy = dy1 + dy2;
|
61 |
-
|
62 |
-
vec3 displacedPos = vec3(a_position.x, a_position.y, z);
|
63 |
-
// The normal is computed as the normalized cross of the tangent derivatives.
|
64 |
-
// For a height field, an approximate normal is: (-dfdx, -dfdy, 1)
|
65 |
-
vec3 normal = normalize(vec3(-dfdx, -dfdy, 1.0));
|
66 |
-
|
67 |
-
v_normal = normal;
|
68 |
-
v_position = displacedPos;
|
69 |
-
gl_Position = u_MVP * vec4(displacedPos, 1.0);
|
70 |
-
}`;
|
71 |
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
void main() {
|
81 |
-
vec3 normal = normalize(v_normal);
|
82 |
-
vec3 lightDir = normalize(u_lightPos - v_position);
|
83 |
-
float diff = max(dot(normal, lightDir), 0.0);
|
84 |
-
vec3 ambient = vec3(0.2);
|
85 |
-
vec3 diffuse = diff * vec3(0.7, 0.7, 0.8);
|
86 |
-
vec3 viewDir = normalize(u_viewPos - v_position);
|
87 |
-
vec3 reflectDir = reflect(-lightDir, normal);
|
88 |
-
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
|
89 |
-
vec3 specular = vec3(0.3) * spec;
|
90 |
-
vec3 color = ambient + diffuse + specular;
|
91 |
-
outColor = vec4(color, 1.0);
|
92 |
-
}`;
|
93 |
-
|
94 |
-
// Create the shader program using webgl-utils
|
95 |
-
const program = webglUtils.createProgramFromSources(gl, [vsSource, fsSource]);
|
96 |
-
|
97 |
-
// Look up attribute and uniform locations
|
98 |
-
const positionAttribLocation = gl.getAttribLocation(program, "a_position");
|
99 |
-
const timeUniformLocation = gl.getUniformLocation(program, "u_time");
|
100 |
-
const mvpUniformLocation = gl.getUniformLocation(program, "u_MVP");
|
101 |
-
const lightPosUniformLocation = gl.getUniformLocation(program, "u_lightPos");
|
102 |
-
const viewPosUniformLocation = gl.getUniformLocation(program, "u_viewPos");
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
}
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
|
|
126 |
}
|
127 |
-
}
|
128 |
-
|
129 |
-
// Create and bind a Vertex Array Object
|
130 |
-
const vao = gl.createVertexArray();
|
131 |
-
gl.bindVertexArray(vao);
|
132 |
-
|
133 |
-
// Create and fill the position buffer
|
134 |
-
const positionBuffer = gl.createBuffer();
|
135 |
-
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
136 |
-
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
|
137 |
-
gl.enableVertexAttribArray(positionAttribLocation);
|
138 |
-
gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, false, 0, 0);
|
139 |
-
|
140 |
-
// Create and fill the index buffer
|
141 |
-
const indexBuffer = gl.createBuffer();
|
142 |
-
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
143 |
-
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), gl.STATIC_DRAW);
|
144 |
-
|
145 |
-
// Set up camera matrices using glMatrix
|
146 |
-
const fieldOfView = 45 * Math.PI / 180;
|
147 |
-
const near = 0.1;
|
148 |
-
const far = 100;
|
149 |
-
let projectionMatrix = glMatrix.mat4.create();
|
150 |
-
let viewMatrix = glMatrix.mat4.create();
|
151 |
-
let modelMatrix = glMatrix.mat4.create();
|
152 |
-
const eye = [0, -2.5, 2.5]; // Camera position
|
153 |
-
const center = [0, 0, 0]; // Look at center of plate
|
154 |
-
const up = [0, 0, 1];
|
155 |
-
glMatrix.mat4.perspective(projectionMatrix, fieldOfView, canvas.clientWidth / canvas.clientHeight, near, far);
|
156 |
-
glMatrix.mat4.lookAt(viewMatrix, eye, center, up);
|
157 |
-
// Optionally, rotate the model slightly for a dramatic view
|
158 |
-
glMatrix.mat4.rotateX(modelMatrix, modelMatrix, -0.5);
|
159 |
-
let mvpMatrix = glMatrix.mat4.create();
|
160 |
-
glMatrix.mat4.multiply(mvpMatrix, viewMatrix, modelMatrix);
|
161 |
-
glMatrix.mat4.multiply(mvpMatrix, projectionMatrix, mvpMatrix);
|
162 |
-
|
163 |
-
// Set light and view positions (for the shader)
|
164 |
-
const lightPos = [2.0, -2.0, 3.0];
|
165 |
-
const viewPos = eye;
|
166 |
-
|
167 |
-
// Animation loop
|
168 |
-
let startTime = null;
|
169 |
-
function render(now) {
|
170 |
-
if (!startTime) startTime = now;
|
171 |
-
const timeInSeconds = (now - startTime) * 0.001;
|
172 |
-
|
173 |
-
// Resize canvas if needed
|
174 |
-
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
|
175 |
-
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
176 |
-
gl.enable(gl.DEPTH_TEST);
|
177 |
-
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
178 |
-
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
179 |
-
|
180 |
-
// Update the projection in case the canvas size changed
|
181 |
-
glMatrix.mat4.perspective(projectionMatrix, fieldOfView, gl.canvas.clientWidth / gl.canvas.clientHeight, near, far);
|
182 |
-
glMatrix.mat4.lookAt(viewMatrix, eye, center, up);
|
183 |
-
glMatrix.mat4.multiply(mvpMatrix, viewMatrix, modelMatrix);
|
184 |
-
glMatrix.mat4.multiply(mvpMatrix, projectionMatrix, mvpMatrix);
|
185 |
-
|
186 |
-
// Use our program and bind the VAO
|
187 |
-
gl.useProgram(program);
|
188 |
-
gl.bindVertexArray(vao);
|
189 |
-
|
190 |
-
// Set shader uniforms
|
191 |
-
gl.uniform1f(timeUniformLocation, timeInSeconds);
|
192 |
-
gl.uniformMatrix4fv(mvpUniformLocation, false, mvpMatrix);
|
193 |
-
gl.uniform3fv(lightPosUniformLocation, lightPos);
|
194 |
-
gl.uniform3fv(viewPosUniformLocation, viewPos);
|
195 |
-
|
196 |
-
// Draw the grid
|
197 |
-
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0);
|
198 |
-
requestAnimationFrame(render);
|
199 |
-
}
|
200 |
-
requestAnimationFrame(render);
|
201 |
}
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use strict";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
+
function main() {
|
4 |
+
// Get a WebGL2 context
|
5 |
+
const canvas = document.querySelector("#canvas");
|
6 |
+
const gl = canvas.getContext("webgl2");
|
7 |
+
if (!gl) {
|
8 |
+
return;
|
9 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
//ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
12 |
+
// Vertex shader: simply pass the vertex positions along.
|
13 |
+
const vs = `#version 300 es
|
14 |
+
in vec4 a_position;
|
15 |
+
void main() {
|
16 |
+
gl_Position = a_position;
|
17 |
+
}
|
18 |
+
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
//ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
21 |
+
// Fragment shader: a scientificallyβinspired 3D cymatic display.
|
22 |
+
// This shader rayβmarches a vibrating βplateβ (whose height is defined
|
23 |
+
// by the sum of two sinusoidal (mode) functions) and then shades it with
|
24 |
+
// diffuse and specular lighting. The palette() function is used to inject
|
25 |
+
// a pleasing color variation based on the local vibration amplitude.
|
26 |
+
const fs = `#version 300 es
|
27 |
+
precision highp float;
|
28 |
+
|
29 |
+
uniform vec2 iResolution;
|
30 |
+
uniform vec2 iMouse;
|
31 |
+
uniform float iTime;
|
32 |
+
out vec4 outColor;
|
33 |
+
|
34 |
+
// A color palette function (from Shadertoy) to add some βpopβ
|
35 |
+
vec3 palette( float t ) {
|
36 |
+
vec3 a = vec3(0.5, 0.5, 0.5);
|
37 |
+
vec3 b = vec3(0.5, 0.5, 0.5);
|
38 |
+
vec3 c = vec3(1.0, 1.0, 1.0);
|
39 |
+
vec3 d = vec3(0.263, 0.416, 0.557);
|
40 |
+
return a + b * cos( 6.28318 * (c * t + d) );
|
41 |
+
}
|
42 |
+
|
43 |
+
// The vibrating plate β defined on the xzβplane (with x,z in [-1,1])
|
44 |
+
// and with vertical displacement given by y = plate(x,z,t).
|
45 |
+
// Two modes are added (a βfundamentalβ and a secondβharmonic mode) to mimic
|
46 |
+
// realistic cymatic (Chladni) patterns on a clamped plate.
|
47 |
+
float plate(vec2 pos, float t) {
|
48 |
+
// Map pos from [-1,1] to [0,1] (for clampedβedge conditions)
|
49 |
+
vec2 uv = (pos + 1.0) * 0.5;
|
50 |
+
float mode1 = sin(3.14159 * uv.x) * sin(3.14159 * uv.y) * cos(3.14159 * t);
|
51 |
+
float mode2 = sin(2.0 * 3.14159 * uv.x) * sin(2.0 * 3.14159 * uv.y) * cos(2.0 * 3.14159 * t);
|
52 |
+
return 0.2 * (mode1 + mode2);
|
53 |
+
}
|
54 |
+
|
55 |
+
// Compute the normal of the heightfield (the vibrating plate) using finite differences.
|
56 |
+
vec3 calcNormal(vec2 pos, float t) {
|
57 |
+
float eps = 0.001;
|
58 |
+
float h = plate(pos, t);
|
59 |
+
float hx = plate(pos + vec2(eps, 0.0), t) - h;
|
60 |
+
float hz = plate(pos + vec2(0.0, eps), t) - h;
|
61 |
+
return normalize(vec3(-hx, 1.0, -hz));
|
62 |
+
}
|
63 |
+
|
64 |
+
// Given a 3D point p, return its vertical distance to the plate surface.
|
65 |
+
// (If p is exactly on the surface then p.y = plate(p.xz,t) and the result is zero.)
|
66 |
+
float mapHeight(vec3 p, float t) {
|
67 |
+
// Outside the domain x,z β [-1,1] we assume a flat floor at y=0.
|
68 |
+
if (abs(p.x) > 1.0 || abs(p.z) > 1.0) {
|
69 |
+
return p.y;
|
70 |
+
}
|
71 |
+
return p.y - plate(vec2(p.x, p.z), t);
|
72 |
+
}
|
73 |
+
|
74 |
+
// A simple raycast function that marches a ray from the camera and
|
75 |
+
// returns the distance along the ray at which the plate is hit.
|
76 |
+
float raycast(vec3 ro, vec3 rd, float t) {
|
77 |
+
float tMin = 0.0;
|
78 |
+
float tMax = 20.0;
|
79 |
+
float tCurrent = tMin;
|
80 |
+
float stepSize = 0.02;
|
81 |
+
bool hit = false;
|
82 |
+
for (int i = 0; i < 500; i++) {
|
83 |
+
vec3 pos = ro + rd * tCurrent;
|
84 |
+
float d = mapHeight(pos, t);
|
85 |
+
if (d < 0.001) {
|
86 |
+
hit = true;
|
87 |
+
break;
|
88 |
}
|
89 |
+
tCurrent += stepSize;
|
90 |
+
if (tCurrent > tMax) break;
|
91 |
+
}
|
92 |
+
if (!hit) return -1.0;
|
93 |
+
// Refine the hit point with a short binary search.
|
94 |
+
float tA = tCurrent - stepSize;
|
95 |
+
float tB = tCurrent;
|
96 |
+
for (int i = 0; i < 10; i++) {
|
97 |
+
float tMid = (tA + tB) * 0.5;
|
98 |
+
float dMid = mapHeight(ro + rd * tMid, t);
|
99 |
+
if (dMid > 0.0) {
|
100 |
+
tA = tMid;
|
101 |
+
} else {
|
102 |
+
tB = tMid;
|
103 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
}
|
105 |
+
return (tA + tB) * 0.5;
|
106 |
+
}
|
107 |
+
|
108 |
+
void main() {
|
109 |
+
// Compute normalized screen coordinates (centered on 0)
|
110 |
+
vec2 uv = (gl_FragCoord.xy - 0.5 * iResolution.xy) / iResolution.y;
|
111 |
+
|
112 |
+
// Use the mouse to control the cameraβs azimuth and pitch.
|
113 |
+
// Horizontal movement rotates 0β2Ο; vertical movement adjusts pitch.
|
114 |
+
float angle = iMouse.x / iResolution.x * 6.28318; // full rotation
|
115 |
+
float pitch = mix(0.4, 1.2, iMouse.y / iResolution.y);
|
116 |
+
float radius = 4.0;
|
117 |
+
vec3 ro = vec3(
|
118 |
+
radius * cos(pitch) * cos(angle),
|
119 |
+
radius * sin(pitch),
|
120 |
+
radius * cos(pitch) * sin(angle)
|
121 |
+
);
|
122 |
+
vec3 target = vec3(0.0, 0.0, 0.0);
|
123 |
+
|
124 |
+
// Construct a simple camera coordinate system.
|
125 |
+
vec3 forward = normalize(target - ro);
|
126 |
+
vec3 right = normalize(cross(forward, vec3(0.0, 1.0, 0.0)));
|
127 |
+
vec3 up = cross(right, forward);
|
128 |
+
|
129 |
+
// Compute the ray direction using a basic perspective projection.
|
130 |
+
vec3 rd = normalize(forward + uv.x * right + uv.y * up);
|
131 |
+
|
132 |
+
// March the ray to see if and where it hits the vibrating plate.
|
133 |
+
float tHit = raycast(ro, rd, iTime);
|
134 |
+
vec3 color;
|
135 |
+
if (tHit > 0.0) {
|
136 |
+
vec3 pos = ro + rd * tHit;
|
137 |
+
// Get the local normal from the heightfield
|
138 |
+
vec3 normal = calcNormal(vec2(pos.x, pos.z), iTime);
|
139 |
+
|
140 |
+
// Standard lighting: diffuse + specular
|
141 |
+
vec3 lightDir = normalize(vec3(0.5, 1.0, 0.8));
|
142 |
+
float diff = max(dot(normal, lightDir), 0.0);
|
143 |
+
vec3 viewDir = normalize(ro - pos);
|
144 |
+
vec3 halfDir = normalize(lightDir + viewDir);
|
145 |
+
float spec = pow(max(dot(normal, halfDir), 0.0), 32.0);
|
146 |
+
|
147 |
+
// Base color comes from the palette β modulated by the local vibration amplitude.
|
148 |
+
float h = plate(vec2(pos.x, pos.z), iTime);
|
149 |
+
vec3 baseColor = palette(h * 5.0);
|
150 |
+
|
151 |
+
color = baseColor * diff + vec3(0.1) * spec + vec3(0.1);
|
152 |
+
} else {
|
153 |
+
// If no hit, use a subtle background gradient.
|
154 |
+
color = mix(vec3(0.0, 0.0, 0.1), vec3(0.0), uv.y + 0.5);
|
155 |
+
}
|
156 |
+
|
157 |
+
outColor = vec4(color, 1.0);
|
158 |
+
}
|
159 |
+
`;
|
160 |
+
|
161 |
+
//βββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½ββββββββββββββββ
|
162 |
+
// Create and compile the shader program using webgl-utils.
|
163 |
+
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
|
164 |
+
|
165 |
+
// Look up attribute and uniform locations.
|
166 |
+
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
|
167 |
+
const resolutionLocation = gl.getUniformLocation(program, "iResolution");
|
168 |
+
const mouseLocation = gl.getUniformLocation(program, "iMouse");
|
169 |
+
const timeLocation = gl.getUniformLocation(program, "iTime");
|
170 |
+
|
171 |
+
// Create a vertex array object (VAO) and bind it.
|
172 |
+
const vao = gl.createVertexArray();
|
173 |
+
gl.bindVertexArray(vao);
|
174 |
+
|
175 |
+
// Create a buffer and put a fullβscreen quad in it.
|
176 |
+
const positionBuffer = gl.createBuffer();
|
177 |
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
178 |
+
gl.bufferData(
|
179 |
+
gl.ARRAY_BUFFER,
|
180 |
+
new Float32Array([
|
181 |
+
-1, -1,
|
182 |
+
1, -1,
|
183 |
+
-1, 1,
|
184 |
+
-1, 1,
|
185 |
+
1, -1,
|
186 |
+
1, 1,
|
187 |
+
]),
|
188 |
+
gl.STATIC_DRAW
|
189 |
+
);
|
190 |
+
|
191 |
+
// Enable the position attribute.
|
192 |
+
gl.enableVertexAttribArray(positionAttributeLocation);
|
193 |
+
gl.vertexAttribPointer(
|
194 |
+
positionAttributeLocation,
|
195 |
+
2, // 2 components per vertex
|
196 |
+
gl.FLOAT, // data type is float
|
197 |
+
false,
|
198 |
+
0,
|
199 |
+
0
|
200 |
+
);
|
201 |
+
|
202 |
+
//ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
203 |
+
// Setup mouse / touch interactions.
|
204 |
+
const playpauseElem = document.querySelector(".playpause");
|
205 |
+
const inputElem = document.querySelector(".divcanvas");
|
206 |
+
inputElem.addEventListener("mouseover", requestFrame);
|
207 |
+
inputElem.addEventListener("mouseout", cancelFrame);
|
208 |
+
|
209 |
+
let mouseX = 0;
|
210 |
+
let mouseY = 0;
|
211 |
+
function setMousePosition(e) {
|
212 |
+
const rect = inputElem.getBoundingClientRect();
|
213 |
+
mouseX = e.clientX - rect.left;
|
214 |
+
mouseY = rect.height - (e.clientY - rect.top) - 1;
|
215 |
+
}
|
216 |
+
|
217 |
+
inputElem.addEventListener("mousemove", setMousePosition);
|
218 |
+
inputElem.addEventListener("touchstart", (e) => {
|
219 |
+
e.preventDefault();
|
220 |
+
playpauseElem.classList.add("playpausehide");
|
221 |
+
requestFrame();
|
222 |
+
}, { passive: false });
|
223 |
+
inputElem.addEventListener("touchmove", (e) => {
|
224 |
+
e.preventDefault();
|
225 |
+
setMousePosition(e.touches[0]);
|
226 |
+
}, { passive: false });
|
227 |
+
inputElem.addEventListener("touchend", (e) => {
|
228 |
+
e.preventDefault();
|
229 |
+
playpauseElem.classList.remove("playpausehide");
|
230 |
+
cancelFrame();
|
231 |
+
}, { passive: false });
|
232 |
+
|
233 |
+
//ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
234 |
+
// Animation loop variables and functions.
|
235 |
+
let requestId;
|
236 |
+
function requestFrame() {
|
237 |
+
if (!requestId) {
|
238 |
+
requestId = requestAnimationFrame(render);
|
239 |
+
}
|
240 |
+
}
|
241 |
+
function cancelFrame() {
|
242 |
+
if (requestId) {
|
243 |
+
cancelAnimationFrame(requestId);
|
244 |
+
requestId = undefined;
|
245 |
+
}
|
246 |
+
}
|
247 |
+
|
248 |
+
let then = 0;
|
249 |
+
let time = 0;
|
250 |
+
function render(now) {
|
251 |
+
requestId = undefined;
|
252 |
+
now *= 0.001; // convert milliseconds to seconds
|
253 |
+
const elapsedTime = Math.min(now - then, 0.1);
|
254 |
+
time += elapsedTime;
|
255 |
+
then = now;
|
256 |
+
|
257 |
+
// Resize canvas if needed.
|
258 |
+
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
|
259 |
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
260 |
+
|
261 |
+
// Use our program and bind our VAO.
|
262 |
+
gl.useProgram(program);
|
263 |
+
gl.bindVertexArray(vao);
|
264 |
+
|
265 |
+
// Set the uniforms.
|
266 |
+
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
|
267 |
+
gl.uniform2f(mouseLocation, mouseX, mouseY);
|
268 |
+
gl.uniform1f(timeLocation, time);
|
269 |
+
|
270 |
+
// Draw the fullβscreen quad.
|
271 |
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
272 |
+
|
273 |
+
requestFrame();
|
274 |
+
}
|
275 |
+
|
276 |
+
requestFrame();
|
277 |
+
requestAnimationFrame(cancelFrame);
|
278 |
+
}
|
279 |
+
|
280 |
+
main();
|