mirror of
https://github.com/PavelDoGreat/WebGL-Fluid-Simulation.git
synced 2025-10-04 18:01:40 +02:00
pause, transparency, background color, take screenshot
This commit is contained in:
344
script.js
344
script.js
@@ -13,23 +13,41 @@ let config = {
|
|||||||
PRESSURE_ITERATIONS: 20,
|
PRESSURE_ITERATIONS: 20,
|
||||||
CURL: 30,
|
CURL: 30,
|
||||||
SPLAT_RADIUS: 0.5,
|
SPLAT_RADIUS: 0.5,
|
||||||
SHADING: true
|
SHADING: true,
|
||||||
|
COLORFUL: true,
|
||||||
|
PAUSED: false,
|
||||||
|
BACK_COLOR: { r: 0, g: 0, b: 0 },
|
||||||
|
TRANSPARENT: false
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointerPrototype () {
|
||||||
|
this.id = -1;
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
this.dx = 0;
|
||||||
|
this.dy = 0;
|
||||||
|
this.down = false;
|
||||||
|
this.moved = false;
|
||||||
|
this.color = [30, 0, 300];
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointers = [];
|
let pointers = [];
|
||||||
let splatStack = [];
|
let splatStack = [];
|
||||||
|
pointers.push(new pointerPrototype());
|
||||||
|
|
||||||
const { gl, ext } = getWebGLContext(canvas);
|
const { gl, ext } = getWebGLContext(canvas);
|
||||||
|
|
||||||
if (isMobile())
|
if (isMobile()) {
|
||||||
config.DYE_RESOLUTION = 256;
|
config.DYE_RESOLUTION = 256;
|
||||||
|
config.SHADING = false;
|
||||||
|
}
|
||||||
if (!ext.supportLinearFiltering)
|
if (!ext.supportLinearFiltering)
|
||||||
config.SHADING = false;
|
config.SHADING = false;
|
||||||
|
|
||||||
startGUI();
|
startGUI();
|
||||||
|
|
||||||
function getWebGLContext (canvas) {
|
function getWebGLContext (canvas) {
|
||||||
const params = { alpha: false, depth: false, stencil: false, antialias: false };
|
const params = { alpha: true, depth: false, stencil: false, antialias: false, preserveDrawingBuffer: false };
|
||||||
|
|
||||||
let gl = canvas.getContext('webgl2', params);
|
let gl = canvas.getContext('webgl2', params);
|
||||||
const isWebGL2 = !!gl;
|
const isWebGL2 = !!gl;
|
||||||
@@ -134,11 +152,18 @@ function startGUI () {
|
|||||||
gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
|
gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
|
||||||
gui.add(config, 'SPLAT_RADIUS', 0.01, 1.0).name('splat radius');
|
gui.add(config, 'SPLAT_RADIUS', 0.01, 1.0).name('splat radius');
|
||||||
gui.add(config, 'SHADING').name('shading');
|
gui.add(config, 'SHADING').name('shading');
|
||||||
|
gui.add(config, 'COLORFUL').name('colorful');
|
||||||
|
gui.add(config, 'PAUSED').name('paused').listen();
|
||||||
|
|
||||||
gui.add({ fun: () => {
|
gui.add({ fun: () => {
|
||||||
splatStack.push(parseInt(Math.random() * 20) + 5);
|
splatStack.push(parseInt(Math.random() * 20) + 5);
|
||||||
} }, 'fun').name('Random splats');
|
} }, 'fun').name('Random splats');
|
||||||
|
|
||||||
|
let captureFolder = gui.addFolder('Capture');
|
||||||
|
captureFolder.addColor(config, 'BACK_COLOR').name('background color');
|
||||||
|
captureFolder.add(config, 'TRANSPARENT').name('transparent');
|
||||||
|
captureFolder.add({ fun: CaptureScreenshot }, 'fun').name('take screenshot');
|
||||||
|
|
||||||
let github = gui.add({ fun : () => {
|
let github = gui.add({ fun : () => {
|
||||||
window.open('https://github.com/PavelDoGreat/WebGL-Fluid-Simulation');
|
window.open('https://github.com/PavelDoGreat/WebGL-Fluid-Simulation');
|
||||||
ga('send', 'event', 'link button', 'github');
|
ga('send', 'event', 'link button', 'github');
|
||||||
@@ -173,23 +198,64 @@ function startGUI () {
|
|||||||
gui.close();
|
gui.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CaptureScreenshot () {
|
||||||
|
colorProgram.bind();
|
||||||
|
gl.uniform4f(colorProgram.uniforms.color, 0, 0, 0, 1);
|
||||||
|
blit(density.write.fbo);
|
||||||
|
|
||||||
|
render(density.write.fbo);
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, density.write.fbo);
|
||||||
|
|
||||||
|
let length = dyeWidth * dyeHeight * 4;
|
||||||
|
let pixels = new Float32Array(length);
|
||||||
|
gl.readPixels(0, 0, dyeWidth, dyeHeight, gl.RGBA, gl.FLOAT, pixels);
|
||||||
|
|
||||||
|
let newPixels = new Uint8Array(length);
|
||||||
|
|
||||||
|
let id = 0;
|
||||||
|
for (let i = dyeHeight - 1; i >= 0; i--) {
|
||||||
|
for (let j = 0; j < dyeWidth; j++) {
|
||||||
|
let nid = i * dyeWidth * 4 + j * 4;
|
||||||
|
newPixels[nid + 0] = clamp01(pixels[id + 0]) * 255;
|
||||||
|
newPixels[nid + 1] = clamp01(pixels[id + 1]) * 255;
|
||||||
|
newPixels[nid + 2] = clamp01(pixels[id + 2]) * 255;
|
||||||
|
newPixels[nid + 3] = clamp01(pixels[id + 3]) * 255;
|
||||||
|
id += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let captureCanvas = document.createElement('canvas');
|
||||||
|
let ctx = captureCanvas.getContext('2d');
|
||||||
|
captureCanvas.width = dyeWidth;
|
||||||
|
captureCanvas.height = dyeHeight;
|
||||||
|
|
||||||
|
let imageData = ctx.createImageData(dyeWidth, dyeHeight);
|
||||||
|
imageData.data.set(newPixels);
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
let datauri = captureCanvas.toDataURL();
|
||||||
|
|
||||||
|
downloadURI("fluid.png", datauri);
|
||||||
|
|
||||||
|
URL.revokeObjectURL(datauri);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp01 (input) {
|
||||||
|
return Math.min(Math.max(input, 0), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadURI (filename, uri) {
|
||||||
|
let link = document.createElement("a");
|
||||||
|
link.download = filename;
|
||||||
|
link.href = uri;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
|
||||||
function isMobile () {
|
function isMobile () {
|
||||||
return /Mobi|Android/i.test(navigator.userAgent);
|
return /Mobi|Android/i.test(navigator.userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pointerPrototype () {
|
|
||||||
this.id = -1;
|
|
||||||
this.x = 0;
|
|
||||||
this.y = 0;
|
|
||||||
this.dx = 0;
|
|
||||||
this.dy = 0;
|
|
||||||
this.down = false;
|
|
||||||
this.moved = false;
|
|
||||||
this.color = [30, 0, 300];
|
|
||||||
}
|
|
||||||
|
|
||||||
pointers.push(new pointerPrototype());
|
|
||||||
|
|
||||||
class GLProgram {
|
class GLProgram {
|
||||||
constructor (vertexShader, fragmentShader) {
|
constructor (vertexShader, fragmentShader) {
|
||||||
this.uniforms = {};
|
this.uniforms = {};
|
||||||
@@ -227,7 +293,6 @@ function compileShader (type, source) {
|
|||||||
|
|
||||||
const baseVertexShader = compileShader(gl.VERTEX_SHADER, `
|
const baseVertexShader = compileShader(gl.VERTEX_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision mediump sampler2D;
|
|
||||||
|
|
||||||
attribute vec2 aPosition;
|
attribute vec2 aPosition;
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
@@ -248,10 +313,10 @@ const baseVertexShader = compileShader(gl.VERTEX_SHADER, `
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const clearShader = compileShader(gl.FRAGMENT_SHADER, `
|
const clearShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision mediump float;
|
||||||
precision mediump sampler2D;
|
precision mediump sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying highp vec2 vUv;
|
||||||
uniform sampler2D uTexture;
|
uniform sampler2D uTexture;
|
||||||
uniform float value;
|
uniform float value;
|
||||||
|
|
||||||
@@ -260,21 +325,51 @@ const clearShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const colorShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform vec4 color;
|
||||||
|
|
||||||
|
void main () {
|
||||||
|
gl_FragColor = color;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const backgroundShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
|
precision highp float;
|
||||||
|
precision highp sampler2D;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform sampler2D uTexture;
|
||||||
|
uniform float aspectRatio;
|
||||||
|
|
||||||
|
#define SCALE 25.0
|
||||||
|
|
||||||
|
void main () {
|
||||||
|
vec2 uv = floor(vUv * SCALE * vec2(aspectRatio, 1.0));
|
||||||
|
float v = mod(uv.x + uv.y, 2.0);
|
||||||
|
v = v * 0.1 + 0.8;
|
||||||
|
gl_FragColor = vec4(vec3(v), 1.0);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
const displayShader = compileShader(gl.FRAGMENT_SHADER, `
|
const displayShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision mediump sampler2D;
|
precision highp sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
uniform sampler2D uTexture;
|
uniform sampler2D uTexture;
|
||||||
|
|
||||||
void main () {
|
void main () {
|
||||||
gl_FragColor = texture2D(uTexture, vUv);
|
vec3 C = texture2D(uTexture, vUv).rgb;
|
||||||
|
float a = max(C.r, max(C.g, C.b));
|
||||||
|
gl_FragColor = vec4(C, a);
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const displayShadingShader = compileShader(gl.FRAGMENT_SHADER, `
|
const displayShadingShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision mediump sampler2D;
|
precision highp sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
varying vec2 vL;
|
varying vec2 vL;
|
||||||
@@ -300,13 +395,14 @@ const displayShadingShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
|
float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);
|
||||||
C.rgb *= diffuse;
|
C.rgb *= diffuse;
|
||||||
|
|
||||||
gl_FragColor = vec4(C, 1.0);
|
float a = max(C.r, max(C.g, C.b));
|
||||||
|
gl_FragColor = vec4(C, a);
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const splatShader = compileShader(gl.FRAGMENT_SHADER, `
|
const splatShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision mediump sampler2D;
|
precision highp sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
uniform sampler2D uTarget;
|
uniform sampler2D uTarget;
|
||||||
@@ -326,7 +422,7 @@ const splatShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
|
|
||||||
const advectionManualFilteringShader = compileShader(gl.FRAGMENT_SHADER, `
|
const advectionManualFilteringShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision mediump sampler2D;
|
precision highp sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
uniform sampler2D uVelocity;
|
uniform sampler2D uVelocity;
|
||||||
@@ -336,7 +432,7 @@ const advectionManualFilteringShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
uniform float dt;
|
uniform float dt;
|
||||||
uniform float dissipation;
|
uniform float dissipation;
|
||||||
|
|
||||||
vec4 bilerp (in sampler2D sam, in vec2 uv, in vec2 tsize) {
|
vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {
|
||||||
vec2 st = uv / tsize - 0.5;
|
vec2 st = uv / tsize - 0.5;
|
||||||
|
|
||||||
vec2 iuv = floor(st);
|
vec2 iuv = floor(st);
|
||||||
@@ -359,7 +455,7 @@ const advectionManualFilteringShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
|
|
||||||
const advectionShader = compileShader(gl.FRAGMENT_SHADER, `
|
const advectionShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision mediump sampler2D;
|
precision highp sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
uniform sampler2D uVelocity;
|
uniform sampler2D uVelocity;
|
||||||
@@ -376,44 +472,42 @@ const advectionShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const divergenceShader = compileShader(gl.FRAGMENT_SHADER, `
|
const divergenceShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision mediump float;
|
||||||
precision mediump sampler2D;
|
precision mediump sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying highp vec2 vUv;
|
||||||
varying vec2 vL;
|
varying highp vec2 vL;
|
||||||
varying vec2 vR;
|
varying highp vec2 vR;
|
||||||
varying vec2 vT;
|
varying highp vec2 vT;
|
||||||
varying vec2 vB;
|
varying highp vec2 vB;
|
||||||
uniform sampler2D uVelocity;
|
uniform sampler2D uVelocity;
|
||||||
|
|
||||||
vec2 sampleVelocity (in vec2 uv) {
|
|
||||||
vec2 multiplier = vec2(1.0, 1.0);
|
|
||||||
if (uv.x < 0.0) { uv.x = 0.0; multiplier.x = -1.0; }
|
|
||||||
if (uv.x > 1.0) { uv.x = 1.0; multiplier.x = -1.0; }
|
|
||||||
if (uv.y < 0.0) { uv.y = 0.0; multiplier.y = -1.0; }
|
|
||||||
if (uv.y > 1.0) { uv.y = 1.0; multiplier.y = -1.0; }
|
|
||||||
return multiplier * texture2D(uVelocity, uv).xy;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main () {
|
void main () {
|
||||||
float L = sampleVelocity(vL).x;
|
float L = texture2D(uVelocity, vL).x;
|
||||||
float R = sampleVelocity(vR).x;
|
float R = texture2D(uVelocity, vR).x;
|
||||||
float T = sampleVelocity(vT).y;
|
float T = texture2D(uVelocity, vT).y;
|
||||||
float B = sampleVelocity(vB).y;
|
float B = texture2D(uVelocity, vB).y;
|
||||||
|
|
||||||
|
vec2 C = texture2D(uVelocity, vUv).xy;
|
||||||
|
if (vL.x < 0.0) { L = -C.x; }
|
||||||
|
if (vR.x > 1.0) { R = -C.x; }
|
||||||
|
if (vT.y > 1.0) { T = -C.y; }
|
||||||
|
if (vB.y < 0.0) { B = -C.y; }
|
||||||
|
|
||||||
float div = 0.5 * (R - L + T - B);
|
float div = 0.5 * (R - L + T - B);
|
||||||
gl_FragColor = vec4(div, 0.0, 0.0, 1.0);
|
gl_FragColor = vec4(div, 0.0, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const curlShader = compileShader(gl.FRAGMENT_SHADER, `
|
const curlShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision mediump float;
|
||||||
precision mediump sampler2D;
|
precision mediump sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying highp vec2 vUv;
|
||||||
varying vec2 vL;
|
varying highp vec2 vL;
|
||||||
varying vec2 vR;
|
varying highp vec2 vR;
|
||||||
varying vec2 vT;
|
varying highp vec2 vT;
|
||||||
varying vec2 vB;
|
varying highp vec2 vB;
|
||||||
uniform sampler2D uVelocity;
|
uniform sampler2D uVelocity;
|
||||||
|
|
||||||
void main () {
|
void main () {
|
||||||
@@ -428,7 +522,7 @@ const curlShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
|
|
||||||
const vorticityShader = compileShader(gl.FRAGMENT_SHADER, `
|
const vorticityShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision mediump sampler2D;
|
precision highp sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
varying vec2 vL;
|
varying vec2 vL;
|
||||||
@@ -458,20 +552,22 @@ const vorticityShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const pressureShader = compileShader(gl.FRAGMENT_SHADER, `
|
const pressureShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision mediump float;
|
||||||
precision mediump sampler2D;
|
precision mediump sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying highp vec2 vUv;
|
||||||
varying vec2 vL;
|
varying highp vec2 vL;
|
||||||
varying vec2 vR;
|
varying highp vec2 vR;
|
||||||
varying vec2 vT;
|
varying highp vec2 vT;
|
||||||
varying vec2 vB;
|
varying highp vec2 vB;
|
||||||
uniform sampler2D uPressure;
|
uniform sampler2D uPressure;
|
||||||
uniform sampler2D uDivergence;
|
uniform sampler2D uDivergence;
|
||||||
|
|
||||||
vec2 boundary (in vec2 uv) {
|
vec2 boundary (vec2 uv) {
|
||||||
uv = min(max(uv, 0.0), 1.0);
|
|
||||||
return uv;
|
return uv;
|
||||||
|
// uncomment if you use wrap or repeat texture mode
|
||||||
|
// uv = min(max(uv, 0.0), 1.0);
|
||||||
|
// return uv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main () {
|
void main () {
|
||||||
@@ -487,20 +583,21 @@ const pressureShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const gradientSubtractShader = compileShader(gl.FRAGMENT_SHADER, `
|
const gradientSubtractShader = compileShader(gl.FRAGMENT_SHADER, `
|
||||||
precision highp float;
|
precision mediump float;
|
||||||
precision mediump sampler2D;
|
precision mediump sampler2D;
|
||||||
|
|
||||||
varying vec2 vUv;
|
varying highp vec2 vUv;
|
||||||
varying vec2 vL;
|
varying highp vec2 vL;
|
||||||
varying vec2 vR;
|
varying highp vec2 vR;
|
||||||
varying vec2 vT;
|
varying highp vec2 vT;
|
||||||
varying vec2 vB;
|
varying highp vec2 vB;
|
||||||
uniform sampler2D uPressure;
|
uniform sampler2D uPressure;
|
||||||
uniform sampler2D uVelocity;
|
uniform sampler2D uVelocity;
|
||||||
|
|
||||||
vec2 boundary (in vec2 uv) {
|
vec2 boundary (vec2 uv) {
|
||||||
uv = min(max(uv, 0.0), 1.0);
|
|
||||||
return uv;
|
return uv;
|
||||||
|
// uv = min(max(uv, 0.0), 1.0);
|
||||||
|
// return uv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main () {
|
void main () {
|
||||||
@@ -539,6 +636,8 @@ let curl;
|
|||||||
let pressure;
|
let pressure;
|
||||||
|
|
||||||
const clearProgram = new GLProgram(baseVertexShader, clearShader);
|
const clearProgram = new GLProgram(baseVertexShader, clearShader);
|
||||||
|
const colorProgram = new GLProgram(baseVertexShader, colorShader);
|
||||||
|
const backgroundProgram = new GLProgram(baseVertexShader, backgroundShader);
|
||||||
const displayProgram = new GLProgram(baseVertexShader, displayShader);
|
const displayProgram = new GLProgram(baseVertexShader, displayShader);
|
||||||
const displayShadingProgram = new GLProgram(baseVertexShader, displayShadingShader);
|
const displayShadingProgram = new GLProgram(baseVertexShader, displayShadingShader);
|
||||||
const splatProgram = new GLProgram(baseVertexShader, splatShader);
|
const splatProgram = new GLProgram(baseVertexShader, splatShader);
|
||||||
@@ -575,8 +674,8 @@ function getResolution (resolution) {
|
|||||||
if (aspectRatio < 1)
|
if (aspectRatio < 1)
|
||||||
aspectRatio = 1.0 / aspectRatio;
|
aspectRatio = 1.0 / aspectRatio;
|
||||||
|
|
||||||
let max = resolution * aspectRatio;
|
let max = Math.round(resolution * aspectRatio);
|
||||||
let min = resolution;
|
let min = Math.round(resolution);
|
||||||
|
|
||||||
if (gl.drawingBufferWidth > gl.drawingBufferHeight)
|
if (gl.drawingBufferWidth > gl.drawingBufferHeight)
|
||||||
return { width: max, height: min };
|
return { width: max, height: min };
|
||||||
@@ -629,13 +728,16 @@ function createDoubleFBO (texId, w, h, internalFormat, format, type, param) {
|
|||||||
initFramebuffers();
|
initFramebuffers();
|
||||||
multipleSplats(parseInt(Math.random() * 20) + 5);
|
multipleSplats(parseInt(Math.random() * 20) + 5);
|
||||||
|
|
||||||
|
let lastColorChangeTime = Date.now();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
function update () {
|
function update () {
|
||||||
resizeCanvas();
|
resizeCanvas();
|
||||||
input();
|
input();
|
||||||
step(0.016);
|
if (!config.PAUSED)
|
||||||
render();
|
step(0.016);
|
||||||
|
render(null);
|
||||||
requestAnimationFrame(update);
|
requestAnimationFrame(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,15 +746,28 @@ function input () {
|
|||||||
multipleSplats(splatStack.pop());
|
multipleSplats(splatStack.pop());
|
||||||
|
|
||||||
for (let i = 0; i < pointers.length; i++) {
|
for (let i = 0; i < pointers.length; i++) {
|
||||||
const pointer = pointers[i];
|
const p = pointers[i];
|
||||||
if (pointer.moved) {
|
if (p.moved) {
|
||||||
splat(pointer.x, pointer.y, pointer.dx, pointer.dy, pointer.color);
|
splat(p.x, p.y, p.dx, p.dy, p.color);
|
||||||
pointer.moved = false;
|
p.moved = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.COLORFUL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lastColorChangeTime + 100 < Date.now())
|
||||||
|
{
|
||||||
|
lastColorChangeTime = Date.now();
|
||||||
|
for (let i = 0; i < pointers.length; i++) {
|
||||||
|
const p = pointers[i];
|
||||||
|
p.color = generateColor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function step (dt) {
|
function step (dt) {
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
gl.viewport(0, 0, simWidth, simHeight);
|
gl.viewport(0, 0, simWidth, simHeight);
|
||||||
|
|
||||||
curlProgram.bind();
|
curlProgram.bind();
|
||||||
@@ -714,8 +829,9 @@ function step (dt) {
|
|||||||
velocity.swap();
|
velocity.swap();
|
||||||
|
|
||||||
gl.viewport(0, 0, dyeWidth, dyeHeight);
|
gl.viewport(0, 0, dyeWidth, dyeHeight);
|
||||||
|
|
||||||
if (!ext.supportLinearFiltering)
|
if (!ext.supportLinearFiltering)
|
||||||
gl.uniform2f(advectionProgram.uniforms.dyeTexelSize, 1.0 / dyeWidth, 1.0 / dyeWidth);
|
gl.uniform2f(advectionProgram.uniforms.dyeTexelSize, 1.0 / dyeWidth, 1.0 / dyeHeight);
|
||||||
gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.read.texId);
|
gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.read.texId);
|
||||||
gl.uniform1i(advectionProgram.uniforms.uSource, density.read.texId);
|
gl.uniform1i(advectionProgram.uniforms.uSource, density.read.texId);
|
||||||
gl.uniform1f(advectionProgram.uniforms.dissipation, config.DENSITY_DISSIPATION);
|
gl.uniform1f(advectionProgram.uniforms.dissipation, config.DENSITY_DISSIPATION);
|
||||||
@@ -723,12 +839,33 @@ function step (dt) {
|
|||||||
density.swap();
|
density.swap();
|
||||||
}
|
}
|
||||||
|
|
||||||
function render () {
|
function render (target) {
|
||||||
let width = gl.drawingBufferWidth;
|
if (target == null || !config.TRANSPARENT) {
|
||||||
let height = gl.drawingBufferHeight;
|
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = target == null ? gl.drawingBufferWidth : dyeWidth;
|
||||||
|
let height = target == null ? gl.drawingBufferHeight : dyeHeight;
|
||||||
|
|
||||||
gl.viewport(0, 0, width, height);
|
gl.viewport(0, 0, width, height);
|
||||||
|
|
||||||
|
if (!config.TRANSPARENT) {
|
||||||
|
colorProgram.bind();
|
||||||
|
let bc = config.BACK_COLOR;
|
||||||
|
gl.uniform4f(colorProgram.uniforms.color, bc.r / 255, bc.g / 255, bc.b / 255, 1);
|
||||||
|
blit(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == null && config.TRANSPARENT) {
|
||||||
|
backgroundProgram.bind();
|
||||||
|
gl.uniform1f(backgroundProgram.uniforms.aspectRatio, canvas.width / canvas.height);
|
||||||
|
blit(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (config.SHADING) {
|
if (config.SHADING) {
|
||||||
displayShadingProgram.bind();
|
displayShadingProgram.bind();
|
||||||
gl.uniform2f(displayShadingProgram.uniforms.texelSize, 1.0 / width, 1.0 / height);
|
gl.uniform2f(displayShadingProgram.uniforms.texelSize, 1.0 / width, 1.0 / height);
|
||||||
@@ -739,7 +876,7 @@ function render () {
|
|||||||
gl.uniform1i(displayProgram.uniforms.uTexture, density.read.texId);
|
gl.uniform1i(displayProgram.uniforms.uTexture, density.read.texId);
|
||||||
}
|
}
|
||||||
|
|
||||||
blit(null);
|
blit(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
function splat (x, y, dx, dy, color) {
|
function splat (x, y, dx, dy, color) {
|
||||||
@@ -782,7 +919,7 @@ function resizeCanvas () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.addEventListener('mousemove', (e) => {
|
canvas.addEventListener('mousemove', e => {
|
||||||
pointers[0].moved = pointers[0].down;
|
pointers[0].moved = pointers[0].down;
|
||||||
pointers[0].dx = (e.offsetX - pointers[0].x) * 5.0;
|
pointers[0].dx = (e.offsetX - pointers[0].x) * 5.0;
|
||||||
pointers[0].dy = (e.offsetY - pointers[0].y) * 5.0;
|
pointers[0].dy = (e.offsetY - pointers[0].y) * 5.0;
|
||||||
@@ -790,7 +927,7 @@ canvas.addEventListener('mousemove', (e) => {
|
|||||||
pointers[0].y = e.offsetY;
|
pointers[0].y = e.offsetY;
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener('touchmove', (e) => {
|
canvas.addEventListener('touchmove', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const touches = e.targetTouches;
|
const touches = e.targetTouches;
|
||||||
for (let i = 0; i < touches.length; i++) {
|
for (let i = 0; i < touches.length; i++) {
|
||||||
@@ -808,7 +945,7 @@ canvas.addEventListener('mousedown', () => {
|
|||||||
pointers[0].color = generateColor();
|
pointers[0].color = generateColor();
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener('touchstart', (e) => {
|
canvas.addEventListener('touchstart', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const touches = e.targetTouches;
|
const touches = e.targetTouches;
|
||||||
for (let i = 0; i < touches.length; i++) {
|
for (let i = 0; i < touches.length; i++) {
|
||||||
@@ -827,7 +964,7 @@ window.addEventListener('mouseup', () => {
|
|||||||
pointers[0].down = false;
|
pointers[0].down = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('touchend', (e) => {
|
window.addEventListener('touchend', e => {
|
||||||
const touches = e.changedTouches;
|
const touches = e.changedTouches;
|
||||||
for (let i = 0; i < touches.length; i++)
|
for (let i = 0; i < touches.length; i++)
|
||||||
for (let j = 0; j < pointers.length; j++)
|
for (let j = 0; j < pointers.length; j++)
|
||||||
@@ -835,10 +972,39 @@ window.addEventListener('touchend', (e) => {
|
|||||||
pointers[j].down = false;
|
pointers[j].down = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keydown', e => {
|
||||||
|
if (e.key === 'p')
|
||||||
|
config.PAUSED = !config.PAUSED;
|
||||||
|
});
|
||||||
|
|
||||||
function generateColor () {
|
function generateColor () {
|
||||||
|
let c = HSVtoRGB(Math.random(), 1.0, 1.0);
|
||||||
|
c.r *= 0.15;
|
||||||
|
c.g *= 0.15;
|
||||||
|
c.b *= 0.15;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function HSVtoRGB (h, s, v) {
|
||||||
|
let r, g, b, i, f, p, q, t;
|
||||||
|
i = Math.floor(h * 6);
|
||||||
|
f = h * 6 - i;
|
||||||
|
p = v * (1 - s);
|
||||||
|
q = v * (1 - f * s);
|
||||||
|
t = v * (1 - (1 - f) * s);
|
||||||
|
|
||||||
|
switch (i % 6) {
|
||||||
|
case 0: r = v, g = t, b = p; break;
|
||||||
|
case 1: r = q, g = v, b = p; break;
|
||||||
|
case 2: r = p, g = v, b = t; break;
|
||||||
|
case 3: r = p, g = q, b = v; break;
|
||||||
|
case 4: r = t, g = p, b = v; break;
|
||||||
|
case 5: r = v, g = p, b = q; break;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
r: Math.random() * 0.15 + 0.05,
|
r,
|
||||||
g: Math.random() * 0.15 + 0.05,
|
g,
|
||||||
b: Math.random() * 0.15 + 0.05
|
b
|
||||||
};
|
};
|
||||||
}
|
}
|
Reference in New Issue
Block a user