Public
Edited
Jun 5, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
canvas = DOM.canvas(width, 530)
Insert cell
regl.frame(function (context) {
regl.clear({
color: [0, 0, 0, 1]
});

draw();
})
Insert cell
draw = regl({
vert: `
precision highp float;
attribute float theta;
attribute vec2 circlePoint;
varying vec3 vColor;
uniform vec2 aspectRatio;
uniform float time;
const float PI = 3.1415926535;

void main () {
// Use lots of sines and cosines to place the circles
vec2 circleCenter = vec2(cos(theta), sin(theta))
* (0.6 + 0.2 * cos(theta * 6.0 + cos(theta * 8.0 + time)));

// Modulate the circle sizes around the circle and in time
float circleSize = 0.2 + 0.12 * cos(theta * 9.0 - time * 2.0);

vec2 xy = circleCenter + circlePoint * circleSize;

// Define some pretty colors
float th = 8.0 * theta + time * 2.0;
vColor = 0.6 + 0.4 * vec3(
cos(th),
cos(th - PI / 3.0),
cos(th - PI * 2.0 / 3.0)
);

gl_Position = vec4(xy / aspectRatio, 0, 1);
}`,

frag: `
precision highp float;
varying vec3 vColor;
uniform float alpha;
void main () {
gl_FragColor = vec4(vColor, alpha);
}`,

attributes: {
// 각 원의 정점 정의
// 모든 인스턴스에서 동일하게 사용되므로 버퍼를 사용하지 않음 (배열수 = division + 1)
circlePoint: circleInstanceGeometry,

// 각 인스턴스의 중심 위치 정의
// 각 인스턴스마다 다른 속성을 적용하기 위해 divisor: 1을 사용
theta: { buffer: instanceTheta, divisor: 1 } // 400 (theta for each circle)
},

uniforms: {
// Scale so that it fits in the view whether it's portrait or landscape:
aspectRatio: (ctx) =>
ctx.framebufferWidth > ctx.framebufferHeight
? [ctx.framebufferWidth / ctx.framebufferHeight, 1]
: [1, ctx.framebufferHeight / ctx.framebufferWidth],

time: regl.context("time"),

// Decrease opacity when there are more circles
alpha: Math.max(0, Math.min(1, (0.15 * 2000) / numCircleInstances))
},

blend: {
// Additive blending
enable: true,
func: { srcRGB: "src alpha", srcAlpha: 1, dstRGB: 1, dstAlpha: 1 },
equation: { rgb: "add", alpha: "add" }
},

// GL_LINES are in general *pretty bad*, but they're good for some things
primitive: "line strip",

depth: { enable: false },

count: numCircleDivisions + 1, // 160 + 1 (num of vertices for each circle, '+1' to close circle)

// 렌더링할 인스턴스의 개수를 지정
instances: numCircleInstances // 400 (num of circles) 그리는 객체의 갯수
})
Insert cell
Insert cell
Insert cell
// 각 circle의 geometry (vertices, 정점)
circleInstanceGeometry = Array.from(Array(numCircleDivisions + 1).keys()).map(
(i) => {
var theta = (Math.PI * 2 * i) / numCircleDivisions;
return [Math.cos(theta), Math.sin(theta)];
}
)
Insert cell
// a list of θ values we'll use in the vertex shader to place each instance
instanceTheta = Array.from(Array(numCircleInstances).keys()).map(
(i) => (i / numCircleInstances) * 2 * Math.PI
)
Insert cell
numCircleInstances = 400 // 400 (객체의 갯수)
Insert cell
numCircleDivisions = 3 // 160 (많아질 수록 원이 됨)
Insert cell
Insert cell
regl = (await import("https://cdn.skypack.dev/regl@2")).default({
canvas,
extensions: ["angle_instanced_arrays"],
attributes: { antialias: false, depth: false }
})
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