mirror of
https://github.com/PavelDoGreat/WebGL-Fluid-Simulation.git
synced 2025-10-03 17:31:53 +02:00
refactoring
This commit is contained in:
@@ -49,9 +49,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-family: 'iconfont';
|
font-family: 'iconfont';
|
||||||
font-size: 130%;
|
font-size: 130%;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.twitter:before {
|
.twitter:before {
|
||||||
|
255
script.js
255
script.js
@@ -4,53 +4,100 @@ const canvas = document.getElementsByTagName('canvas')[0];
|
|||||||
canvas.width = canvas.clientWidth;
|
canvas.width = canvas.clientWidth;
|
||||||
canvas.height = canvas.clientHeight;
|
canvas.height = canvas.clientHeight;
|
||||||
|
|
||||||
const params = { alpha: false, depth: false, stencil: false, antialias: false };
|
|
||||||
let gl = canvas.getContext('webgl2', params);
|
|
||||||
const isWebGL2 = !!gl;
|
|
||||||
if (!isWebGL2) {
|
|
||||||
gl = canvas.getContext('webgl', params) || canvas.getContext('experimental-webgl', params);
|
|
||||||
}
|
|
||||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
const halfFloat = gl.getExtension('OES_texture_half_float');
|
|
||||||
let support_linear_float = gl.getExtension('OES_texture_half_float_linear');
|
|
||||||
if (isWebGL2) {
|
|
||||||
gl.getExtension('EXT_color_buffer_float');
|
|
||||||
support_linear_float = gl.getExtension('OES_texture_float_linear');
|
|
||||||
}
|
|
||||||
|
|
||||||
let config = {
|
let config = {
|
||||||
TEXTURE_DOWNSAMPLE: 1,
|
TEXTURE_DOWNSAMPLE: 1,
|
||||||
DENSITY_DISSIPATION: 0.98,
|
DENSITY_DISSIPATION: 0.98,
|
||||||
VELOCITY_DISSIPATION: 0.99,
|
VELOCITY_DISSIPATION: 0.99,
|
||||||
PRESSURE_CONCENTRATION: 0.8,
|
PRESSURE_DISSIPATION: 0.8,
|
||||||
PRESSURE_ITERATIONS: 25,
|
PRESSURE_ITERATIONS: 25,
|
||||||
CURL: 30,
|
CURL: 30,
|
||||||
SPLAT_RADIUS: 0.005
|
SPLAT_RADIUS: 0.0025
|
||||||
}
|
}
|
||||||
|
|
||||||
var gui = new dat.GUI({ width: 270 });
|
let pointers = [];
|
||||||
gui.add(config, 'TEXTURE_DOWNSAMPLE', { Full: 0, Half: 1, Quarter: 2 }).name('resolution').onFinishChange(initFramebuffers);
|
let splatStack = [];
|
||||||
gui.add(config, 'DENSITY_DISSIPATION', 0.9, 1.0).name('density diffusion');
|
|
||||||
gui.add(config, 'VELOCITY_DISSIPATION', 0.9, 1.0).name('velocity diffusion');
|
|
||||||
gui.add(config, 'PRESSURE_CONCENTRATION', 0.0, 1.0).name('jelly');
|
|
||||||
gui.add(config, 'PRESSURE_ITERATIONS', 1, 60).name('iterations');
|
|
||||||
gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
|
|
||||||
gui.add(config, 'SPLAT_RADIUS', 0.0001, 0.01).name('splat radius');
|
|
||||||
|
|
||||||
let github = gui.add({ fun : function () { window.open('https://github.com/PavelDoGreat/WebGL-Fluid-Simulation'); } }, 'fun').name('Github');
|
const { gl, ext, support_linear_float } = getWebGLContext(canvas);
|
||||||
github.__li.className = 'cr function bigFont';
|
startGUI();
|
||||||
github.__li.style.borderLeft = '3px solid #8C8C8C';
|
|
||||||
let githubIcon = document.createElement('span');
|
|
||||||
github.domElement.parentElement.appendChild(githubIcon);
|
|
||||||
githubIcon.className = 'icon github';
|
|
||||||
|
|
||||||
let twitter = gui.add({ fun : function () { window.open('https://twitter.com/PavelDoGreat'); } }, 'fun').name('Twitter');
|
function getWebGLContext (canvas) {
|
||||||
twitter.__li.className = 'cr function bigFont';
|
const params = { alpha: false, depth: false, stencil: false, antialias: false };
|
||||||
twitter.__li.style.borderLeft = '3px solid #8C8C8C';
|
|
||||||
let twitterIcon = document.createElement('span');
|
let gl = canvas.getContext('webgl2', params);
|
||||||
twitter.domElement.parentElement.appendChild(twitterIcon);
|
const isWebGL2 = !!gl;
|
||||||
twitterIcon.className = 'icon twitter';
|
if (!isWebGL2)
|
||||||
|
gl = canvas.getContext('webgl', params) || canvas.getContext('experimental-webgl', params);
|
||||||
|
|
||||||
|
const halfFloat = gl.getExtension('OES_texture_half_float');
|
||||||
|
let support_linear_float = gl.getExtension('OES_texture_half_float_linear');
|
||||||
|
if (isWebGL2) {
|
||||||
|
gl.getExtension('EXT_color_buffer_float');
|
||||||
|
support_linear_float = gl.getExtension('OES_texture_float_linear');
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
const internalFormat = isWebGL2 ? gl.RGBA16F : gl.RGBA;
|
||||||
|
const internalFormatRG = isWebGL2 ? gl.RG16F : gl.RGBA;
|
||||||
|
const formatRG = isWebGL2 ? gl.RG : gl.RGBA;
|
||||||
|
const texType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES;
|
||||||
|
|
||||||
|
return {
|
||||||
|
gl,
|
||||||
|
ext: {
|
||||||
|
internalFormat,
|
||||||
|
internalFormatRG,
|
||||||
|
formatRG,
|
||||||
|
texType
|
||||||
|
},
|
||||||
|
support_linear_float
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function startGUI () {
|
||||||
|
var gui = new dat.GUI({ width: 300 });
|
||||||
|
gui.add(config, 'TEXTURE_DOWNSAMPLE', { Full: 0, Half: 1, Quarter: 2 }).name('resolution').onFinishChange(initFramebuffers);
|
||||||
|
gui.add(config, 'DENSITY_DISSIPATION', 0.9, 1.0).name('density diffusion');
|
||||||
|
gui.add(config, 'VELOCITY_DISSIPATION', 0.9, 1.0).name('velocity diffusion');
|
||||||
|
gui.add(config, 'PRESSURE_DISSIPATION', 0.0, 1.0).name('pressure diffusion');
|
||||||
|
gui.add(config, 'PRESSURE_ITERATIONS', 1, 60).name('iterations');
|
||||||
|
gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
|
||||||
|
gui.add(config, 'SPLAT_RADIUS', 0.0001, 0.01).name('splat radius');
|
||||||
|
|
||||||
|
let randomSplats = gui.add({ fun: () => {
|
||||||
|
splatStack.push(parseInt(Math.random() * 20) + 5);
|
||||||
|
}
|
||||||
|
}, 'fun').name('Random splats');
|
||||||
|
|
||||||
|
let github = gui.add({ fun : () => { window.open('https://github.com/PavelDoGreat/WebGL-Fluid-Simulation'); } }, 'fun').name('Github');
|
||||||
|
github.__li.className = 'cr function bigFont';
|
||||||
|
github.__li.style.borderLeft = '3px solid #8C8C8C';
|
||||||
|
let githubIcon = document.createElement('span');
|
||||||
|
github.domElement.parentElement.appendChild(githubIcon);
|
||||||
|
githubIcon.className = 'icon github';
|
||||||
|
|
||||||
|
let twitter = gui.add({ fun : () => { window.open('https://twitter.com/PavelDoGreat'); } }, 'fun').name('Twitter');
|
||||||
|
twitter.__li.className = 'cr function bigFont';
|
||||||
|
twitter.__li.style.borderLeft = '3px solid #8C8C8C';
|
||||||
|
let twitterIcon = document.createElement('span');
|
||||||
|
twitter.domElement.parentElement.appendChild(twitterIcon);
|
||||||
|
twitterIcon.className = 'icon twitter';
|
||||||
|
|
||||||
|
gui.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -337,23 +384,39 @@ const gradientSubtractShader = compileShader(gl.FRAGMENT_SHADER, `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const blit = (() => {
|
let textureWidth;
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
|
let textureHeight;
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), gl.STATIC_DRAW);
|
let density;
|
||||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
|
let velocity;
|
||||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW);
|
let divergence;
|
||||||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
let curl;
|
||||||
gl.enableVertexAttribArray(0);
|
let pressure;
|
||||||
|
initFramebuffers();
|
||||||
|
|
||||||
return (destination) => {
|
const clearProgram = new GLProgram(baseVertexShader, clearShader);
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, destination);
|
const displayProgram = new GLProgram(baseVertexShader, displayShader);
|
||||||
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
|
const splatProgram = new GLProgram(baseVertexShader, splatShader);
|
||||||
}
|
const advectionProgram = new GLProgram(baseVertexShader, support_linear_float ? advectionShader : advectionManualFilteringShader);
|
||||||
})();
|
const divergenceProgram = new GLProgram(baseVertexShader, divergenceShader);
|
||||||
|
const curlProgram = new GLProgram(baseVertexShader, curlShader);
|
||||||
|
const vorticityProgram = new GLProgram(baseVertexShader, vorticityShader);
|
||||||
|
const pressureProgram = new GLProgram(baseVertexShader, pressureShader);
|
||||||
|
const gradienSubtractProgram = new GLProgram(baseVertexShader, gradientSubtractShader);
|
||||||
|
|
||||||
function clear (target) {
|
function initFramebuffers () {
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, target);
|
textureWidth = gl.drawingBufferWidth >> config.TEXTURE_DOWNSAMPLE;
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
textureHeight = gl.drawingBufferHeight >> config.TEXTURE_DOWNSAMPLE;
|
||||||
|
|
||||||
|
const iFormat = ext.internalFormat;
|
||||||
|
const iFormatRG = ext.internalFormatRG;
|
||||||
|
const formatRG = ext.formatRG;
|
||||||
|
const texType = ext.texType;
|
||||||
|
|
||||||
|
density = createDoubleFBO(0, textureWidth, textureHeight, iFormat , gl.RGBA , texType, support_linear_float ? gl.LINEAR : gl.NEAREST);
|
||||||
|
velocity = createDoubleFBO(2, textureWidth, textureHeight, iFormatRG, formatRG, texType, support_linear_float ? gl.LINEAR : gl.NEAREST);
|
||||||
|
divergence = createFBO (4, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST);
|
||||||
|
curl = createFBO (5, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST);
|
||||||
|
pressure = createDoubleFBO(6, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFBO (texId, w, h, internalFormat, format, type, param) {
|
function createFBO (texId, w, h, internalFormat, format, type, param) {
|
||||||
@@ -394,66 +457,22 @@ function createDoubleFBO (texId, w, h, internalFormat, format, type, param) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let textureWidth;
|
const blit = (() => {
|
||||||
let textureHeight;
|
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
|
||||||
let density;
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), gl.STATIC_DRAW);
|
||||||
let velocity;
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
|
||||||
let divergence;
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW);
|
||||||
let curl;
|
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
|
||||||
let pressure;
|
gl.enableVertexAttribArray(0);
|
||||||
|
|
||||||
function initFramebuffers () {
|
return (destination) => {
|
||||||
textureWidth = gl.drawingBufferWidth >> config.TEXTURE_DOWNSAMPLE;
|
gl.bindFramebuffer(gl.FRAMEBUFFER, destination);
|
||||||
textureHeight = gl.drawingBufferHeight >> config.TEXTURE_DOWNSAMPLE;
|
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
|
||||||
|
}
|
||||||
const internalFormat = isWebGL2 ? gl.RGBA16F : gl.RGBA;
|
})();
|
||||||
const internalFormatRG = isWebGL2 ? gl.RG16F : gl.RGBA;
|
|
||||||
const formatRG = isWebGL2 ? gl.RG : gl.RGBA;
|
|
||||||
const texType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES;
|
|
||||||
|
|
||||||
density = createDoubleFBO(0, textureWidth, textureHeight, internalFormat , gl.RGBA , texType, support_linear_float ? gl.LINEAR : gl.NEAREST);
|
|
||||||
velocity = createDoubleFBO(2, textureWidth, textureHeight, internalFormatRG, formatRG, texType, support_linear_float ? gl.LINEAR : gl.NEAREST);
|
|
||||||
divergence = createFBO (4, textureWidth, textureHeight, internalFormatRG, formatRG, texType, gl.NEAREST);
|
|
||||||
curl = createFBO (5, textureWidth, textureHeight, internalFormatRG, formatRG, texType, gl.NEAREST);
|
|
||||||
pressure = createDoubleFBO(6, textureWidth, textureHeight, internalFormatRG, formatRG, texType, gl.NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
initFramebuffers();
|
|
||||||
|
|
||||||
const clearProgram = new GLProgram(baseVertexShader, clearShader);
|
|
||||||
const displayProgram = new GLProgram(baseVertexShader, displayShader);
|
|
||||||
const splatProgram = new GLProgram(baseVertexShader, splatShader);
|
|
||||||
const advectionProgram = new GLProgram(baseVertexShader, support_linear_float ? advectionShader : advectionManualFilteringShader);
|
|
||||||
const divergenceProgram = new GLProgram(baseVertexShader, divergenceShader);
|
|
||||||
const curlProgram = new GLProgram(baseVertexShader, curlShader);
|
|
||||||
const vorticityProgram = new GLProgram(baseVertexShader, vorticityShader);
|
|
||||||
const pressureProgram = new GLProgram(baseVertexShader, pressureShader);
|
|
||||||
const gradienSubtractProgram = new GLProgram(baseVertexShader, gradientSubtractShader);
|
|
||||||
|
|
||||||
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 = [];
|
|
||||||
pointers.push(new pointerPrototype());
|
|
||||||
|
|
||||||
for (let i = 0; i < 10; i++) {
|
|
||||||
const color = [Math.random() * 10, Math.random() * 10, Math.random() * 10];
|
|
||||||
const x = canvas.width * Math.random();
|
|
||||||
const y = canvas.height * Math.random();
|
|
||||||
const dx = 1000 * (Math.random() - 0.5);
|
|
||||||
const dy = 1000 * (Math.random() - 0.5);
|
|
||||||
splat(x, y, dx, dy, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastTime = Date.now();
|
let lastTime = Date.now();
|
||||||
|
multipleSplats(parseInt(Math.random() * 20) + 5);
|
||||||
update();
|
update();
|
||||||
|
|
||||||
function update () {
|
function update () {
|
||||||
@@ -464,6 +483,9 @@ function update () {
|
|||||||
|
|
||||||
gl.viewport(0, 0, textureWidth, textureHeight);
|
gl.viewport(0, 0, textureWidth, textureHeight);
|
||||||
|
|
||||||
|
if (splatStack.length > 0)
|
||||||
|
multipleSplats(splatStack.pop());
|
||||||
|
|
||||||
advectionProgram.bind();
|
advectionProgram.bind();
|
||||||
gl.uniform2f(advectionProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
|
gl.uniform2f(advectionProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);
|
||||||
gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]);
|
gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]);
|
||||||
@@ -507,12 +529,12 @@ function update () {
|
|||||||
blit(divergence[1]);
|
blit(divergence[1]);
|
||||||
|
|
||||||
clearProgram.bind();
|
clearProgram.bind();
|
||||||
|
|
||||||
let pressureTexId = pressure.first[2];
|
let pressureTexId = pressure.first[2];
|
||||||
gl.activeTexture(gl.TEXTURE0 + pressureTexId);
|
gl.activeTexture(gl.TEXTURE0 + pressureTexId);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]);
|
gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]);
|
||||||
gl.uniform1i(clearProgram.uniforms.uTexture, pressureTexId);
|
gl.uniform1i(clearProgram.uniforms.uTexture, pressureTexId);
|
||||||
gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE_CONCENTRATION);
|
gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE_DISSIPATION);
|
||||||
blit(pressure.second[1]);
|
blit(pressure.second[1]);
|
||||||
pressure.swap();
|
pressure.swap();
|
||||||
|
|
||||||
@@ -559,6 +581,17 @@ function splat (x, y, dx, dy, color) {
|
|||||||
density.swap();
|
density.swap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function multipleSplats (amount) {
|
||||||
|
for (let i = 0; i < amount; i++) {
|
||||||
|
const color = [Math.random() * 10, Math.random() * 10, Math.random() * 10];
|
||||||
|
const x = canvas.width * Math.random();
|
||||||
|
const y = canvas.height * Math.random();
|
||||||
|
const dx = 1000 * (Math.random() - 0.5);
|
||||||
|
const dy = 1000 * (Math.random() - 0.5);
|
||||||
|
splat(x, y, dx, dy, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function resizeCanvas () {
|
function resizeCanvas () {
|
||||||
if (canvas.width != canvas.clientWidth || canvas.height != canvas.clientHeight) {
|
if (canvas.width != canvas.clientWidth || canvas.height != canvas.clientHeight) {
|
||||||
canvas.width = canvas.clientWidth;
|
canvas.width = canvas.clientWidth;
|
||||||
|
Reference in New Issue
Block a user