script.js

"use strict"; { const canvas = { init() { // create webGL context this.elem = document.querySelector("canvas"); const gl = this.elem.getContext("webgl", { alpha: false }); const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(vertexShader, ` uniform mat4 camProj, camView; uniform vec2 blocPosition; attribute vec3 aPosition, aNormal; varying lowp vec3 vColor; const vec3 lightDir = vec3(0, 1, -2); const vec3 lightColor = vec3(0.25, 0.15, 0.05); const vec3 ambientColor = vec3(0.1, 0.05, 0.0); void main(void) { vec4 mpos = vec4(aPosition.x + blocPosition.x, aPosition.y, aPosition.z + blocPosition.y, 1.0); vec3 normal = vec3(camView * vec4(aNormal, 0.0)); float dist = 0.1 * dot(mpos, mpos); vColor = dist + lightColor * dot(lightDir, normal) + ambientColor; gl_Position = camProj * camView * mpos; } ` ); gl.shaderSource(fragmentShader, ` precision highp float; uniform float uTime; uniform vec2 uResolution; varying vec3 vColor; void main(void) { vec2 uv = gl_FragCoord.xy / uResolution; float x = uv.x * uv.y * uTime; float grain = (mod((mod(x, 13.0) + 1.0) * (mod(x, 123.0) + 1.0), 0.01) - 0.005) * 20.0; gl_FragColor = vec4(vColor + grain, 1.0); } ` ); gl.compileShader(vertexShader); gl.compileShader(fragmentShader); this.program = gl.createProgram(); gl.attachShader(this.program, vertexShader); gl.attachShader(this.program, fragmentShader); gl.linkProgram(this.program); gl.useProgram(this.program); return gl; }, resize() { this.width = this.elem.width = this.elem.offsetWidth; this.height = this.elem.height = this.elem.offsetHeight; this.uResolution.set(this.width, this.height); camera.proj.perspective(90, this.width / this.height).load(); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); } }; //////// uniforms ///////// const Float = class { constructor(program, uName, v = 0.0) { this.value = v; this.u = gl.getUniformLocation(program, uName); } set(v) { this.value = v; gl.uniform1f(this.u, v); } }; const Vec2 = class { constructor(program, uName, x = 0.0, y = 0.0) { this.x = x; this.y = y; this.u = gl.getUniformLocation(program, uName); } set(x, y) { this.x = x; this.y = y; gl.uniform2f(this.u, x, y); } } const Mat4 = class { constructor(program, uName) { this.u = gl.getUniformLocation(program, uName); this.data = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]); } identity() { const d = this.data; d[0] = 1; d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 0; d[5] = 1; d[6] = 0; d[7] = 0; d[8] = 0; d[9] = 0; d[10] = 1; d[11] = 0; d[12] = 0; d[13] = 0; d[14] = 0; d[15] = 1; return this; } translate(x, y, z) { const d = this.data; d[12] = d[0] * x + d[4] * y + d[8] * z + d[12]; d[13] = d[1] * x + d[5] * y + d[9] * z + d[13]; d[14] = d[2] * x + d[6] * y + d[10] * z + d[14]; d[15] = d[3] * x + d[7] * y + d[11] * z + d[15]; return this; } rotateX(angle) { const d = this.data; const s = Math.sin(angle); const c = Math.cos(angle); const a10 = d[4]; const a11 = d[5]; const a12 = d[6]; const a13 = d[7]; const a20 = d[8]; const a21 = d[9]; const a22 = d[10]; const a23 = d[11]; d[4] = a10 * c + a20 * s; d[5] = a11 * c + a21 * s; d[6] = a12 * c + a22 * s; d[7] = a13 * c + a23 * s; d[8] = a10 * -s + a20 * c; d[9] = a11 * -s + a21 * c; d[10] = a12 * -s + a22 * c; d[11] = a13 * -s + a23 * c; return this; } rotateY(angle) { const d = this.data; const s = Math.sin(angle); const c = Math.cos(angle); const a00 = d[0]; const a01 = d[1]; const a02 = d[2]; const a03 = d[3]; const a20 = d[8]; const a21 = d[9]; const a22 = d[10]; const a23 = d[11]; d[0] = a00 * c + a20 * -s; d[1] = a01 * c + a21 * -s; d[2] = a02 * c + a22 * -s; d[3] = a03 * c + a23 * -s; d[8] = a00 * s + a20 * c; d[9] = a01 * s + a21 * c; d[10] = a02 * s + a22 * c; d[11] = a03 * s + a23 * c; return this; } perspective(fov, aspect) { const d = this.data; const near = 0.01; const far = 100; const top = near * Math.tan(fov * Math.PI / 360); const right = top * aspect; const left = -right; const bottom = -top; d[0] = 2 * near / (right - left); d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 0; d[5] = 2 * near / (top - bottom); d[6] = 0; d[7] = 0; d[8] = (right + left) / (right - left); d[9] = (top + bottom) / (top - bottom); d[10] = -(far + near) / (far - near); d[11] = -1; d[12] = 0; d[13] = 0; d[14] = -(2 * far * near) / (far - near); d[15] = 0; return this; } load() { gl.uniformMatrix4fv(this.u, gl.FALSE, this.data); return this; } }; //////////// mesh //////////// const geometry = () => { let iV = 0, iN = 0; const vertices = new Float32Array(89424); const normals = new Float32Array(89424); const genNormals = []; { const n = [0, 0, 1.2, 0, 0, -1.2, 0, 1.2, 0, 0, -1.2, 0, 1.2, 0, 0, -1.2, 0, 0]; for (let i = 0; i < 6; i++) { for (let j = 0; j < 6; j++) { genNormals.push(n[i * 3], n[i * 3 + 1], n[i * 3 + 2]); } } } const concat = (a1, a2, index) => { for (let i = 0, l = a2.length; i < l; i++) { a1[index++] = a2[i]; } return index; }; const fractalCube = (cx, cy, cz, scale, level) => { if (level === 0) return; cube(cx, cy, cz, scale, level); fractalCube(cx + scale * 0.45, cy - scale * 0.45, cz + scale * 0.45, scale / 2, level - 1); fractalCube(cx - scale * 0.45, cy - scale * 0.45, cz + scale * 0.45, scale / 2, level - 1); fractalCube(cx + scale * 0.45, cy - scale * 0.45, cz - scale * 0.45, scale / 2, level - 1); fractalCube(cx - scale * 0.45, cy - scale * 0.45, cz - scale * 0.45, scale / 2, level - 1); } const cube = (cx, cy, cz, scale, level) => { const h = level === 1 ? 2 : 0.7; const y = level === 1 ? -0.5 : 0; const structure = [ [-0.425, y, -0.425, 0.15, h, 0.15], [-0.425, y, 0.425, 0.15, h, 0.15], [0.425, y, -0.425, 0.15, h, 0.15], [0.425, y, 0.425, 0.15, h, 0.15], [-0.425, 0.425, 0, 0.15, 0.15, 1], [0.425, 0.425, 0, 0.15, 0.15, 1], [0, 0.425, -0.425, 0.7, 0.15, 0.15], [0, 0.425, 0.425, 0.7, 0.15, 0.15] ]; if (level > 1) { structure.push( [-0.425, -0.425, 0, 0.15, 0.15, 1], [0.425, -0.425, 0, 0.15, 0.15, 1], [0, -0.425, -0.425, 1, 0.15, 0.15], [0, -0.425, 0.425, 1, 0.15, 0.15] ); } else { structure.push( [0, -2, 0, 2, 0.01, 2] ); } for (const c of structure) { iV = concat(vertices, genVertices( cx + c[0] * scale, cy + c[1] * scale, cz + c[2] * scale, c[3] * scale / 2, c[4] * scale / 2, c[5] * scale / 2 ), iV); iN = concat(normals, genNormals, iN); } }; const genVertices = (x, y, z, l, h, w) => { return [ x-l,y-h,z+w,x+l,y-h,z+w,x+l,y+h,z+w,x-l,y-h,z+w,x+l,y+h,z+w,x-l,y+h,z+w,// Front face x-l,y-h,z-w,x-l,y+h,z-w,x+l,y+h,z-w,x-l,y-h,z-w,x+l,y+h,z-w,x+l,y-h,z-w,// Back face x-l,y+h,z-w,x-l,y+h,z+w,x+l,y+h,z+w,x-l,y+h,z-w,x+l,y+h,z+w,x+l,y+h,z-w,// Top face x-l,y-h,z-w,x+l,y-h,z-w,x+l,y-h,z+w,x-l,y-h,z-w,x+l,y-h,z+w,x-l,y-h,z+w,// Bottom face x+l,y-h,z-w,x+l,y+h,z-w,x+l,y+h,z+w,x+l,y-h,z-w,x+l,y+h,z+w,x+l,y-h,z+w,// Right face x-l,y-h,z-w,x-l,y-h,z+w,x-l,y+h,z+w,x-l,y-h,z-w,x-l,y+h,z+w,x-l,y+h,z-w // Left face ]; }; // def buffers const attribute = (program, name, data, size) => { const index = gl.getAttribLocation(program, name); gl.enableVertexAttribArray(index); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.vertexAttribPointer(index, size, gl.FLOAT, false, 0, 0); gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }; // build fractalCube(0, 0, 0, 1, 4); attribute(canvas.program, "aPosition", vertices, 3); attribute(canvas.program, "aNormal", normals, 3); return normals.length / 3; }; ////////// bloc //////////// const Block = class { constructor(x, z) { this.position = new Vec2(canvas.program, "blocPosition", x, z); this.numElements = numElements; } draw(dt) { this.position.set(this.position.x, this.position.y + dt); gl.drawArrays(gl.TRIANGLES, 0, this.numElements); if (this.position.y > 5.4) this.position.y -= 6 * 1.8; } }; ///////////// init ////////////// const gl = canvas.init(); gl.enable(gl.DEPTH_TEST); gl.enable(gl.CULL_FACE); const camera = { proj: new Mat4(canvas.program, "camProj").load(), view: new Mat4(canvas.program, "camView").load() }; let uTime = new Float(canvas.program, "uTime", 0); canvas.uResolution = new Vec2(canvas.program, "uResolution"); canvas.resize(); window.addEventListener("resize", () => canvas.resize(), false); ///////// generate structure //////// const blocs = []; const numElements = geometry(); for (let x = -2; x <= 2; x++) { for (let z = -5; z <= 0; z++) { const b = new Block(x * 1.8, 1.8 + z * 1.8); blocs.push(b); } } ///////// pointer /////////// const pointer = { isDown: false, move(e, touch) { if (touch === true) { e.preventDefault(); this.x = e.targetTouches[0].clientX; this.y = e.targetTouches[0].clientY; } else { this.x = e.clientX; this.y = e.clientY; } }, down(e, touch) { this.move(e, touch); this.isDown = true; this.xb = this.x; this.yb = this.y; canvas.elem.style.cursor = "move"; }, up(e) { this.isDown = false; canvas.elem.style.cursor = "pointer"; }, init(canvas) { this.x = canvas.width * 0.5; this.y = canvas.height * 0.5; this.cx = this.xb = this.ex = 0; this.cy = this.yb = this.ey = 0; window.addEventListener("mousemove", e => this.move(e, false), false); canvas.elem.addEventListener("touchmove", e => this.move(e, true), false); window.addEventListener("mousedown", e => this.down(e, false), false); window.addEventListener("touchstart", e => this.down(e, true), false); window.addEventListener("mouseup", e => this.up(e), false); window.addEventListener("touchend", e => this.up(e), false); }, rotate() { if (this.isDown) { this.cx += (this.x - this.xb) / 200; this.cy += (this.y - this.yb) / 200; } else { this.cx *= 0.995; this.cy *= 0.995; } this.xb = this.x; this.yb = this.y; this.ex += (this.cx - this.ex) * 0.01; this.ey += (this.cy - this.ey) * 0.01; } }; pointer.init(canvas); ///////// main loop ///////// let ry = 0, time = 0, dt = 0.0027; const run = (newTime) => { requestAnimationFrame(run); // delta time let d = 0.0002 * (newTime - time); if (d > 0.1) d = 0.1; dt += (d - dt) * 0.01; time = newTime; // clear screen gl.clearColor(1, 1, 1, 0); gl.clear(gl.COLOR_BUFFER_BIT); // move camera uTime.set(time); pointer.rotate(); camera.view .identity() .rotateY(0.75 * Math.sin(ry += dt)) .rotateX(pointer.ey + Math.PI / 8) .translate(0, -0.25 + 1 * Math.cos(ry), 0) .rotateY(pointer.ex) .load(); // draw blocs for (const bloc of blocs) bloc.draw(dt); }; // start requestAnimationFrame(run); }
Never

Be the first to comment

You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.