Severian commited on
Commit
b4a37d1
Β·
verified Β·
1 Parent(s): 682ec84

Update test.js

Browse files
Files changed (1) hide show
  1. test.js +117 -135
test.js CHANGED
@@ -2,6 +2,7 @@
2
 
3
  function main() {
4
  // Get a WebGL2 context
 
5
  const canvas = document.querySelector("#canvas");
6
  const gl = canvas.getContext("webgl2");
7
  if (!gl) {
@@ -9,7 +10,8 @@ function main() {
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() {
@@ -18,11 +20,10 @@ function main() {
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
 
@@ -31,147 +32,134 @@ function main() {
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);
@@ -187,25 +175,25 @@ function main() {
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) {
@@ -213,7 +201,6 @@ function main() {
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();
@@ -229,9 +216,9 @@ function main() {
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) {
@@ -244,32 +231,27 @@ function main() {
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
 
 
2
 
3
  function main() {
4
  // Get a WebGL2 context
5
+ /** @type {HTMLCanvasElement} */
6
  const canvas = document.querySelector("#canvas");
7
  const gl = canvas.getContext("webgl2");
8
  if (!gl) {
 
10
  }
11
 
12
  //────────────────────────────────────────────────────────────
13
+ // Vertex Shader
14
+ // Simply passes along vertex positions.
15
  const vs = `#version 300 es
16
  in vec4 a_position;
17
  void main() {
 
20
  `;
21
 
22
  //────────────────────────────────────────────────────────────
23
+ // Fragment Shader
24
+ // This shader ray–marches a sphere whose radius is perturbed by multiple
25
+ // cymatic (sine–based) modes. Its surface is lit with diffuse and specular
26
+ // shading and decorated with intricate stripe textures and a wild color palette.
 
27
  const fs = `#version 300 es
28
  precision highp float;
29
 
 
32
  uniform float iTime;
33
  out vec4 outColor;
34
 
35
+ // A wild palette function that returns vivid colors.
36
+ vec3 wildPalette(float t) {
37
+ vec3 a = vec3(0.5, 0.5, 0.5);
38
+ vec3 b = vec3(0.5, 0.5, 0.5);
39
+ vec3 c = vec3(1.0, 1.0, 1.0);
40
+ vec3 d = vec3(0.0, 0.33, 0.67);
41
+ return a + b * cos(6.28318 * (c * t + d));
42
  }
43
 
44
+ // The sphere’s surface is modulated by several sine–based modes.
45
+ // We compute a β€œdisplacement” that is added to the base radius.
46
+ float sphereDisplacement(vec3 p, float t) {
47
+ float r = length(p);
48
+ float theta = acos(p.y / r);
49
+ float phi = atan(p.z, p.x);
50
+ // Three modes for a rich, cymatic effect:
51
+ float d1 = sin(3.0 * theta + t) * sin(4.0 * phi + 1.3 * t);
52
+ float d2 = cos(5.0 * theta - 0.7 * t) * sin(2.0 * phi + 1.1 * t);
53
+ float d3 = sin(7.0 * theta + 3.0 * t) * cos(6.0 * phi - 2.0 * t);
54
+ return 0.2 * (d1 + d2 + d3);
55
  }
56
 
57
+ // Signed distance function for the deformed (cymatic) sphere.
58
+ // Base sphere has radius 1.0; its radius is perturbed by sphereDisplacement.
59
+ float sdCymaticSphere(vec3 p, float t) {
60
+ return length(p) - (1.0 + sphereDisplacement(p, t));
 
 
 
61
  }
62
 
63
+ // Compute the normal at point p via finite differences of the SDF.
64
+ vec3 calcNormal(vec3 p, float t) {
65
+ float eps = 0.001;
66
+ vec3 n;
67
+ n.x = sdCymaticSphere(p + vec3(eps, 0.0, 0.0), t) - sdCymaticSphere(p - vec3(eps, 0.0, 0.0), t);
68
+ n.y = sdCymaticSphere(p + vec3(0.0, eps, 0.0), t) - sdCymaticSphere(p - vec3(0.0, eps, 0.0), t);
69
+ n.z = sdCymaticSphere(p + vec3(0.0, 0.0, eps), t) - sdCymaticSphere(p - vec3(0.0, 0.0, eps), t);
70
+ return normalize(n);
71
  }
72
 
73
+ // Ray–marching routine to find the intersection of a ray with the deformed sphere.
74
+ float raymarch(vec3 ro, vec3 rd, float t, out vec3 pos) {
75
+ float depth = 0.0;
76
+ for (int i = 0; i < 100; i++) {
77
+ pos = ro + rd * depth;
78
+ float dist = sdCymaticSphere(pos, t);
79
+ if (abs(dist) < 0.001) {
80
+ return depth;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
+ depth += dist;
83
+ if (depth >= 20.0) break;
84
+ }
85
+ return -1.0;
86
  }
87
 
88
  void main() {
89
+ // Normalized pixel coordinates (centered on zero)
90
+ vec2 uv = (gl_FragCoord.xy - 0.5 * iResolution.xy) / iResolution.y;
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ // Use mouse position to control the camera’s azimuth and pitch.
93
+ float angle = iMouse.x / iResolution.x * 6.28318;
94
+ float pitch = mix(0.3, 1.2, iMouse.y / iResolution.y);
95
+ float radius = 4.0;
96
+ vec3 ro = vec3(
97
+ radius * cos(pitch) * cos(angle),
98
+ radius * sin(pitch),
99
+ radius * cos(pitch) * sin(angle)
100
+ );
101
+ vec3 target = vec3(0.0);
102
 
103
+ // Build a simple camera coordinate system.
104
+ vec3 forward = normalize(target - ro);
105
+ vec3 right = normalize(cross(forward, vec3(0.0, 1.0, 0.0)));
106
+ vec3 up = cross(right, forward);
107
 
108
+ // Compute the ray direction.
109
+ vec3 rd = normalize(forward + uv.x * right + uv.y * up);
 
 
 
 
 
110
 
111
+ // Ray–march the scene.
112
+ vec3 pos;
113
+ float d = raymarch(ro, rd, iTime, pos);
114
+ vec3 color;
115
+ if (d > 0.0) {
116
+ // Surface hit: compute normal for lighting.
117
+ vec3 normal = calcNormal(pos, iTime);
118
+ vec3 lightDir = normalize(vec3(0.8, 1.0, 0.6));
119
+ float diff = max(dot(normal, lightDir), 0.0);
120
+ vec3 viewDir = normalize(ro - pos);
121
+ vec3 halfDir = normalize(lightDir + viewDir);
122
+ float spec = pow(max(dot(normal, halfDir), 0.0), 32.0);
123
+
124
+ // Compute spherical coordinates for the hit point.
125
+ float rPos = length(pos);
126
+ float theta = acos(pos.y / rPos);
127
+ float phi = atan(pos.z, pos.x);
128
 
129
+ // Use the cymatic displacement to drive the wild color palette.
130
+ float sp = sphereDisplacement(pos, iTime);
131
+ float factor = sp * 5.0;
132
+ vec3 baseColor = wildPalette(factor + sin(iTime));
133
 
134
+ // Add intricate stripe textures via high–frequency sine patterns.
135
+ float stripes = sin(10.0 * phi + iTime) * sin(10.0 * theta + iTime);
136
+ baseColor *= 0.5 + 0.5 * stripes;
137
+
138
+ // Combine the base color with lighting.
139
+ color = baseColor * diff + vec3(0.2) * spec;
140
+ color = mix(color, baseColor, 0.3);
141
+ } else {
142
+ // No hit: use a subtle background gradient.
143
+ color = mix(vec3(0.0, 0.0, 0.1), vec3(0.0), uv.y + 0.5);
144
+ }
145
+ outColor = vec4(color, 1.0);
146
  }
147
  `;
148
+
149
  //────────────────────────────────────────────────────────────
150
  // Create and compile the shader program using webgl-utils.
151
  const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
152
+
153
  // Look up attribute and uniform locations.
154
  const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
155
  const resolutionLocation = gl.getUniformLocation(program, "iResolution");
156
  const mouseLocation = gl.getUniformLocation(program, "iMouse");
157
  const timeLocation = gl.getUniformLocation(program, "iTime");
158
+
159
  // Create a vertex array object (VAO) and bind it.
160
  const vao = gl.createVertexArray();
161
  gl.bindVertexArray(vao);
162
+
163
  // Create a buffer and put a full–screen quad in it.
164
  const positionBuffer = gl.createBuffer();
165
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
 
175
  ]),
176
  gl.STATIC_DRAW
177
  );
178
+
179
+ // Enable the attribute.
180
  gl.enableVertexAttribArray(positionAttributeLocation);
181
  gl.vertexAttribPointer(
182
  positionAttributeLocation,
183
+ 2, // 2 components per vertex
184
+ gl.FLOAT, // data type is float
185
  false,
186
  0,
187
  0
188
  );
189
+
190
  //────────────────────────────────────────────────────────────
191
+ // Set up mouse/touch interactions.
192
  const playpauseElem = document.querySelector(".playpause");
193
  const inputElem = document.querySelector(".divcanvas");
194
  inputElem.addEventListener("mouseover", requestFrame);
195
  inputElem.addEventListener("mouseout", cancelFrame);
196
+
197
  let mouseX = 0;
198
  let mouseY = 0;
199
  function setMousePosition(e) {
 
201
  mouseX = e.clientX - rect.left;
202
  mouseY = rect.height - (e.clientY - rect.top) - 1;
203
  }
 
204
  inputElem.addEventListener("mousemove", setMousePosition);
205
  inputElem.addEventListener("touchstart", (e) => {
206
  e.preventDefault();
 
216
  playpauseElem.classList.remove("playpausehide");
217
  cancelFrame();
218
  }, { passive: false });
219
+
220
  //────────────────────────────────────────────────────────────
221
+ // Animation loop.
222
  let requestId;
223
  function requestFrame() {
224
  if (!requestId) {
 
231
  requestId = undefined;
232
  }
233
  }
234
+
235
  let then = 0;
236
  let time = 0;
237
  function render(now) {
238
  requestId = undefined;
239
+ now *= 0.001; // Convert to seconds.
240
  const elapsedTime = Math.min(now - then, 0.1);
241
  time += elapsedTime;
242
  then = now;
243
+
 
244
  webglUtils.resizeCanvasToDisplaySize(gl.canvas);
245
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
 
 
246
  gl.useProgram(program);
247
  gl.bindVertexArray(vao);
248
 
249
+ // Set uniforms.
250
  gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
251
  gl.uniform2f(mouseLocation, mouseX, mouseY);
252
  gl.uniform1f(timeLocation, time);
253
 
 
254
  gl.drawArrays(gl.TRIANGLES, 0, 6);
 
255
  requestFrame();
256
  }
257