UNPKG

@figliolia/ripples

Version:

WebGL ripples based on the clever work of Pim Schreurs

162 lines (161 loc) 6.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Shaders = void 0; class Shaders { constructor(GL, resolution) { this.GL = GL; this.textureDelta = new Float32Array([1 / resolution, 1 / resolution]); this.dropProgram = this.compileDrop(); this.updateProgram = this.compileUpdate(); this.GL.uniform2fv(this.updateProgram.locations.delta, this.textureDelta); this.renderProgram = this.compileRender(); this.GL.uniform2fv(this.renderProgram.locations.delta, this.textureDelta); } drop(position, radius, strength) { if (this.dropProgram) { this.GL.useProgram(this.dropProgram.id); this.GL.uniform2fv(this.dropProgram.locations.center, position); this.GL.uniform1f(this.dropProgram.locations.radius, radius); this.GL.uniform1f(this.dropProgram.locations.strength, strength); } } render(perturbance) { this.GL.uniform1f(this.renderProgram.locations.perturbance, perturbance); this.GL.uniform2fv(this.renderProgram.locations.topLeft, // @ts-ignore this.renderProgram.uniforms.topLeft); this.GL.uniform2fv(this.renderProgram.locations.bottomRight, // @ts-ignore this.renderProgram.uniforms.bottomRight); this.GL.uniform2fv(this.renderProgram.locations.containerRatio, // @ts-ignore this.renderProgram.uniforms.containerRatio); this.GL.uniform1i(this.renderProgram.locations.samplerBackground, 0); this.GL.uniform1i(this.renderProgram.locations.samplerRipples, 1); } compileDrop() { return this.createProgram(Shaders.VERTEX_SHADER, Shaders.DROP_PROGRAM); } compileUpdate() { return this.createProgram(Shaders.VERTEX_SHADER, Shaders.UPDATE_PROGRAM); } compileRender() { return this.createProgram(Shaders.RENDER_BACKGROUND_PROGRAM, Shaders.RENDER_RIPPLE_PROGRAM); } createProgram(vertexSource, fragmentSource) { if (!this.GL) { throw new Error("Cannot initialize shaders without a rendering context"); } const program = { id: this.GL.createProgram() }; this.GL.attachShader(program.id, this.compileSource(this.GL.VERTEX_SHADER, vertexSource)); this.GL.attachShader(program.id, this.compileSource(this.GL.FRAGMENT_SHADER, fragmentSource)); this.GL.linkProgram(program.id); if (!this.GL.getProgramParameter(program.id, this.GL.LINK_STATUS)) { throw new Error("link error: " + this.GL.getProgramInfoLog(program.id)); } program.uniforms = {}; program.locations = {}; this.GL.useProgram(program.id); this.GL.enableVertexAttribArray(0); let match, name; const regex = /uniform (\w+) (\w+)/g; const shaderCode = vertexSource + fragmentSource; while ((match = regex.exec(shaderCode)) != null) { name = match[2]; program.locations[name] = this.GL.getUniformLocation(program.id, name); } return program; } compileSource(type, source) { if (!this.GL) { throw new Error("Cannot initialize shaders without a rendering context"); } const shader = this.GL.createShader(type); this.GL.shaderSource(shader, source); this.GL.compileShader(shader); if (!this.GL.getShaderParameter(shader, this.GL.COMPILE_STATUS)) { throw new Error("compile error: " + this.GL.getShaderInfoLog(shader)); } return shader; } } exports.Shaders = Shaders; Shaders.VERTEX_SHADER = [ "attribute vec2 vertex;", "varying vec2 coord;", "void main() {", "coord = vertex * 0.5 + 0.5;", "gl_Position = vec4(vertex, 0.0, 1.0);", "}", ].join("\n"); Shaders.DROP_PROGRAM = [ "precision highp float;", "const float PI = 3.141592653589793;", "uniform sampler2D texture;", "uniform vec2 center;", "uniform float radius;", "uniform float strength;", "varying vec2 coord;", "void main() {", "vec4 info = texture2D(texture, coord);", "float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);", "drop = 0.5 - cos(drop * PI) * 0.5;", "info.r += drop * strength;", "gl_FragColor = info;", "}", ].join("\n"); Shaders.UPDATE_PROGRAM = [ "precision highp float;", "uniform sampler2D texture;", "uniform vec2 delta;", "varying vec2 coord;", "void main() {", "vec4 info = texture2D(texture, coord);", "vec2 dx = vec2(delta.x, 0.0);", "vec2 dy = vec2(0.0, delta.y);", "float average = (", "texture2D(texture, coord - dx).r +", "texture2D(texture, coord - dy).r +", "texture2D(texture, coord + dx).r +", "texture2D(texture, coord + dy).r", ") * 0.25;", "info.g += (average - info.r) * 2.0;", "info.g *= 0.995;", "info.r += info.g;", "gl_FragColor = info;", "}", ].join("\n"); Shaders.RENDER_BACKGROUND_PROGRAM = [ "precision highp float;", "attribute vec2 vertex;", "uniform vec2 topLeft;", "uniform vec2 bottomRight;", "uniform vec2 containerRatio;", "varying vec2 ripplesCoord;", "varying vec2 backgroundCoord;", "void main() {", "backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);", "backgroundCoord.y = 1.0 - backgroundCoord.y;", "ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;", "gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);", "}", ].join("\n"); Shaders.RENDER_RIPPLE_PROGRAM = [ "precision highp float;", "uniform sampler2D samplerBackground;", "uniform sampler2D samplerRipples;", "uniform vec2 delta;", "uniform float perturbance;", "varying vec2 ripplesCoord;", "varying vec2 backgroundCoord;", "void main() {", "float height = texture2D(samplerRipples, ripplesCoord).r;", "float heightX = texture2D(samplerRipples, vec2(ripplesCoord.x + delta.x, ripplesCoord.y)).r;", "float heightY = texture2D(samplerRipples, vec2(ripplesCoord.x, ripplesCoord.y + delta.y)).r;", "vec3 dx = vec3(delta.x, heightX - height, 0.0);", "vec3 dy = vec3(0.0, heightY - height, delta.y);", "vec2 offset = -normalize(cross(dy, dx)).xz;", "float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);", "gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;", "}", ].join("\n");