Public
Edited
Jun 5, 2024
Also listed in…
regl
Insert cell
Insert cell
Insert cell
Insert cell
canvas = DOM.canvas(width, 500)
Insert cell
regl.frame(() => {
regl.clear({
depth: 1,
color: [0, 0, 0, 1]
});

drawParticles();
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
drawParticles = regl({
vert: `
precision mediump float;
attribute vec4 freq, phase;
attribute vec3 color;
uniform float time; // current time
uniform mat4 view, projection; // view (camera position), projection (camera's perspective)
varying vec3 fragColor;

void main() {
// it oscillates between -8.0 and 8.0 for each of the x, y, and z coordinates
vec3 position = 8.0 * cos(freq.xyz * time + phase.xyz);

gl_PointSize = 4.2 * (1.0 + cos(freq.w * time + phase.w));

// 3D to 2D position using camera
gl_Position = projection * view * vec4(position, 1);

fragColor = color;
}`,

frag: `
precision lowp float; // this is sufficient for this color calulation
varying vec3 fragColor;
void main() {
// gl_PointCoord is built-in variable, coordinates of the current fragment within point
// xy components range from 0.0 to 1.0, where (0.5, 0.5) is the center of the point
// length(gl_PointCoord.xy - 0.5) calculates the distance from the current fragment to the center of the point.

if (length(gl_PointCoord.xy - 0.5) > 0.5) {
discard; // discard fragment, making the point circular rather than square
}
gl_FragColor = vec4(fragColor, 1);
}`,

attributes: {
freq: {
// frequencies for the x, y, and z coordinates of the particle's motion
buffer: pointBuffer,
stride: VERT_SIZE, // 각 점(vertex) 사이의 byte 단위간격 (각점의 전체 데이터 크기, 44 = 16 + 16 +16)
offset: 0 // 각 점의 데이터 내에서 해당속성이 시작하는 위치
},
phase: {
// phase offsets -> shift the starting position of an oscillating function like a sine or cosine wave
buffer: pointBuffer,
stride: VERT_SIZE, // the total size of the data for each vertex in bytes
offset: 16 // 각 점의 데이터 내에서 해당속성이 시작하는 위치
},
color: {
buffer: pointBuffer,
stride: VERT_SIZE,
offset: 32 // 각 점의 데이터 내에서 해당속성이 시작하는 위치
}
},

uniforms: {
view: ({ tick }) => {
const t = 0.004 * tick;

//mat4.lookAt(out, eye, center, up)
return mat4.lookAt(
[],
[30 * Math.cos(t), 2.5, 30 * Math.sin(t)], // eye, position of camera -> circling around (0,0,0)
[0, 0, 0], // center, the point the camera is looking at
[0, 1, 0] // up, the up direction for the camera
);
},

projection: ({ viewportWidth, viewportHeight }) =>
mat4.perspective(
[],
Math.PI / 4,
viewportWidth / viewportHeight,
0.01,
1000
),

time: ({ tick }) => tick * 0.001
},

count: NUM_POINTS,

primitive: "points"
})
Insert cell
Insert cell
// creates a buffer in the GPU to store data
pointBuffer = regl.buffer(points)
Insert cell
points = Array(NUM_POINTS)
.fill()
.map(function () {
const color = hsv2rgb(Math.random() * 360, 0.6, 1);

return [
// freq (4 float values, each 4 bytes = 16 bytes total) -> starts at offset 0 byte
Math.random() * 10, // x
Math.random() * 10, // y
Math.random() * 10, // z
Math.random() * 10,

// phase (4 float values, each 4 bytes = 16 bytes total) -> starts at offset 16 byte
2.0 * Math.PI * Math.random(),
2.0 * Math.PI * Math.random(),
2.0 * Math.PI * Math.random(),
2.0 * Math.PI * Math.random(),

// color (3 float values, each 4 bytes = 12 bytes total) -> starts at offset 32 byte
color[0] / 255,
color[1] / 255,
color[2] / 255
];
})
Insert cell
NUM_POINTS = 1000 // 1e4 = 10000
Insert cell
Insert cell
Insert cell
FileAttachment("Screenshot 2024-06-05 at 3.02.43 PM.png").image({ width: 430 })
Insert cell
VERT_SIZE = 4 * (4 + 4 + 3)
Insert cell
Insert cell
// mat4 객체는 4x4 행렬을 생성하고, 변환하며, 다양한 연산을 수행할 수 있는 기능을 제공
// 카메라 변환에 필요한 행렬을 쉽게 생성하고 관리할 수 있음
mat4 = require("https://bundle.run/gl-mat4@1.2.0")
Insert cell
Insert cell
regl = (await import("https://cdn.skypack.dev/regl@2")).default({ canvas })
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more