151 lines
3.1 KiB
JavaScript
151 lines
3.1 KiB
JavaScript
import GLea from 'https://terabaud.github.io/glea/dist/glea.js';
|
|
|
|
// this is just for code highlighting in VSCode
|
|
// via the glsl-literal extension
|
|
const glsl = x => x;
|
|
|
|
const frag = glsl`
|
|
precision highp float;
|
|
uniform float time;
|
|
uniform float width;
|
|
uniform float height;
|
|
uniform sampler2D texture;
|
|
const float PI = 3.141592654;
|
|
const float DEG = PI / 180.0;
|
|
const int ITERS = 10000;
|
|
|
|
|
|
vec2 coords() {
|
|
float vmin = min(width, height);
|
|
return vec2((gl_FragCoord.x - width * .5) / vmin,
|
|
(gl_FragCoord.y - height * .5) / vmin);
|
|
}
|
|
|
|
vec2 rotate(vec2 p, float a) {
|
|
return vec2(p.x * cos(a) - p.y * sin(a),
|
|
p.x * sin(a) + p.y * cos(a));
|
|
}
|
|
|
|
vec2 repeat(in vec2 p, in vec2 c) {
|
|
return mod(p, c) - 0.5 * c;
|
|
}
|
|
|
|
// Distance functions by Inigo Quilez
|
|
// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
|
|
float circle(in vec2 p, in vec2 pos, float radius) {
|
|
return length((p - pos)) - radius;
|
|
}
|
|
|
|
float distanceField(vec2 p) {
|
|
float d = 10000.0;
|
|
for (int i = 0; i < 250; i++) {
|
|
vec2 point = vec2(cos(float(i)) * float(i), sin(float(i)) * float(i));
|
|
d = min(d, circle(p, point, 3.0));
|
|
}
|
|
return d;
|
|
}
|
|
|
|
// check if is prime, works for numbers 1 .. 900
|
|
bool isPrime(int n) {
|
|
if (n <= 3) {
|
|
return (n > 1) ? true : false;
|
|
}
|
|
if (mod(float(n), 2.0) == 0.0 || mod(float(n), 3.0) == 0.0) {
|
|
return false;
|
|
}
|
|
int i = 5;
|
|
for (int j = 0; j < 30; j++) {
|
|
if (i * i > n) {
|
|
return true;
|
|
}
|
|
if (mod(float(n), float(i)) == 0.0 || mod(float(n), float(i + 2)) == 0.0) {
|
|
return false;
|
|
}
|
|
i += 6;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
float primeDistanceField(vec2 p) {
|
|
float d = 10000.0;
|
|
for (int i = 0; i < 250; i++) {
|
|
if (isPrime(i)) {
|
|
vec2 point = vec2(cos(float(i)) * float(i), sin(float(i)) * float(i));
|
|
d = min(d, circle(p, point, 3.0));
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
vec3 shade(in vec2 p)
|
|
{
|
|
vec3 background = vec3(.1, .3, .7);
|
|
vec3 foreground = vec3(.7, .3, .1);
|
|
float sdf = distanceField(p);
|
|
if (sdf < 0.0) {
|
|
return foreground;
|
|
}
|
|
|
|
vec3 col = background;
|
|
|
|
// Darken around surface
|
|
col = mix(col, col*1.0-exp(-2.0 * abs(sdf)), 0.4);
|
|
|
|
// repeating lines
|
|
col *= 0.8 + 0.2*cos(1.25*sdf - time * .5);
|
|
return col;
|
|
}
|
|
|
|
|
|
void main () {
|
|
vec2 p0 = coords();
|
|
float zoom = 150.0 + sin(time * .05)*100.0;
|
|
vec2 p1 = rotate(p0 * zoom, time * DEG);
|
|
vec3 col = shade(p1);
|
|
gl_FragColor = vec4(col, 1.0);
|
|
}
|
|
`
|
|
|
|
const vert = glsl`
|
|
precision mediump float;
|
|
attribute vec2 position;
|
|
|
|
void main () {
|
|
gl_Position = vec4(position, 0, 1.0);
|
|
}
|
|
`
|
|
|
|
|
|
|
|
|
|
let texture = null;
|
|
|
|
const glea = new GLea({
|
|
shaders: [
|
|
GLea.fragmentShader(frag),
|
|
GLea.vertexShader(vert)
|
|
],
|
|
buffers: {
|
|
'position': GLea.buffer(2, [1, 1, -1, 1, 1,-1, -1,-1])
|
|
}
|
|
}).create();
|
|
|
|
function loop(time) {
|
|
const { gl } = glea;
|
|
glea.clear();
|
|
glea.uni('width', glea.width);
|
|
glea.uni('height', glea.height);
|
|
glea.uni('time', time * .005);
|
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
requestAnimationFrame(loop);
|
|
}
|
|
|
|
function setup() {
|
|
const { gl } = glea;
|
|
window.addEventListener('resize', () => {
|
|
glea.resize();
|
|
});
|
|
loop(0);
|
|
}
|
|
|
|
setup(); |