<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>发光粒子</title>
<style>
body {
display:flex;
justify-content:center;
align-items:center;
overflow:hidden;
padding:0px;
margin:0px
}
</style>
</head>
<body>
<script type="type/shader" id="vertex">
#version 300 es
layout (location=0) in vec2 point;
void main() {
gl_Position = vec4(point.x, point.y, 0.0, 1.0);
}
</script>
<script type="type/shader" id="fragment">
#version 300 es
precision highp float;
float N21(vec2 p) {
p = fract(p * vec2(233.34, 851.73));
p += dot(p, p + 23.45);
return fract(p.x * p.y);
}
vec2 N22(vec2 p) {
float n = N21(p);
return vec2(n, N21(p + n));
}
vec2 getPos(vec2 id, vec2 offset, float iTime) {
vec2 n = N22(id + offset);
float x = cos(iTime * n.x);
float y = sin(iTime * n.y);
return vec2(x, y) * 0.4 + offset;
}
float distanceToLine(vec2 p, vec2 a, vec2 b) {
vec2 pa = p - a;
vec2 ba = b - a;
float t = clamp(dot(pa, ba) / dot(ba, ba), 0., 1.);
return length(pa - t * ba);
}
float getLine(vec2 p, vec2 a, vec2 b, vec2 iResolution) {
float distance = distanceToLine(p, a, b);
float dx = 15./iResolution.y;
return smoothstep(dx, 0., distance) * smoothstep(1.2, 0.3, length(a - b));
}
float layer(vec2 st, float iTime, vec2 iResolution) {
float m = 0.;
vec2 gv = fract(st) - 0.5;
vec2 id = floor(st);
// m = gv.x > 0.48 || gv.y > 0.48 ? 1. : 0.;
// vec2 pointPos = getPos(id, vec2(0., 0.));
// m += smoothstep(0.05, 0.03, length(gv - pointPos));
float dx=15./iResolution.y;
// m += smoothstep(-dx,0., abs(gv.x)-.5);
// m += smoothstep(-dx,0., abs(gv.y)-.5);
// m += smoothstep(dx, 0., length(gv - pointPos)-0.03);
vec2 p[9];
int i = 0;
for (float x = -1.; x <= 1.; x++) {
for (float y = -1.; y <= 1.; y++) {
p[i++] = getPos(id, vec2(x, y), iTime);
}
}
for (int j = 0; j <= 8; j++) {
m += getLine(gv, p[4], p[j], iResolution);
vec2 temp = (gv - p[j]) * 20.;
m += 1./dot(temp, temp) * (sin(10. * iTime + fract(p[j].x) * 20.) * 0.5 + 0.5);
}
m += getLine(gv, p[1], p[3], iResolution);
m += getLine(gv, p[1], p[5], iResolution);
m += getLine(gv, p[3], p[7], iResolution);
m += getLine(gv, p[5], p[7], iResolution);
// m += smoothstep(0.05, 0.04, length(st - vec2(0., 0.)));
return m;
}
uniform float iTime;
uniform vec2 iResolution;
out vec4 fragColor;
void main()
{
vec2 uv = (gl_FragCoord.xy - 0.5 * iResolution.xy) / iResolution.y;
float m = 0.;
float theta = iTime * 0.1;
mat2 rot = mat2(cos(theta), -sin(theta), sin(theta), cos(theta));
vec2 gradient = uv;
uv = rot * uv;
for (float i = 0.; i < 1.0 ; i += 0.25) {
float depth = fract(i + iTime * 0.1);
m += layer(uv * mix(10., 0.5, depth) + i * 20., iTime, iResolution) * smoothstep(0., 0.2, depth) * smoothstep(1., 0.8, depth);
}
vec3 baseColor = sin(vec3(3.45, 6.56, 8.78) * iTime * 0.2) * 0.5 + 0.5;
vec3 col = (m - gradient.y) * baseColor;
// Output to screen
fragColor = vec4(col, 1.0);
}
</script>
<canvas id="cvs" width="1920" height="1080"></canvas>
<script>
class RenderLoop {
constructor(cb, fps = 0) {
this.currentFps = 0;
this.isActive = false;
this.msLastFrame = performance.now();
this.cb = cb;
this.totalTime = 0;
if (fps && typeof fps === 'number' && !Number.isNaN(fps)) {
this.msFpsLimit = 1000 / fps;
this.run = () => {
const currentTime = performance.now();
const msDt = currentTime - this.msLastFrame;
this.totalTime += msDt;
const dt = msDt / 1000;
if (msDt >= this.msFpsLimit) {
this.cb(dt, this.totalTime);
this.currentFps = Math.floor(1.0 / dt);
this.msLastFrame = currentTime;
}
if (this.isActive) window.requestAnimationFrame(this.run);
};
} else {
this.run = () => {
const currentTime = performance.now();
const dt = (currentTime - this.msLastFrame) / 1000;
this.totalTime += (currentTime - this.msLastFrame);
this.cb(dt, this.totalTime);
this.currentFps = Math.floor(1.0 / dt);
this.msLastFrame = currentTime;
if (this.isActive) window.requestAnimationFrame(this.run);
};
}
}
changeCb(cb) {
this.cb = cb;
}
start() {
this.msLastFrame = performance.now();
this.isActive = true;
window.requestAnimationFrame(this.run);
return this;
}
stop() {
this.isActive = false;
return this;
}
}
let startTime = performance.now();
const initGl = (canvas, vertexShaderSrc, fragShaderSrc) => {
const gl = canvas.getContext('webgl2');
if (!gl) {
document.write('Please change to a browser which supports WebGl 2.0~');
return;
}
// set background
gl.clearColor(0, 0, 0, 0.9);
const vertexShader = gl.createShader(gl.VERTEX_SHADER),
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc.trim());
gl.shaderSource(fragmentShader, fragShaderSrc.trim());
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(vertexShader));
return;
}
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(fragmentShader));
return;
}
let program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.log(gl.getProgramInfoLog(program));
}
gl.useProgram(program);
return {gl, program};
}
const cvs = document.querySelector('#cvs');
const vertexShaderSrc = document.querySelector('#vertex').text.trim();
const fragShaderSrc = document.querySelector('#fragment').text.trim();
const { gl, program } = initGl(cvs, vertexShaderSrc, fragShaderSrc);
gl.enable(gl.DEPTH_TEST);
let vertexBuffer = gl.createBuffer();
let indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0 ]), gl.STATIC_DRAW);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([1, 0, 2, 3]), gl.STATIC_DRAW);
const uStartIndex = gl.getUniformLocation(program, 'u_start');
gl.uniform1f(uStartIndex, performance.now());
const uResolution = gl.getUniformLocation(program, 'iResolution');
const { width, height } = cvs.getBoundingClientRect();
gl.uniform2f(uResolution, width, height);
const uTimeIndex = gl.getUniformLocation(program, 'iTime');
new RenderLoop(function(dt, tInMs) {
gl.uniform1f(uTimeIndex, tInMs / 1000.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, 0);
}).start();
console.log('render time', performance.now() - startTime);</script>
</body>
</html>