├── .gitignore ├── docs ├── fluids.gif ├── image.png ├── index.html ├── wglut.js └── StableFluids.js ├── src ├── shader │ ├── ps_color.glsl │ ├── ps_default.glsl │ ├── vs_default.glsl │ ├── vs_default_flip.glsl │ ├── ps_advect.glsl │ ├── ps_force.glsl │ ├── ps_projsetup.glsl │ ├── ps_projfinish.glsl │ ├── ps_jacobi1d.glsl │ ├── ps_jacobi2d.glsl │ └── ps_fluid.glsl └── script │ ├── ShaderLibs.ts │ └── StableFluids.ts ├── README.md ├── package.json ├── gulpfile.js ├── tsconfig.json └── dist └── stablefluids.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | dist/index.html 4 | dist/image.png -------------------------------------------------------------------------------- /docs/fluids.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcyemi/webgl2-stablefluids/HEAD/docs/fluids.gif -------------------------------------------------------------------------------- /docs/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcyemi/webgl2-stablefluids/HEAD/docs/image.png -------------------------------------------------------------------------------- /src/shader/ps_color.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | in vec2 vUV; 4 | uniform vec4 uColor; 5 | 6 | out vec4 fragColor; 7 | void main(){ 8 | fragColor = uColor; 9 | } -------------------------------------------------------------------------------- /src/shader/ps_default.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | in vec2 vUV; 4 | uniform sampler2D uSampler; 5 | 6 | out vec4 fragColor; 7 | void main(){ 8 | fragColor = texture(uSampler,vUV); 9 | } -------------------------------------------------------------------------------- /src/shader/vs_default.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision mediump float; 3 | layout(location = 0) in vec2 aPosition; 4 | layout(location = 1) in vec2 aUV; 5 | 6 | out vec2 vUV; 7 | 8 | void main(){ 9 | gl_Position = vec4(aPosition *2.0-1.0,0,1); 10 | 11 | vUV = aUV; 12 | } -------------------------------------------------------------------------------- /src/shader/vs_default_flip.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision mediump float; 3 | layout(location = 0) in vec2 aPosition; 4 | layout(location = 1) in vec2 aUV; 5 | 6 | out vec2 vUV; 7 | 8 | void main(){ 9 | gl_Position = vec4(aPosition *2.0-1.0,0,1); 10 | 11 | vUV = aUV; 12 | vUV.y = 1.0 - vUV.y; 13 | } -------------------------------------------------------------------------------- /src/shader/ps_advect.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | in vec2 vUV; 4 | 5 | uniform sampler2D uSampler; 6 | uniform float uDeltaTime; 7 | 8 | out vec2 fragmentColor; 9 | void main(){ 10 | vec4 col = texture(uSampler,vUV); 11 | ivec2 texsize = textureSize(uSampler,1); 12 | vec2 duv = col.xy * vec2(texsize.y/texsize.x,1.0) * uDeltaTime; 13 | fragmentColor = texture(uSampler,vUV - duv).xy; 14 | } -------------------------------------------------------------------------------- /src/shader/ps_force.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | in vec2 vUV; 4 | 5 | uniform sampler2D uSampler; //W_in 2D 6 | uniform float uDeltaTime; 7 | 8 | //map to [0,1.0] 9 | uniform vec2 uForceOrigin; 10 | uniform vec2 uForceVector; 11 | uniform float uForceExponent; 12 | 13 | out vec2 fragColor; //W_out 2D 14 | void main(){ 15 | vec4 col = texture(uSampler,vUV); 16 | ivec2 texsize = textureSize(uSampler,0); 17 | 18 | vec2 pos = vUV; 19 | float amp = exp(-uForceExponent * distance(uForceOrigin,pos)); 20 | fragColor = col.xy + uForceVector * amp; 21 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webgl2-stablefluids 2 | 3 | Jos Stam's "Stable Fluids" implementation on WebGL2. 4 | 5 | Migrate from [keijiro/StableFluids](https://github.com/keijiro/StableFluids). 6 | 7 | [Live Demo](https://zcyemi.github.io/webgl2-stablefluids/) 8 | 9 | ![gif](https://github.com/soyemi/webgl2-stablefluids/blob/master/docs/fluids.gif) 10 | 11 | 12 | ## Quick Start 13 | ``` 14 | //clone this repo 15 | git clone git@github.com:soyemi/webgl2-stablefluids.git 16 | npm install 17 | 18 | //run demo 19 | npm run start 20 | 21 | //build source 22 | npm run build 23 | ``` 24 | 25 | ## License CC0 26 | -------------------------------------------------------------------------------- /src/shader/ps_projsetup.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | in vec2 vUV; 4 | 5 | uniform sampler2D uSampler; //W_in 2D 6 | 7 | out vec2 fragColor; //DivW_out 1D 8 | void main(){ 9 | ivec2 texsize = textureSize(uSampler,0); 10 | float uoff = 1.0/ float(texsize.x); 11 | float voff = 1.0 / float(texsize.y); 12 | 13 | 14 | float x1 = texture(uSampler,vUV + vec2(uoff,0)).x; 15 | float x2 = texture(uSampler,vUV - vec2(uoff,0)).x; 16 | 17 | float y1 = texture(uSampler,vUV + vec2(0,voff)).y; 18 | float y2 = texture(uSampler,vUV - vec2(0,voff)).y; 19 | 20 | float v = (x1 - x2 + y1 - y2) / (2.0 * uoff); 21 | fragColor = vec2(v,v); 22 | } -------------------------------------------------------------------------------- /src/shader/ps_projfinish.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision mediump float; 3 | in vec2 vUV; 4 | 5 | uniform sampler2D uSampler; //1D P_in 6 | uniform sampler2D uSampler1; //2D W_in 7 | 8 | out vec2 fragColor; 9 | void main(){ 10 | ivec2 texsize = textureSize(uSampler,0); 11 | float uoff = 1.0 / float(texsize.x); 12 | float voff = 1.0 / float(texsize.y); 13 | 14 | float p1 = texture(uSampler,vUV - vec2(uoff,0)).x; 15 | float p2 = texture(uSampler,vUV + vec2(uoff,0)).x; 16 | float p3 = texture(uSampler,vUV - vec2(0,voff)).x; 17 | float p4 = texture(uSampler,vUV + vec2(0,voff)).x; 18 | 19 | vec2 u = texture(uSampler1,vUV).xy - vec2(p2-p1,p4-p3) /(2.0 * voff); 20 | 21 | fragColor = u; 22 | } -------------------------------------------------------------------------------- /src/shader/ps_jacobi1d.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | in vec2 vUV; 4 | uniform sampler2D uSampler; //1D X1 5 | uniform sampler2D uSampler1;//1D B1 6 | 7 | uniform float uAlpha; 8 | uniform float uBeta; 9 | 10 | out float fragColor; 11 | 12 | void main(){ 13 | ivec2 texsize = textureSize(uSampler,0); 14 | float uoff = 1.0 / float(texsize.x); 15 | float voff = 1.0 / float(texsize.y); 16 | 17 | float x1 = texture(uSampler,vUV - vec2(uoff,0)).x; 18 | float x2 = texture(uSampler,vUV + vec2(uoff,0)).x; 19 | float y1 = texture(uSampler,vUV - vec2(0,voff)).x; 20 | float y2 = texture(uSampler,vUV + vec2(0,voff)).x; 21 | 22 | float b1 = texture(uSampler1,vUV).x; 23 | 24 | fragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta; 25 | } -------------------------------------------------------------------------------- /src/shader/ps_jacobi2d.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision mediump float; 3 | in vec2 vUV; 4 | 5 | uniform sampler2D uSampler; //2D X2 6 | uniform sampler2D uSampler1;//2D B2 7 | 8 | uniform float uAlpha; 9 | uniform float uBeta; 10 | 11 | out vec2 fragColor; 12 | 13 | void main(){ 14 | ivec2 texsize = textureSize(uSampler,0); 15 | float uoff = 1.0/ float(texsize.x); 16 | float voff = 1.0 / float(texsize.y); 17 | 18 | vec2 x1 = texture(uSampler,vUV - vec2(uoff,0)).xy; 19 | vec2 x2 = texture(uSampler,vUV + vec2(uoff,0)).xy; 20 | vec2 y1 = texture(uSampler,vUV - vec2(0,voff)).xy; 21 | vec2 y2 = texture(uSampler,vUV + vec2(0,voff)).xy; 22 | 23 | vec2 b1 = texture(uSampler1,vUV).xy; 24 | 25 | fragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta; 26 | } -------------------------------------------------------------------------------- /src/shader/ps_fluid.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision mediump float; 3 | in vec2 vUV; 4 | uniform sampler2D uSampler; //Color Texture RGB 5 | uniform sampler2D uSampler1; //Velocity texture RG32F 6 | 7 | uniform float uDeltaTime; 8 | 9 | uniform float uForceExponent; 10 | uniform vec2 uForceOrigin; 11 | uniform float uTime; 12 | 13 | 14 | out vec4 fragColor; 15 | void main(){ 16 | //color advection 17 | vec2 delta = texture(uSampler1,vUV).xy * uDeltaTime; 18 | vec3 color =texture(uSampler,vUV - delta).xyz; 19 | 20 | //color added 21 | // vec3 dye = clamp(sin(uTime * vec3(2.72, 5.12, 4.98))+0.5,0.0,1.0); 22 | // float amp = exp(-uForceExponent * distance(uForceOrigin, vUV)); 23 | // color = mix(color, dye, clamp(amp * 100.0,0.0,1.0)); 24 | 25 | fragColor = vec4(color,1.0); 26 | } -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGL2-StableFluids 4 | 5 | 6 | 7 | 8 | 19 | 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webgl2-stablefluids", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "./dist/StableFluids.js", 6 | "scripts": { 7 | "start": "gulp build && gulp run", 8 | "build": "gulp build" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/soyemi/webgl2-stablefluids.git" 13 | }, 14 | "author": "", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/soyemi/webgl2-stablefluids/issues" 18 | }, 19 | "homepage": "https://github.com/soyemi/webgl2-stablefluids#readme", 20 | "devDependencies": { 21 | "@types/node": "^10.5.2", 22 | "@types/webgl2": "0.0.4", 23 | "browser-sync": "^2.24.5", 24 | "gulp": "^3.9.1", 25 | "gulp-glsl": "^1.2.4", 26 | "gulp-rename": "^1.4.0", 27 | "gulp-run": "^1.7.1", 28 | "gulp-sequence": "^1.0.0", 29 | "gulp-typescript": "^5.0.0-alpha.3", 30 | "through2": "^2.0.3", 31 | "typescript": "^2.9.2" 32 | }, 33 | "dependencies": { 34 | "wglut": "0.0.4" 35 | }, 36 | "files": [ 37 | "./dist" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const gulpts = require('gulp-typescript'); 3 | const browersync = require('browser-sync'); 4 | const through = require('through2'); 5 | const path = require('path'); 6 | const fs = require('fs'); 7 | const util = require('util'); 8 | const gulprun = require('gulp-run'); 9 | const rename = require('gulp-rename'); 10 | 11 | gulp.task("build",()=>{ 12 | BuildScript(); 13 | BuildShader(); 14 | }); 15 | 16 | 17 | gulp.task("run",()=>{ 18 | gulp.src('./dist/StableFluids.js').pipe(gulp.dest('./docs/')); 19 | gulp.src('./node_modules/wglut/dist/wglut.umd.js').pipe(rename('wglut.js')).pipe(gulp.dest('./docs/')); 20 | 21 | browersync.init({ 22 | server: { 23 | baseDir: './docs/' 24 | }, 25 | port: 6633, 26 | files: ['./docs/*.js', './docs/*.html'] 27 | }) 28 | }); 29 | 30 | 31 | function BuildScript(){ 32 | console.log('[sync script]'); 33 | gulprun('tsc').exec(); 34 | } 35 | 36 | function BuildShader(){ 37 | console.log('[sync shader]'); 38 | gulp.src('./src/shader/*.glsl').pipe(gulpGLSLMerge('/src/script/ShaderLibs.ts')); 39 | } 40 | 41 | function gulpGLSLMerge(targetFile) { 42 | var tarFile = targetFile; 43 | return through.obj(function (file, enc, cb) { 44 | if (file.isNull()) { 45 | cb(null, file); 46 | return; 47 | } 48 | if (file.isBuffer()) { 49 | var fname = path.basename(file.path); 50 | let contents = file.contents; 51 | let tspath = path.join(process.cwd(), targetFile); 52 | let tscontent = fs.readFileSync(tspath, { encoding: 'utf8' }); 53 | tscontent = MergeShaderLibTs(fname, contents.toString(), tscontent); 54 | fs.writeFileSync(tspath, tscontent, { encoding: 'utf8' }); 55 | cb(null, file); 56 | return; 57 | } 58 | if (file.isStream()) { 59 | console.log("stream: skip"); 60 | cb(null, file); 61 | return; 62 | } 63 | }); 64 | } 65 | function MergeShaderLibTs(fname, source, ts) { 66 | fname = fname.toString(); 67 | if (fname.endsWith('.glsl')) fname = fname.substr(0, fname.length - 5); 68 | fname = 'GLSL_' + fname.toUpperCase(); 69 | let srcSplit = source.split('\n'); 70 | 71 | for (var i = 0; i < srcSplit.length; i++) { 72 | srcSplit[i] = srcSplit[i].trim(); 73 | } 74 | 75 | let mergetdGlsl = srcSplit.join('\\n'); 76 | mergetdGlsl = '\'' + mergetdGlsl + '\';'; 77 | 78 | let tsSplit = ts.split('\n'); 79 | let shLines = {}; 80 | tsSplit.forEach(line => { 81 | if (line.includes('export const')) { 82 | let match = line.match(/export const\s+([\w\d_]+)\s*=(.+)/); 83 | if (match) { 84 | shLines[match[1]] = match[2]; 85 | } 86 | } 87 | }); 88 | shLines[fname] = mergetdGlsl; 89 | 90 | let result = '';// 'namespace cis {\n'; 91 | for (var sh in shLines) { 92 | result += util.format('export const %s = %s \n', sh, shLines[sh]); 93 | } 94 | //result+='}'; 95 | return result; 96 | } -------------------------------------------------------------------------------- /src/script/ShaderLibs.ts: -------------------------------------------------------------------------------- 1 | export const GLSL_PS_ADVECT = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler;\nuniform float uDeltaTime;\n\nout vec2 fragmentColor;\nvoid main(){\nvec4 col = texture(uSampler,vUV);\nivec2 texsize = textureSize(uSampler,1);\nvec2 duv = col.xy * vec2(texsize.y/texsize.x,1.0) * uDeltaTime;\nfragmentColor = texture(uSampler,vUV - duv).xy;\n}'; 2 | export const GLSL_PS_DEFAULT = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform sampler2D uSampler;\n\nout vec4 fragColor;\nvoid main(){\nfragColor = texture(uSampler,vUV);\n}'; 3 | export const GLSL_PS_FORCE = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //W_in 2D\nuniform float uDeltaTime;\n\n//map to [0,1.0]\nuniform vec2 uForceOrigin;\nuniform vec2 uForceVector;\nuniform float uForceExponent;\n\nout vec2 fragColor; //W_out 2D\nvoid main(){\nvec4 col = texture(uSampler,vUV);\nivec2 texsize = textureSize(uSampler,0);\n\nvec2 pos = vUV;\nfloat amp = exp(-uForceExponent * distance(uForceOrigin,pos));\nfragColor = col.xy + uForceVector * amp;\n}'; 4 | export const GLSL_PS_JACOBI1D = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform sampler2D uSampler; //1D X1\nuniform sampler2D uSampler1;//1D B1\n\nuniform float uAlpha;\nuniform float uBeta;\n\nout float fragColor;\n\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0 / float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nfloat x1 = texture(uSampler,vUV - vec2(uoff,0)).x;\nfloat x2 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat y1 = texture(uSampler,vUV - vec2(0,voff)).x;\nfloat y2 = texture(uSampler,vUV + vec2(0,voff)).x;\n\nfloat b1 = texture(uSampler1,vUV).x;\n\nfragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta;\n}'; 5 | export const GLSL_PS_JACOBI2D = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //2D X2\nuniform sampler2D uSampler1;//2D B2\n\nuniform float uAlpha;\nuniform float uBeta;\n\nout vec2 fragColor;\n\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0/ float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nvec2 x1 = texture(uSampler,vUV - vec2(uoff,0)).xy;\nvec2 x2 = texture(uSampler,vUV + vec2(uoff,0)).xy;\nvec2 y1 = texture(uSampler,vUV - vec2(0,voff)).xy;\nvec2 y2 = texture(uSampler,vUV + vec2(0,voff)).xy;\n\nvec2 b1 = texture(uSampler1,vUV).xy;\n\nfragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta;\n}'; 6 | export const GLSL_PS_PROJFINISH = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //1D P_in\nuniform sampler2D uSampler1; //2D W_in\n\nout vec2 fragColor;\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0 / float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nfloat p1 = texture(uSampler,vUV - vec2(uoff,0)).x;\nfloat p2 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat p3 = texture(uSampler,vUV - vec2(0,voff)).x;\nfloat p4 = texture(uSampler,vUV + vec2(0,voff)).x;\n\nvec2 u = texture(uSampler1,vUV).xy - vec2(p2-p1,p4-p3) /(2.0 * voff);\n\nfragColor = u;\n}'; 7 | export const GLSL_PS_PROJSETUP = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //W_in 2D\n\nout vec2 fragColor; //DivW_out 1D\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0/ float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\n\nfloat x1 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat x2 = texture(uSampler,vUV - vec2(uoff,0)).x;\n\nfloat y1 = texture(uSampler,vUV + vec2(0,voff)).y;\nfloat y2 = texture(uSampler,vUV - vec2(0,voff)).y;\n\nfloat v = (x1 - x2 + y1 - y2) / (2.0 * uoff);\nfragColor = vec2(v,v);\n}'; 8 | export const GLSL_VS_DEFAULT = '#version 300 es\nprecision mediump float;\nlayout(location = 0) in vec2 aPosition;\nlayout(location = 1) in vec2 aUV;\n\nout vec2 vUV;\n\nvoid main(){\ngl_Position = vec4(aPosition *2.0-1.0,0,1);\n\nvUV = aUV;\n}'; 9 | export const GLSL_PS_COLOR = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform vec4 uColor;\n\nout vec4 fragColor;\nvoid main(){\nfragColor = uColor;\n}'; 10 | export const GLSL_PS_FLUID = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\nuniform sampler2D uSampler; //Color Texture RGB\nuniform sampler2D uSampler1; //Velocity texture RG32F\n\nuniform float uDeltaTime;\n\nuniform float uForceExponent;\nuniform vec2 uForceOrigin;\nuniform float uTime;\n\n\nout vec4 fragColor;\nvoid main(){\n//color advection\nvec2 delta = texture(uSampler1,vUV).xy * uDeltaTime;\nvec3 color =texture(uSampler,vUV - delta).xyz;\n\n//color added\n// vec3 dye = clamp(sin(uTime * vec3(2.72, 5.12, 4.98))+0.5,0.0,1.0);\n// float amp = exp(-uForceExponent * distance(uForceOrigin, vUV));\n// color = mix(color, dye, clamp(amp * 100.0,0.0,1.0));\n\nfragColor = vec4(color,1.0);\n}'; 11 | export const GLSL_VS_DEFAULT_FLIP = '#version 300 es\nprecision mediump float;\nlayout(location = 0) in vec2 aPosition;\nlayout(location = 1) in vec2 aUV;\n\nout vec2 vUV;\n\nvoid main(){\ngl_Position = vec4(aPosition *2.0-1.0,0,1);\n\nvUV = aUV;\nvUV.y = 1.0 - vUV.y;\n}'; 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 5 | "module": "amd", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": ["dom","es2015"], /* Specify library files to be included in the compilation. */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 12 | "outFile": "./dist/StableFluids.js", /* Concatenate and emit output to single file. */ 13 | // "outDir": "./", /* Redirect output structure to the directory. */ 14 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 15 | // "removeComments": true, /* Do not emit comments to output. */ 16 | // "noEmit": true, /* Do not emit outputs. */ 17 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 18 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 19 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 20 | 21 | /* Strict Type-Checking Options */ 22 | "strict": true, /* Enable all strict type-checking options. */ 23 | "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ 24 | "strictNullChecks": false, /* Enable strict null checks. */ 25 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 26 | "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ 27 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 28 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 29 | 30 | /* Additional Checks */ 31 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 32 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 33 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 34 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 35 | 36 | /* Module Resolution Options */ 37 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 38 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 39 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 40 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 41 | // "typeRoots": [], /* List of folders to include type definitions from. */ 42 | "types": ["wglut"], /* Type declaration files to be included in compilation. */ 43 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 44 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 45 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 46 | 47 | /* Source Map Options */ 48 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 49 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 50 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 51 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 52 | 53 | /* Experimental Options */ 54 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 55 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 56 | }, 57 | "include": [ 58 | "./src/script" 59 | ] 60 | } -------------------------------------------------------------------------------- /docs/wglut.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 | (factory((global.wglut = {}))); 5 | }(this, (function (exports) { 'use strict'; 6 | 7 | var GLProgram = /** @class */ (function () { 8 | function GLProgram(gl, program) { 9 | this.Attributes = {}; 10 | this.Unifroms = {}; 11 | this.Program = program; 12 | var numAttrs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); 13 | for (var i = 0; i < numAttrs; i++) { 14 | var attrInfo = gl.getActiveAttrib(program, i); 15 | if (attrInfo == null) 16 | continue; 17 | var attrLoca = gl.getAttribLocation(program, attrInfo.name); 18 | this.Attributes[attrInfo.name] = attrLoca; 19 | } 20 | var numUniform = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 21 | for (var i = 0; i < numUniform; i++) { 22 | var uniformInfo = gl.getActiveUniform(program, i); 23 | if (uniformInfo == null) 24 | continue; 25 | var uniformLoca = gl.getUniformLocation(program, uniformInfo.name); 26 | this.Unifroms[uniformInfo.name] = uniformLoca; 27 | } 28 | } 29 | GLProgram.prototype.GetUnifrom = function (key) { 30 | return this.Unifroms[key]; 31 | }; 32 | GLProgram.prototype.GetAttribute = function (key) { 33 | return this.Attributes[key]; 34 | }; 35 | return GLProgram; 36 | }()); 37 | 38 | var GLContext = /** @class */ (function () { 39 | function GLContext(wgl) { 40 | this.gl = wgl; 41 | } 42 | GLContext.createFromGL = function (wgl) { 43 | return new GLContext(wgl); 44 | }; 45 | GLContext.createFromCanvas = function (canvas) { 46 | var g = canvas.getContext('webgl2'); 47 | if (g == null) { 48 | g = canvas.getContext('webgl'); 49 | } 50 | if (g == null) 51 | return null; 52 | return new GLContext(g); 53 | }; 54 | GLContext.prototype.createProgram = function (vsource, psource) { 55 | var gl = this.gl; 56 | var vs = gl.createShader(gl.VERTEX_SHADER); 57 | gl.shaderSource(vs, vsource); 58 | gl.compileShader(vs); 59 | if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { 60 | console.error('compile vertex shader failed: ' + gl.getShaderInfoLog(vs)); 61 | gl.deleteShader(vs); 62 | return null; 63 | } 64 | var ps = gl.createShader(gl.FRAGMENT_SHADER); 65 | gl.shaderSource(ps, psource); 66 | gl.compileShader(ps); 67 | if (!gl.getShaderParameter(ps, gl.COMPILE_STATUS)) { 68 | console.error('compile fragment shader failed: ' + gl.getShaderInfoLog(ps)); 69 | gl.deleteShader(ps); 70 | return null; 71 | } 72 | var program = gl.createProgram(); 73 | gl.attachShader(program, vs); 74 | gl.attachShader(program, ps); 75 | gl.linkProgram(program); 76 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 77 | console.error('link shader program failed!:' + gl.getProgramInfoLog(program)); 78 | gl.deleteProgram(program); 79 | gl.deleteShader(vs); 80 | gl.deleteShader(ps); 81 | return null; 82 | } 83 | if (program == null) 84 | return null; 85 | var p = new GLProgram(gl, program); 86 | var handler = { 87 | get: function (tar, name) { 88 | if (name in tar) 89 | return tar[name]; 90 | if (name in tar.Unifroms) { 91 | tar[name] = tar.Unifroms[name]; 92 | return tar[name]; 93 | } 94 | else if (name in tar.Attributes) { 95 | tar[name] = tar.Attributes[name]; 96 | return tar[name]; 97 | } 98 | console.warn('program can not find attr/uniform:' + name); 99 | tar[name] = undefined; 100 | return null; 101 | } 102 | }; 103 | return new Proxy(p, handler); 104 | }; 105 | GLContext.prototype.createTextureImage = function (src, callback) { 106 | var img = new Image(); 107 | var gl = this.gl; 108 | var tex = gl.createTexture(); 109 | img.onload = function () { 110 | gl.bindTexture(gl.TEXTURE_2D, tex); 111 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img); 112 | gl.generateMipmap(gl.TEXTURE_2D); 113 | gl.bindTexture(gl.TEXTURE_2D, null); 114 | console.log('init webgl texture'); 115 | if (callback != null) 116 | callback(); 117 | }; 118 | img.src = src; 119 | return tex; 120 | }; 121 | GLContext.prototype.createTexture = function (internalFormat, width, height, linear, mipmap) { 122 | if (linear === void 0) { linear = false; } 123 | if (mipmap === void 0) { mipmap = false; } 124 | var gl = this.gl; 125 | var tex = gl.createTexture(); 126 | gl.bindTexture(gl.TEXTURE_2D, tex); 127 | gl.texStorage2D(gl.TEXTURE_2D, 1, internalFormat, width, height); 128 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, linear ? gl.LINEAR : gl.NEAREST); 129 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, linear ? (mipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR) : gl.NEAREST); 130 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 131 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 132 | if (mipmap) 133 | gl.generateMipmap(gl.TEXTURE_2D); 134 | return tex; 135 | }; 136 | return GLContext; 137 | }()); 138 | 139 | exports.GLContext = GLContext; 140 | exports.GLProgram = GLProgram; 141 | 142 | Object.defineProperty(exports, '__esModule', { value: true }); 143 | 144 | }))); 145 | //# sourceMappingURL=wglut.umd.js.map 146 | -------------------------------------------------------------------------------- /src/script/StableFluids.ts: -------------------------------------------------------------------------------- 1 | import { GLSL_VS_DEFAULT, GLSL_PS_ADVECT, GLSL_PS_FORCE, GLSL_PS_JACOBI1D, GLSL_PS_JACOBI2D, GLSL_PS_PROJSETUP, GLSL_PS_PROJFINISH, GLSL_PS_DEFAULT, GLSL_PS_COLOR, GLSL_PS_FLUID, GLSL_VS_DEFAULT_FLIP } from "./ShaderLibs"; 2 | 3 | 4 | /// 5 | import wglut = require('wglut'); 6 | 7 | type GLContext = wglut.GLContext; 8 | type GLProgram = wglut.GLProgram; 9 | 10 | const SIM_SIZE_W: number = 512; 11 | const SIM_SIZE_H: number = 512; 12 | 13 | export class StableFluids { 14 | 15 | private gl: WebGL2RenderingContext; 16 | private glctx: GLContext; 17 | 18 | private m_frameBuffer: WebGLFramebuffer; 19 | 20 | private m_bufferVertexQuad: WebGLBuffer; 21 | private m_bufferIndicesQuad: WebGLBuffer; 22 | 23 | private m_vaoQuad: WebGLVertexArrayObject; 24 | 25 | private m_programAdvect: GLProgram; 26 | private m_programForce: GLProgram; 27 | private m_programProjSetup: GLProgram; 28 | private m_programProjFinish: GLProgram; 29 | private m_programJacobi1D: GLProgram; 30 | private m_programJacobi2D: GLProgram; 31 | private m_programColor: GLProgram; 32 | 33 | private m_programDefault: GLProgram; 34 | private m_programDefaultFlipY: GLProgram; 35 | 36 | private m_programFluid: GLProgram; 37 | 38 | private m_texImage: WebGLTexture; 39 | 40 | private m_inited: boolean = false; 41 | private m_textureLoaded: boolean = false; 42 | private m_lastTimestamp: number = 0; 43 | 44 | private m_inputX:number = 0; 45 | private m_inputY:number = 0; 46 | 47 | private m_inputX_pre:number = 0; 48 | private m_inputY_pre:number = 0; 49 | 50 | private m_inputOnDrag:boolean = false; 51 | 52 | private m_mouseMoved:boolean = false; 53 | private m_mouseDown:boolean = false; 54 | 55 | private m_canvasWidth:number; 56 | private m_canvasHeight:number; 57 | 58 | 59 | 60 | public constructor(canvas: HTMLCanvasElement) { 61 | 62 | this.glctx = wglut.GLContext.createFromCanvas(canvas); 63 | if (this.glctx == null) { 64 | throw new Error("webgl2 not supported!"); 65 | } 66 | this.gl = this.glctx.gl; 67 | 68 | 69 | this.m_canvasWidth = canvas.width; 70 | this.m_canvasHeight = canvas.height; 71 | 72 | canvas.addEventListener('mousemove', this.EvtOnMouseMove.bind(this), false); 73 | canvas.addEventListener('mousedown',this.EvtOnMouseDown.bind(this),false); 74 | canvas.addEventListener('mouseup',this.EvtOnMouseUp.bind(this),false); 75 | canvas.addEventListener('mouseleave',this.EvtOnMouseLeave.bind(this),false); 76 | 77 | this.InitGL(); 78 | } 79 | 80 | private EvtOnMouseUp(e:MouseEvent){ 81 | this.m_inputOnDrag = false; 82 | this.m_mouseDown = false; 83 | } 84 | 85 | private EvtOnMouseLeave(e:MouseEvent){ 86 | this.m_inputOnDrag = false; 87 | this.m_mouseDown = false; 88 | } 89 | 90 | private EvtOnMouseDown(e:MouseEvent){ 91 | this.m_mouseDown = true; 92 | this.m_inputOnDrag = true; 93 | } 94 | 95 | private EvtOnMouseMove(e: MouseEvent) { 96 | 97 | this.m_inputX_pre = this.m_inputX; 98 | this.m_inputY_pre = this.m_inputY; 99 | 100 | this.m_inputX = e.offsetX / SIM_SIZE_H; 101 | this.m_inputY = 1.0 - e.offsetY / SIM_SIZE_H; 102 | 103 | } 104 | 105 | private InitGL() { 106 | let gl = this.gl; 107 | let glctx = this.glctx; 108 | 109 | //exts 110 | let avail_exts = gl.getSupportedExtensions(); 111 | let ext = gl.getExtension('EXT_color_buffer_float'); 112 | let extf = gl.getExtension('OES_texture_float_linear'); 113 | 114 | //buffers 115 | let vbuffer = gl.createBuffer(); 116 | gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer); 117 | var vdata: number[] = []; 118 | vdata.push(0, 0); 119 | vdata.push(0, 1.0); 120 | vdata.push(1.0, 1.0); 121 | vdata.push(1.0, 0); 122 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vdata), gl.STATIC_DRAW); 123 | this.m_bufferVertexQuad = vbuffer; 124 | 125 | let ibuffer: WebGLBuffer = gl.createBuffer(); 126 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer); 127 | let idata: number[] = [0, 1, 2, 0, 2, 3]; 128 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(idata), gl.STATIC_DRAW); 129 | this.m_bufferIndicesQuad = ibuffer; 130 | 131 | //shader programs 132 | this.m_programColor = glctx.createProgram(GLSL_VS_DEFAULT, GLSL_PS_COLOR); 133 | this.m_programDefault = glctx.createProgram( GLSL_VS_DEFAULT, GLSL_PS_DEFAULT); 134 | this.m_programDefaultFlipY =glctx.createProgram(GLSL_VS_DEFAULT_FLIP,GLSL_PS_DEFAULT); 135 | 136 | 137 | this.m_programFluid = glctx.createProgram(GLSL_VS_DEFAULT,GLSL_PS_FLUID); 138 | console.log(this.m_programFluid); 139 | 140 | this.m_programAdvect = glctx.createProgram(GLSL_VS_DEFAULT, GLSL_PS_ADVECT); 141 | this.m_programForce = glctx.createProgram(GLSL_VS_DEFAULT, GLSL_PS_FORCE); 142 | this.m_programJacobi1D =glctx.createProgram( GLSL_VS_DEFAULT, GLSL_PS_JACOBI1D); 143 | this.m_programJacobi2D = glctx.createProgram( GLSL_VS_DEFAULT, GLSL_PS_JACOBI2D); 144 | this.m_programProjSetup = glctx.createProgram( GLSL_VS_DEFAULT, GLSL_PS_PROJSETUP); 145 | this.m_programProjFinish = glctx.createProgram(GLSL_VS_DEFAULT, GLSL_PS_PROJFINISH); 146 | 147 | //vao 148 | let vao = gl.createVertexArray(); 149 | gl.bindVertexArray(vao); 150 | gl.bindBuffer(gl.ARRAY_BUFFER, this.m_bufferVertexQuad); 151 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_bufferIndicesQuad); 152 | 153 | gl.enableVertexAttribArray(this.m_programColor.Attributes['aPosition']); 154 | gl.vertexAttribPointer(this.m_programColor.Attributes['aPosition'], 2, gl.FLOAT, false,0, 0); 155 | gl.enableVertexAttribArray(this.m_programColor.Attributes['aUV']); 156 | gl.vertexAttribPointer(this.m_programColor.Attributes['aUV'], 2, gl.FLOAT, false, 0,0); 157 | 158 | gl.bindVertexArray(null); 159 | this.m_vaoQuad = vao; 160 | 161 | //image 162 | this.m_texImage = glctx.createTextureImage('image.png',()=>this.m_textureLoaded = true); 163 | 164 | //framebuffer 165 | let fbuffer: WebGLFramebuffer = gl.createFramebuffer(); 166 | gl.bindFramebuffer(gl.FRAMEBUFFER, fbuffer); 167 | this.m_frameBuffer = fbuffer; 168 | 169 | gl.viewport(0, 0, SIM_SIZE_W, SIM_SIZE_H); 170 | gl.clearColor(0, 0, 0, 1); 171 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 172 | 173 | } 174 | 175 | private m_texV1: WebGLTexture; 176 | private m_texV2: WebGLTexture; 177 | private m_texV3: WebGLTexture; 178 | private m_texP1: WebGLTexture; 179 | private m_texP2: WebGLTexture; 180 | 181 | private m_colRT1 :WebGLTexture; 182 | private m_colRT2: WebGLTexture; 183 | 184 | private m_dx: number; 185 | private m_difAlpha_prec:number; 186 | private m_viscosity:number = 0.000001; 187 | private m_force:number = 300; 188 | private m_exponent:number = 200; 189 | 190 | 191 | private InitSimulator() { 192 | if(!this.m_textureLoaded) return; 193 | 194 | let glctx = this.glctx; 195 | 196 | let gl = this.gl; 197 | this.m_texV1 = glctx.createTexture(gl.RG32F,SIM_SIZE_W,SIM_SIZE_H,true,false); 198 | this.m_texV2 = glctx.createTexture(gl.RG32F,SIM_SIZE_W,SIM_SIZE_H,true,false); 199 | this.m_texV3 = glctx.createTexture(gl.RG32F,SIM_SIZE_W,SIM_SIZE_H,true,false); 200 | 201 | this.m_texP1 = glctx.createTexture(gl.R32F,SIM_SIZE_W,SIM_SIZE_H,true,false); 202 | this.m_texP2 = glctx.createTexture(gl.R32F,SIM_SIZE_W,SIM_SIZE_H,true,false); 203 | 204 | this.m_colRT1 = glctx.createTexture(gl.RGBA8,SIM_SIZE_W,SIM_SIZE_H,true,true); 205 | this.m_colRT2 = glctx.createTexture(gl.RGBA8,SIM_SIZE_W,SIM_SIZE_H,true,true); 206 | 207 | this.RenderToTexture(this.m_texImage,this.m_colRT1,true); 208 | this.ResetFrameBuffer(); 209 | 210 | this.m_inited = true; 211 | 212 | let dx = 1.0/ SIM_SIZE_H; 213 | this.m_dx = dx; 214 | this.m_difAlpha_prec = dx * dx / this.m_viscosity; 215 | } 216 | 217 | public onFrame(ts: number) { 218 | if (!this.m_inited){ 219 | this.InitSimulator(); 220 | return; 221 | } 222 | if (this.m_lastTimestamp == 0.0) { 223 | this.m_lastTimestamp = ts; 224 | return; 225 | } 226 | let deltaTime = (ts - this.m_lastTimestamp)/1000.0; 227 | this.m_lastTimestamp = ts; 228 | 229 | var gl = this.gl; 230 | 231 | 232 | let debug_Velocity:boolean = false; 233 | let debug_enableProj:boolean = true; 234 | let debug_enableDiffuse:boolean= true; 235 | 236 | //Do simulation 237 | 238 | //Advection 239 | this.SetRenderTarget(this.m_texV2); 240 | this.DrawTexture(this.m_texV1,null,null,this.m_programAdvect,(p)=>{ 241 | let wgl = gl; 242 | let uDeltaTime = p['uDeltaTime']; 243 | gl.uniform1f(uDeltaTime,deltaTime); 244 | }); 245 | 246 | //Diffuse setup 247 | let dif_alpha = this.m_difAlpha_prec / deltaTime; //0.1 248 | let alpha = dif_alpha; 249 | let beta = alpha + 4; 250 | if(debug_enableDiffuse){ 251 | 252 | //copy v2 to v1 253 | this.RenderToTexture(this.m_texV2,this.m_texV1); 254 | 255 | //jacobi iteration 2D 256 | for(let i=0;i<20;i++){ 257 | this.SetRenderTarget(this.m_texV3); 258 | this.DrawTexture(this.m_texV2,this.m_texV1,null,this.m_programJacobi2D,(p)=>{ 259 | let wgl = gl; 260 | 261 | let ualpha = p['uAlpha']; 262 | let ubeta = p['uBeta']; 263 | wgl.uniform1f(ualpha,alpha); 264 | wgl.uniform1f(ubeta,beta); 265 | }) 266 | 267 | this.SetRenderTarget(this.m_texV2); 268 | this.DrawTexture(this.m_texV3,this.m_texV1,null,this.m_programJacobi2D,(p)=>{ 269 | let wgl = gl; 270 | let ualpha = p['uAlpha']; 271 | let ubeta = p['uBeta']; 272 | wgl.uniform1f(ualpha,alpha); 273 | wgl.uniform1f(ubeta,beta); 274 | }) 275 | } 276 | } 277 | 278 | //add external force V2->V3 279 | 280 | let forceX = 0; 281 | let forceY = 0; 282 | 283 | let force = this.m_force; 284 | if(this.m_inputOnDrag){ 285 | forceX = force *(this.m_inputX - this.m_inputX_pre); 286 | forceY = force *(this.m_inputY - this.m_inputY_pre); 287 | } 288 | else if(this.m_mouseDown){ 289 | force *=0.1; 290 | let rvec = this.RandomVecIdentity(); 291 | forceX = force * rvec[0]; 292 | forceY = force * rvec[1]; 293 | this.m_mouseDown = false; 294 | } 295 | 296 | this.SetRenderTarget(this.m_texV3); 297 | this.DrawTexture(this.m_texV2,null,null,this.m_programForce,(p)=>{ 298 | let wgl = gl; 299 | 300 | let uForceExponent = p['uForceExponent']; 301 | let uForceOrigin = p['uForceOrigin']; 302 | let uForceVector = p['uForceVector']; 303 | wgl.uniform1f(uForceExponent,this.m_exponent); 304 | wgl.uniform2f(uForceOrigin,this.m_inputX,this.m_inputY); 305 | wgl.uniform2f(uForceVector,forceX,forceY); 306 | }); 307 | 308 | this.RenderToTexture(this.m_texV3,this.m_texV1); 309 | 310 | //Proj setup V3->V1 311 | 312 | if(debug_enableProj){ 313 | this.SetRenderTarget(this.m_texV2); 314 | 315 | //V2(divergence of Velocity) 316 | this.DrawTexture(this.m_texV3,null,null,this.m_programProjSetup,null); 317 | 318 | //set P1 to 0 319 | this.SetRenderTarget(this.m_texP1); 320 | this.DrawColor([0,0,0,0]); 321 | 322 | //Jacobi 1D 323 | let dx = this.m_dx; 324 | var alpha1d = -dx * dx; 325 | var beta1d = 4; 326 | for(var i=0;i<20;i++){ 327 | this.SetRenderTarget(this.m_texP2); 328 | this.DrawTexture(this.m_texP1,this.m_texV2,null,this.m_programJacobi1D,(p)=>{ 329 | let wgl = gl; 330 | wgl.uniform1f(p['uAlpha'],alpha1d); 331 | wgl.uniform1f(p['uBeta'],beta1d); 332 | }); 333 | 334 | this.SetRenderTarget(this.m_texP1); 335 | this.DrawTexture(this.m_texP2,this.m_texV2,null,this.m_programJacobi1D,(p)=>{ 336 | let wgl = gl; 337 | wgl.uniform1f(p['uAlpha'],alpha1d); 338 | wgl.uniform1f(p['uBeta'],beta1d); 339 | }); 340 | } 341 | 342 | //ProjFinish 343 | this.SetRenderTarget(this.m_texV1); 344 | this.DrawTexture(this.m_texP1,this.m_texV3,null,this.m_programProjFinish,null); 345 | } 346 | else{ 347 | this.RenderToTexture(this.m_texV3,this.m_texV1); 348 | } 349 | 350 | //Use velocity to carry color 351 | 352 | if(debug_Velocity){ 353 | this.ResetFrameBuffer(); 354 | this.DrawTextureDefault(this.m_texV1); 355 | return; 356 | } 357 | else{ 358 | this.SetRenderTarget(this.m_colRT2); 359 | this.DrawTexture(this.m_colRT1, this.m_texV1, null, this.m_programFluid, (p) => { 360 | let wgl = gl; 361 | wgl.uniform1f(p['uDeltaTime'], deltaTime); 362 | wgl.uniform1f(p['uTime'],ts/1000.0); 363 | wgl.uniform1f(p['uForceExponent'],this.m_exponent); 364 | 365 | if(this.m_inputOnDrag){ 366 | wgl.uniform2f(p['uForceOrigin'],this.m_inputX,this.m_inputY); 367 | } 368 | else{ 369 | wgl.uniform2f(p['uForceOrigin'],-10.0,-10.0); 370 | } 371 | }) 372 | 373 | this.ResetFrameBuffer(); 374 | this.DrawTextureDefault(this.m_colRT2); 375 | 376 | //swrap colorbuffer 377 | let temp = this.m_colRT1; 378 | this.m_colRT1 = this.m_colRT2; 379 | this.m_colRT2 = temp; 380 | } 381 | } 382 | 383 | private RandomVecIdentity():number[]{ 384 | 385 | let t = Math.random()*Math.PI *2; 386 | let x = Math.sin(t); 387 | let y = Math.cos(t); 388 | 389 | return [x,y]; 390 | } 391 | 392 | 393 | private Clear(){ 394 | let gl = this.gl; 395 | gl.clear(gl.COLOR_BUFFER_BIT); 396 | } 397 | 398 | private ResetFrameBuffer(){ 399 | let gl = this.gl; 400 | gl.bindFramebuffer(gl.FRAMEBUFFER,null); 401 | } 402 | 403 | private SetRenderTarget(texture: WebGLTexture) { 404 | let gl = this.gl; 405 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_frameBuffer); 406 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); 407 | } 408 | 409 | private RenderToTexture(src:WebGLTexture,dest:WebGLTexture,flipY:boolean = false){ 410 | if(src === dest){ 411 | console.error('can not render to the same texture'); 412 | return; 413 | } 414 | 415 | this.SetRenderTarget(dest); 416 | this.DrawTextureDefault(src,flipY); 417 | } 418 | 419 | private DrawTextureDefault(tex0:WebGLTexture,flipY:boolean = false){ 420 | this.DrawTexture(tex0,null,null,flipY? this.m_programDefaultFlipY : this.m_programDefault,null); 421 | } 422 | 423 | private DrawColor(color:number[]){ 424 | let gl = this.gl; 425 | gl.bindVertexArray(this.m_vaoQuad); 426 | gl.useProgram(this.m_programColor.Program); 427 | 428 | let ucolor = this.m_programColor['uColor']; 429 | 430 | gl.uniform4fv(ucolor,color); 431 | gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0); 432 | 433 | } 434 | 435 | private DrawTexture(tex0:WebGLTexture,tex1?:WebGLTexture,tex2?:WebGLTexture,program?:GLProgram,setUniform?:(p:GLProgram)=>void){ 436 | if(program == null) program = this.m_programDefault; 437 | 438 | let gl = this.gl; 439 | gl.bindVertexArray(this.m_vaoQuad); 440 | gl.useProgram(program.Program); 441 | 442 | if(tex0 != null){ 443 | gl.activeTexture(gl.TEXTURE0); 444 | gl.bindTexture(gl.TEXTURE_2D,tex0); 445 | 446 | let usampler0 = program['uSampler']; 447 | gl.uniform1i(usampler0,0); 448 | } 449 | if(tex1 != null){ 450 | gl.activeTexture(gl.TEXTURE1); 451 | gl.bindTexture(gl.TEXTURE_2D,tex1); 452 | 453 | let usampler1 = program['uSampler1']; 454 | gl.uniform1i(usampler1,1); 455 | } 456 | if(tex2 != null){ 457 | gl.activeTexture(gl.TEXTURE2); 458 | gl.bindTexture(gl.TEXTURE_2D,tex2); 459 | let usampler2 = program['uSampler2']; 460 | gl.uniform1i(usampler2,2); 461 | } 462 | 463 | if(setUniform != null){ 464 | setUniform(program); 465 | } 466 | gl.drawElements(gl.TRIANGLES,6,gl.UNSIGNED_SHORT,0); 467 | 468 | } 469 | } -------------------------------------------------------------------------------- /dist/stablefluids.js: -------------------------------------------------------------------------------- 1 | define("ShaderLibs", ["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | exports.GLSL_PS_ADVECT = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler;\nuniform float uDeltaTime;\n\nout vec2 fragmentColor;\nvoid main(){\nvec4 col = texture(uSampler,vUV);\nivec2 texsize = textureSize(uSampler,1);\nvec2 duv = col.xy * vec2(texsize.y/texsize.x,1.0) * uDeltaTime;\nfragmentColor = texture(uSampler,vUV - duv).xy;\n}'; 5 | exports.GLSL_PS_DEFAULT = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform sampler2D uSampler;\n\nout vec4 fragColor;\nvoid main(){\nfragColor = texture(uSampler,vUV);\n}'; 6 | exports.GLSL_PS_FORCE = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //W_in 2D\nuniform float uDeltaTime;\n\n//map to [0,1.0]\nuniform vec2 uForceOrigin;\nuniform vec2 uForceVector;\nuniform float uForceExponent;\n\nout vec2 fragColor; //W_out 2D\nvoid main(){\nvec4 col = texture(uSampler,vUV);\nivec2 texsize = textureSize(uSampler,0);\n\nvec2 pos = vUV;\nfloat amp = exp(-uForceExponent * distance(uForceOrigin,pos));\nfragColor = col.xy + uForceVector * amp;\n}'; 7 | exports.GLSL_PS_JACOBI1D = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform sampler2D uSampler; //1D X1\nuniform sampler2D uSampler1;//1D B1\n\nuniform float uAlpha;\nuniform float uBeta;\n\nout float fragColor;\n\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0 / float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nfloat x1 = texture(uSampler,vUV - vec2(uoff,0)).x;\nfloat x2 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat y1 = texture(uSampler,vUV - vec2(0,voff)).x;\nfloat y2 = texture(uSampler,vUV + vec2(0,voff)).x;\n\nfloat b1 = texture(uSampler1,vUV).x;\n\nfragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta;\n}'; 8 | exports.GLSL_PS_JACOBI2D = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //2D X2\nuniform sampler2D uSampler1;//2D B2\n\nuniform float uAlpha;\nuniform float uBeta;\n\nout vec2 fragColor;\n\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0/ float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nvec2 x1 = texture(uSampler,vUV - vec2(uoff,0)).xy;\nvec2 x2 = texture(uSampler,vUV + vec2(uoff,0)).xy;\nvec2 y1 = texture(uSampler,vUV - vec2(0,voff)).xy;\nvec2 y2 = texture(uSampler,vUV + vec2(0,voff)).xy;\n\nvec2 b1 = texture(uSampler1,vUV).xy;\n\nfragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta;\n}'; 9 | exports.GLSL_PS_PROJFINISH = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //1D P_in\nuniform sampler2D uSampler1; //2D W_in\n\nout vec2 fragColor;\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0 / float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nfloat p1 = texture(uSampler,vUV - vec2(uoff,0)).x;\nfloat p2 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat p3 = texture(uSampler,vUV - vec2(0,voff)).x;\nfloat p4 = texture(uSampler,vUV + vec2(0,voff)).x;\n\nvec2 u = texture(uSampler1,vUV).xy - vec2(p2-p1,p4-p3) /(2.0 * voff);\n\nfragColor = u;\n}'; 10 | exports.GLSL_PS_PROJSETUP = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //W_in 2D\n\nout vec2 fragColor; //DivW_out 1D\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0/ float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\n\nfloat x1 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat x2 = texture(uSampler,vUV - vec2(uoff,0)).x;\n\nfloat y1 = texture(uSampler,vUV + vec2(0,voff)).y;\nfloat y2 = texture(uSampler,vUV - vec2(0,voff)).y;\n\nfloat v = (x1 - x2 + y1 - y2) / (2.0 * uoff);\nfragColor = vec2(v,v);\n}'; 11 | exports.GLSL_VS_DEFAULT = '#version 300 es\nprecision mediump float;\nlayout(location = 0) in vec2 aPosition;\nlayout(location = 1) in vec2 aUV;\n\nout vec2 vUV;\n\nvoid main(){\ngl_Position = vec4(aPosition *2.0-1.0,0,1);\n\nvUV = aUV;\n}'; 12 | exports.GLSL_PS_COLOR = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform vec4 uColor;\n\nout vec4 fragColor;\nvoid main(){\nfragColor = uColor;\n}'; 13 | exports.GLSL_PS_FLUID = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\nuniform sampler2D uSampler; //Color Texture RGB\nuniform sampler2D uSampler1; //Velocity texture RG32F\n\nuniform float uDeltaTime;\n\nuniform float uForceExponent;\nuniform vec2 uForceOrigin;\nuniform float uTime;\n\n\nout vec4 fragColor;\nvoid main(){\n//color advection\nvec2 delta = texture(uSampler1,vUV).xy * uDeltaTime;\nvec3 color =texture(uSampler,vUV - delta).xyz;\n\n//color added\n// vec3 dye = clamp(sin(uTime * vec3(2.72, 5.12, 4.98))+0.5,0.0,1.0);\n// float amp = exp(-uForceExponent * distance(uForceOrigin, vUV));\n// color = mix(color, dye, clamp(amp * 100.0,0.0,1.0));\n\nfragColor = vec4(color,1.0);\n}'; 14 | exports.GLSL_VS_DEFAULT_FLIP = '#version 300 es\nprecision mediump float;\nlayout(location = 0) in vec2 aPosition;\nlayout(location = 1) in vec2 aUV;\n\nout vec2 vUV;\n\nvoid main(){\ngl_Position = vec4(aPosition *2.0-1.0,0,1);\n\nvUV = aUV;\nvUV.y = 1.0 - vUV.y;\n}'; 15 | }); 16 | define("StableFluids", ["require", "exports", "ShaderLibs", "wglut"], function (require, exports, ShaderLibs_1, wglut) { 17 | "use strict"; 18 | Object.defineProperty(exports, "__esModule", { value: true }); 19 | var SIM_SIZE_W = 512; 20 | var SIM_SIZE_H = 512; 21 | var StableFluids = /** @class */ (function () { 22 | function StableFluids(canvas) { 23 | this.m_inited = false; 24 | this.m_textureLoaded = false; 25 | this.m_lastTimestamp = 0; 26 | this.m_inputX = 0; 27 | this.m_inputY = 0; 28 | this.m_inputX_pre = 0; 29 | this.m_inputY_pre = 0; 30 | this.m_inputOnDrag = false; 31 | this.m_mouseMoved = false; 32 | this.m_mouseDown = false; 33 | this.m_viscosity = 0.000001; 34 | this.m_force = 300; 35 | this.m_exponent = 200; 36 | this.glctx = wglut.GLContext.createFromCanvas(canvas); 37 | if (this.glctx == null) { 38 | throw new Error("webgl2 not supported!"); 39 | } 40 | this.gl = this.glctx.gl; 41 | this.m_canvasWidth = canvas.width; 42 | this.m_canvasHeight = canvas.height; 43 | canvas.addEventListener('mousemove', this.EvtOnMouseMove.bind(this), false); 44 | canvas.addEventListener('mousedown', this.EvtOnMouseDown.bind(this), false); 45 | canvas.addEventListener('mouseup', this.EvtOnMouseUp.bind(this), false); 46 | canvas.addEventListener('mouseleave', this.EvtOnMouseLeave.bind(this), false); 47 | this.InitGL(); 48 | } 49 | StableFluids.prototype.EvtOnMouseUp = function (e) { 50 | this.m_inputOnDrag = false; 51 | this.m_mouseDown = false; 52 | }; 53 | StableFluids.prototype.EvtOnMouseLeave = function (e) { 54 | this.m_inputOnDrag = false; 55 | this.m_mouseDown = false; 56 | }; 57 | StableFluids.prototype.EvtOnMouseDown = function (e) { 58 | this.m_mouseDown = true; 59 | this.m_inputOnDrag = true; 60 | }; 61 | StableFluids.prototype.EvtOnMouseMove = function (e) { 62 | this.m_inputX_pre = this.m_inputX; 63 | this.m_inputY_pre = this.m_inputY; 64 | this.m_inputX = e.offsetX / SIM_SIZE_H; 65 | this.m_inputY = 1.0 - e.offsetY / SIM_SIZE_H; 66 | }; 67 | StableFluids.prototype.InitGL = function () { 68 | var _this = this; 69 | var gl = this.gl; 70 | var glctx = this.glctx; 71 | //exts 72 | var avail_exts = gl.getSupportedExtensions(); 73 | var ext = gl.getExtension('EXT_color_buffer_float'); 74 | var extf = gl.getExtension('OES_texture_float_linear'); 75 | //buffers 76 | var vbuffer = gl.createBuffer(); 77 | gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer); 78 | var vdata = []; 79 | vdata.push(0, 0); 80 | vdata.push(0, 1.0); 81 | vdata.push(1.0, 1.0); 82 | vdata.push(1.0, 0); 83 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vdata), gl.STATIC_DRAW); 84 | this.m_bufferVertexQuad = vbuffer; 85 | var ibuffer = gl.createBuffer(); 86 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer); 87 | var idata = [0, 1, 2, 0, 2, 3]; 88 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(idata), gl.STATIC_DRAW); 89 | this.m_bufferIndicesQuad = ibuffer; 90 | //shader programs 91 | this.m_programColor = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_COLOR); 92 | this.m_programDefault = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_DEFAULT); 93 | this.m_programDefaultFlipY = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT_FLIP, ShaderLibs_1.GLSL_PS_DEFAULT); 94 | this.m_programFluid = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_FLUID); 95 | console.log(this.m_programFluid); 96 | this.m_programAdvect = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_ADVECT); 97 | this.m_programForce = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_FORCE); 98 | this.m_programJacobi1D = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_JACOBI1D); 99 | this.m_programJacobi2D = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_JACOBI2D); 100 | this.m_programProjSetup = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_PROJSETUP); 101 | this.m_programProjFinish = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_PROJFINISH); 102 | //vao 103 | var vao = gl.createVertexArray(); 104 | gl.bindVertexArray(vao); 105 | gl.bindBuffer(gl.ARRAY_BUFFER, this.m_bufferVertexQuad); 106 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_bufferIndicesQuad); 107 | gl.enableVertexAttribArray(this.m_programColor.Attributes['aPosition']); 108 | gl.vertexAttribPointer(this.m_programColor.Attributes['aPosition'], 2, gl.FLOAT, false, 0, 0); 109 | gl.enableVertexAttribArray(this.m_programColor.Attributes['aUV']); 110 | gl.vertexAttribPointer(this.m_programColor.Attributes['aUV'], 2, gl.FLOAT, false, 0, 0); 111 | gl.bindVertexArray(null); 112 | this.m_vaoQuad = vao; 113 | //image 114 | this.m_texImage = glctx.createTextureImage('image.png', function () { return _this.m_textureLoaded = true; }); 115 | //framebuffer 116 | var fbuffer = gl.createFramebuffer(); 117 | gl.bindFramebuffer(gl.FRAMEBUFFER, fbuffer); 118 | this.m_frameBuffer = fbuffer; 119 | gl.viewport(0, 0, SIM_SIZE_W, SIM_SIZE_H); 120 | gl.clearColor(0, 0, 0, 1); 121 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 122 | }; 123 | StableFluids.prototype.InitSimulator = function () { 124 | if (!this.m_textureLoaded) 125 | return; 126 | var glctx = this.glctx; 127 | var gl = this.gl; 128 | this.m_texV1 = glctx.createTexture(gl.RG32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 129 | this.m_texV2 = glctx.createTexture(gl.RG32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 130 | this.m_texV3 = glctx.createTexture(gl.RG32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 131 | this.m_texP1 = glctx.createTexture(gl.R32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 132 | this.m_texP2 = glctx.createTexture(gl.R32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 133 | this.m_colRT1 = glctx.createTexture(gl.RGBA8, SIM_SIZE_W, SIM_SIZE_H, true, true); 134 | this.m_colRT2 = glctx.createTexture(gl.RGBA8, SIM_SIZE_W, SIM_SIZE_H, true, true); 135 | this.RenderToTexture(this.m_texImage, this.m_colRT1, true); 136 | this.ResetFrameBuffer(); 137 | this.m_inited = true; 138 | var dx = 1.0 / SIM_SIZE_H; 139 | this.m_dx = dx; 140 | this.m_difAlpha_prec = dx * dx / this.m_viscosity; 141 | }; 142 | StableFluids.prototype.onFrame = function (ts) { 143 | var _this = this; 144 | if (!this.m_inited) { 145 | this.InitSimulator(); 146 | return; 147 | } 148 | if (this.m_lastTimestamp == 0.0) { 149 | this.m_lastTimestamp = ts; 150 | return; 151 | } 152 | var deltaTime = (ts - this.m_lastTimestamp) / 1000.0; 153 | this.m_lastTimestamp = ts; 154 | var gl = this.gl; 155 | var debug_Velocity = false; 156 | var debug_enableProj = true; 157 | var debug_enableDiffuse = true; 158 | //Do simulation 159 | //Advection 160 | this.SetRenderTarget(this.m_texV2); 161 | this.DrawTexture(this.m_texV1, null, null, this.m_programAdvect, function (p) { 162 | var wgl = gl; 163 | var uDeltaTime = p['uDeltaTime']; 164 | gl.uniform1f(uDeltaTime, deltaTime); 165 | }); 166 | //Diffuse setup 167 | var dif_alpha = this.m_difAlpha_prec / deltaTime; //0.1 168 | var alpha = dif_alpha; 169 | var beta = alpha + 4; 170 | if (debug_enableDiffuse) { 171 | //copy v2 to v1 172 | this.RenderToTexture(this.m_texV2, this.m_texV1); 173 | //jacobi iteration 2D 174 | for (var i_1 = 0; i_1 < 20; i_1++) { 175 | this.SetRenderTarget(this.m_texV3); 176 | this.DrawTexture(this.m_texV2, this.m_texV1, null, this.m_programJacobi2D, function (p) { 177 | var wgl = gl; 178 | var ualpha = p['uAlpha']; 179 | var ubeta = p['uBeta']; 180 | wgl.uniform1f(ualpha, alpha); 181 | wgl.uniform1f(ubeta, beta); 182 | }); 183 | this.SetRenderTarget(this.m_texV2); 184 | this.DrawTexture(this.m_texV3, this.m_texV1, null, this.m_programJacobi2D, function (p) { 185 | var wgl = gl; 186 | var ualpha = p['uAlpha']; 187 | var ubeta = p['uBeta']; 188 | wgl.uniform1f(ualpha, alpha); 189 | wgl.uniform1f(ubeta, beta); 190 | }); 191 | } 192 | } 193 | //add external force V2->V3 194 | var forceX = 0; 195 | var forceY = 0; 196 | var force = this.m_force; 197 | if (this.m_inputOnDrag) { 198 | forceX = force * (this.m_inputX - this.m_inputX_pre); 199 | forceY = force * (this.m_inputY - this.m_inputY_pre); 200 | } 201 | else if (this.m_mouseDown) { 202 | force *= 0.1; 203 | var rvec = this.RandomVecIdentity(); 204 | forceX = force * rvec[0]; 205 | forceY = force * rvec[1]; 206 | this.m_mouseDown = false; 207 | } 208 | this.SetRenderTarget(this.m_texV3); 209 | this.DrawTexture(this.m_texV2, null, null, this.m_programForce, function (p) { 210 | var wgl = gl; 211 | var uForceExponent = p['uForceExponent']; 212 | var uForceOrigin = p['uForceOrigin']; 213 | var uForceVector = p['uForceVector']; 214 | wgl.uniform1f(uForceExponent, _this.m_exponent); 215 | wgl.uniform2f(uForceOrigin, _this.m_inputX, _this.m_inputY); 216 | wgl.uniform2f(uForceVector, forceX, forceY); 217 | }); 218 | this.RenderToTexture(this.m_texV3, this.m_texV1); 219 | //Proj setup V3->V1 220 | if (debug_enableProj) { 221 | this.SetRenderTarget(this.m_texV2); 222 | //V2(divergence of Velocity) 223 | this.DrawTexture(this.m_texV3, null, null, this.m_programProjSetup, null); 224 | //set P1 to 0 225 | this.SetRenderTarget(this.m_texP1); 226 | this.DrawColor([0, 0, 0, 0]); 227 | //Jacobi 1D 228 | var dx = this.m_dx; 229 | var alpha1d = -dx * dx; 230 | var beta1d = 4; 231 | for (var i = 0; i < 20; i++) { 232 | this.SetRenderTarget(this.m_texP2); 233 | this.DrawTexture(this.m_texP1, this.m_texV2, null, this.m_programJacobi1D, function (p) { 234 | var wgl = gl; 235 | wgl.uniform1f(p['uAlpha'], alpha1d); 236 | wgl.uniform1f(p['uBeta'], beta1d); 237 | }); 238 | this.SetRenderTarget(this.m_texP1); 239 | this.DrawTexture(this.m_texP2, this.m_texV2, null, this.m_programJacobi1D, function (p) { 240 | var wgl = gl; 241 | wgl.uniform1f(p['uAlpha'], alpha1d); 242 | wgl.uniform1f(p['uBeta'], beta1d); 243 | }); 244 | } 245 | //ProjFinish 246 | this.SetRenderTarget(this.m_texV1); 247 | this.DrawTexture(this.m_texP1, this.m_texV3, null, this.m_programProjFinish, null); 248 | } 249 | else { 250 | this.RenderToTexture(this.m_texV3, this.m_texV1); 251 | } 252 | //Use velocity to carry color 253 | if (debug_Velocity) { 254 | this.ResetFrameBuffer(); 255 | this.DrawTextureDefault(this.m_texV1); 256 | return; 257 | } 258 | else { 259 | this.SetRenderTarget(this.m_colRT2); 260 | this.DrawTexture(this.m_colRT1, this.m_texV1, null, this.m_programFluid, function (p) { 261 | var wgl = gl; 262 | wgl.uniform1f(p['uDeltaTime'], deltaTime); 263 | wgl.uniform1f(p['uTime'], ts / 1000.0); 264 | wgl.uniform1f(p['uForceExponent'], _this.m_exponent); 265 | if (_this.m_inputOnDrag) { 266 | wgl.uniform2f(p['uForceOrigin'], _this.m_inputX, _this.m_inputY); 267 | } 268 | else { 269 | wgl.uniform2f(p['uForceOrigin'], -10.0, -10.0); 270 | } 271 | }); 272 | this.ResetFrameBuffer(); 273 | this.DrawTextureDefault(this.m_colRT2); 274 | //swrap colorbuffer 275 | var temp = this.m_colRT1; 276 | this.m_colRT1 = this.m_colRT2; 277 | this.m_colRT2 = temp; 278 | } 279 | }; 280 | StableFluids.prototype.RandomVecIdentity = function () { 281 | var t = Math.random() * Math.PI * 2; 282 | var x = Math.sin(t); 283 | var y = Math.cos(t); 284 | return [x, y]; 285 | }; 286 | StableFluids.prototype.Clear = function () { 287 | var gl = this.gl; 288 | gl.clear(gl.COLOR_BUFFER_BIT); 289 | }; 290 | StableFluids.prototype.ResetFrameBuffer = function () { 291 | var gl = this.gl; 292 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 293 | }; 294 | StableFluids.prototype.SetRenderTarget = function (texture) { 295 | var gl = this.gl; 296 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_frameBuffer); 297 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); 298 | }; 299 | StableFluids.prototype.RenderToTexture = function (src, dest, flipY) { 300 | if (flipY === void 0) { flipY = false; } 301 | if (src === dest) { 302 | console.error('can not render to the same texture'); 303 | return; 304 | } 305 | this.SetRenderTarget(dest); 306 | this.DrawTextureDefault(src, flipY); 307 | }; 308 | StableFluids.prototype.DrawTextureDefault = function (tex0, flipY) { 309 | if (flipY === void 0) { flipY = false; } 310 | this.DrawTexture(tex0, null, null, flipY ? this.m_programDefaultFlipY : this.m_programDefault, null); 311 | }; 312 | StableFluids.prototype.DrawColor = function (color) { 313 | var gl = this.gl; 314 | gl.bindVertexArray(this.m_vaoQuad); 315 | gl.useProgram(this.m_programColor.Program); 316 | var ucolor = this.m_programColor['uColor']; 317 | gl.uniform4fv(ucolor, color); 318 | gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); 319 | }; 320 | StableFluids.prototype.DrawTexture = function (tex0, tex1, tex2, program, setUniform) { 321 | if (program == null) 322 | program = this.m_programDefault; 323 | var gl = this.gl; 324 | gl.bindVertexArray(this.m_vaoQuad); 325 | gl.useProgram(program.Program); 326 | if (tex0 != null) { 327 | gl.activeTexture(gl.TEXTURE0); 328 | gl.bindTexture(gl.TEXTURE_2D, tex0); 329 | var usampler0 = program['uSampler']; 330 | gl.uniform1i(usampler0, 0); 331 | } 332 | if (tex1 != null) { 333 | gl.activeTexture(gl.TEXTURE1); 334 | gl.bindTexture(gl.TEXTURE_2D, tex1); 335 | var usampler1 = program['uSampler1']; 336 | gl.uniform1i(usampler1, 1); 337 | } 338 | if (tex2 != null) { 339 | gl.activeTexture(gl.TEXTURE2); 340 | gl.bindTexture(gl.TEXTURE_2D, tex2); 341 | var usampler2 = program['uSampler2']; 342 | gl.uniform1i(usampler2, 2); 343 | } 344 | if (setUniform != null) { 345 | setUniform(program); 346 | } 347 | gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); 348 | }; 349 | return StableFluids; 350 | }()); 351 | exports.StableFluids = StableFluids; 352 | }); 353 | -------------------------------------------------------------------------------- /docs/StableFluids.js: -------------------------------------------------------------------------------- 1 | define("ShaderLibs", ["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | exports.GLSL_PS_ADVECT = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler;\nuniform float uDeltaTime;\n\nout vec2 fragmentColor;\nvoid main(){\nvec4 col = texture(uSampler,vUV);\nivec2 texsize = textureSize(uSampler,1);\nvec2 duv = col.xy * vec2(texsize.y/texsize.x,1.0) * uDeltaTime;\nfragmentColor = texture(uSampler,vUV - duv).xy;\n}'; 5 | exports.GLSL_PS_DEFAULT = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform sampler2D uSampler;\n\nout vec4 fragColor;\nvoid main(){\nfragColor = texture(uSampler,vUV);\n}'; 6 | exports.GLSL_PS_FORCE = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //W_in 2D\nuniform float uDeltaTime;\n\n//map to [0,1.0]\nuniform vec2 uForceOrigin;\nuniform vec2 uForceVector;\nuniform float uForceExponent;\n\nout vec2 fragColor; //W_out 2D\nvoid main(){\nvec4 col = texture(uSampler,vUV);\nivec2 texsize = textureSize(uSampler,0);\n\nvec2 pos = vUV;\nfloat amp = exp(-uForceExponent * distance(uForceOrigin,pos));\nfragColor = col.xy + uForceVector * amp;\n}'; 7 | exports.GLSL_PS_JACOBI1D = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform sampler2D uSampler; //1D X1\nuniform sampler2D uSampler1;//1D B1\n\nuniform float uAlpha;\nuniform float uBeta;\n\nout float fragColor;\n\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0 / float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nfloat x1 = texture(uSampler,vUV - vec2(uoff,0)).x;\nfloat x2 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat y1 = texture(uSampler,vUV - vec2(0,voff)).x;\nfloat y2 = texture(uSampler,vUV + vec2(0,voff)).x;\n\nfloat b1 = texture(uSampler1,vUV).x;\n\nfragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta;\n}'; 8 | exports.GLSL_PS_JACOBI2D = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //2D X2\nuniform sampler2D uSampler1;//2D B2\n\nuniform float uAlpha;\nuniform float uBeta;\n\nout vec2 fragColor;\n\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0/ float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nvec2 x1 = texture(uSampler,vUV - vec2(uoff,0)).xy;\nvec2 x2 = texture(uSampler,vUV + vec2(uoff,0)).xy;\nvec2 y1 = texture(uSampler,vUV - vec2(0,voff)).xy;\nvec2 y2 = texture(uSampler,vUV + vec2(0,voff)).xy;\n\nvec2 b1 = texture(uSampler1,vUV).xy;\n\nfragColor = (x1 + x2 +y1+y2 + uAlpha *b1) / uBeta;\n}'; 9 | exports.GLSL_PS_PROJFINISH = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //1D P_in\nuniform sampler2D uSampler1; //2D W_in\n\nout vec2 fragColor;\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0 / float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\nfloat p1 = texture(uSampler,vUV - vec2(uoff,0)).x;\nfloat p2 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat p3 = texture(uSampler,vUV - vec2(0,voff)).x;\nfloat p4 = texture(uSampler,vUV + vec2(0,voff)).x;\n\nvec2 u = texture(uSampler1,vUV).xy - vec2(p2-p1,p4-p3) /(2.0 * voff);\n\nfragColor = u;\n}'; 10 | exports.GLSL_PS_PROJSETUP = '#version 300 es\nprecision highp float;\nin vec2 vUV;\n\nuniform sampler2D uSampler; //W_in 2D\n\nout vec2 fragColor; //DivW_out 1D\nvoid main(){\nivec2 texsize = textureSize(uSampler,0);\nfloat uoff = 1.0/ float(texsize.x);\nfloat voff = 1.0 / float(texsize.y);\n\n\nfloat x1 = texture(uSampler,vUV + vec2(uoff,0)).x;\nfloat x2 = texture(uSampler,vUV - vec2(uoff,0)).x;\n\nfloat y1 = texture(uSampler,vUV + vec2(0,voff)).y;\nfloat y2 = texture(uSampler,vUV - vec2(0,voff)).y;\n\nfloat v = (x1 - x2 + y1 - y2) / (2.0 * uoff);\nfragColor = vec2(v,v);\n}'; 11 | exports.GLSL_VS_DEFAULT = '#version 300 es\nprecision mediump float;\nlayout(location = 0) in vec2 aPosition;\nlayout(location = 1) in vec2 aUV;\n\nout vec2 vUV;\n\nvoid main(){\ngl_Position = vec4(aPosition *2.0-1.0,0,1);\n\nvUV = aUV;\n}'; 12 | exports.GLSL_PS_COLOR = '#version 300 es\nprecision highp float;\nin vec2 vUV;\nuniform vec4 uColor;\n\nout vec4 fragColor;\nvoid main(){\nfragColor = uColor;\n}'; 13 | exports.GLSL_PS_FLUID = '#version 300 es\nprecision mediump float;\nin vec2 vUV;\nuniform sampler2D uSampler; //Color Texture RGB\nuniform sampler2D uSampler1; //Velocity texture RG32F\n\nuniform float uDeltaTime;\n\nuniform float uForceExponent;\nuniform vec2 uForceOrigin;\nuniform float uTime;\n\n\nout vec4 fragColor;\nvoid main(){\n//color advection\nvec2 delta = texture(uSampler1,vUV).xy * uDeltaTime;\nvec3 color =texture(uSampler,vUV - delta).xyz;\n\n//color added\n// vec3 dye = clamp(sin(uTime * vec3(2.72, 5.12, 4.98))+0.5,0.0,1.0);\n// float amp = exp(-uForceExponent * distance(uForceOrigin, vUV));\n// color = mix(color, dye, clamp(amp * 100.0,0.0,1.0));\n\nfragColor = vec4(color,1.0);\n}'; 14 | exports.GLSL_VS_DEFAULT_FLIP = '#version 300 es\nprecision mediump float;\nlayout(location = 0) in vec2 aPosition;\nlayout(location = 1) in vec2 aUV;\n\nout vec2 vUV;\n\nvoid main(){\ngl_Position = vec4(aPosition *2.0-1.0,0,1);\n\nvUV = aUV;\nvUV.y = 1.0 - vUV.y;\n}'; 15 | }); 16 | define("StableFluids", ["require", "exports", "ShaderLibs", "wglut"], function (require, exports, ShaderLibs_1, wglut) { 17 | "use strict"; 18 | Object.defineProperty(exports, "__esModule", { value: true }); 19 | var SIM_SIZE_W = 512; 20 | var SIM_SIZE_H = 512; 21 | var StableFluids = /** @class */ (function () { 22 | function StableFluids(canvas) { 23 | this.m_inited = false; 24 | this.m_textureLoaded = false; 25 | this.m_lastTimestamp = 0; 26 | this.m_inputX = 0; 27 | this.m_inputY = 0; 28 | this.m_inputX_pre = 0; 29 | this.m_inputY_pre = 0; 30 | this.m_inputOnDrag = false; 31 | this.m_mouseMoved = false; 32 | this.m_mouseDown = false; 33 | this.m_viscosity = 0.000001; 34 | this.m_force = 300; 35 | this.m_exponent = 200; 36 | this.glctx = wglut.GLContext.createFromCanvas(canvas); 37 | if (this.glctx == null) { 38 | throw new Error("webgl2 not supported!"); 39 | } 40 | this.gl = this.glctx.gl; 41 | this.m_canvasWidth = canvas.width; 42 | this.m_canvasHeight = canvas.height; 43 | canvas.addEventListener('mousemove', this.EvtOnMouseMove.bind(this), false); 44 | canvas.addEventListener('mousedown', this.EvtOnMouseDown.bind(this), false); 45 | canvas.addEventListener('mouseup', this.EvtOnMouseUp.bind(this), false); 46 | canvas.addEventListener('mouseleave', this.EvtOnMouseLeave.bind(this), false); 47 | this.InitGL(); 48 | } 49 | StableFluids.prototype.EvtOnMouseUp = function (e) { 50 | this.m_inputOnDrag = false; 51 | this.m_mouseDown = false; 52 | }; 53 | StableFluids.prototype.EvtOnMouseLeave = function (e) { 54 | this.m_inputOnDrag = false; 55 | this.m_mouseDown = false; 56 | }; 57 | StableFluids.prototype.EvtOnMouseDown = function (e) { 58 | this.m_mouseDown = true; 59 | this.m_inputOnDrag = true; 60 | }; 61 | StableFluids.prototype.EvtOnMouseMove = function (e) { 62 | this.m_inputX_pre = this.m_inputX; 63 | this.m_inputY_pre = this.m_inputY; 64 | this.m_inputX = e.offsetX / SIM_SIZE_H; 65 | this.m_inputY = 1.0 - e.offsetY / SIM_SIZE_H; 66 | }; 67 | StableFluids.prototype.InitGL = function () { 68 | var _this = this; 69 | var gl = this.gl; 70 | var glctx = this.glctx; 71 | //exts 72 | var avail_exts = gl.getSupportedExtensions(); 73 | var ext = gl.getExtension('EXT_color_buffer_float'); 74 | var extf = gl.getExtension('OES_texture_float_linear'); 75 | //buffers 76 | var vbuffer = gl.createBuffer(); 77 | gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer); 78 | var vdata = []; 79 | vdata.push(0, 0); 80 | vdata.push(0, 1.0); 81 | vdata.push(1.0, 1.0); 82 | vdata.push(1.0, 0); 83 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vdata), gl.STATIC_DRAW); 84 | this.m_bufferVertexQuad = vbuffer; 85 | var ibuffer = gl.createBuffer(); 86 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer); 87 | var idata = [0, 1, 2, 0, 2, 3]; 88 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(idata), gl.STATIC_DRAW); 89 | this.m_bufferIndicesQuad = ibuffer; 90 | //shader programs 91 | this.m_programColor = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_COLOR); 92 | this.m_programDefault = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_DEFAULT); 93 | this.m_programDefaultFlipY = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT_FLIP, ShaderLibs_1.GLSL_PS_DEFAULT); 94 | this.m_programFluid = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_FLUID); 95 | console.log(this.m_programFluid); 96 | this.m_programAdvect = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_ADVECT); 97 | this.m_programForce = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_FORCE); 98 | this.m_programJacobi1D = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_JACOBI1D); 99 | this.m_programJacobi2D = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_JACOBI2D); 100 | this.m_programProjSetup = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_PROJSETUP); 101 | this.m_programProjFinish = glctx.createProgram(ShaderLibs_1.GLSL_VS_DEFAULT, ShaderLibs_1.GLSL_PS_PROJFINISH); 102 | //vao 103 | var vao = gl.createVertexArray(); 104 | gl.bindVertexArray(vao); 105 | gl.bindBuffer(gl.ARRAY_BUFFER, this.m_bufferVertexQuad); 106 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.m_bufferIndicesQuad); 107 | gl.enableVertexAttribArray(this.m_programColor.Attributes['aPosition']); 108 | gl.vertexAttribPointer(this.m_programColor.Attributes['aPosition'], 2, gl.FLOAT, false, 0, 0); 109 | gl.enableVertexAttribArray(this.m_programColor.Attributes['aUV']); 110 | gl.vertexAttribPointer(this.m_programColor.Attributes['aUV'], 2, gl.FLOAT, false, 0, 0); 111 | gl.bindVertexArray(null); 112 | this.m_vaoQuad = vao; 113 | //image 114 | this.m_texImage = glctx.createTextureImage('image.png', function () { return _this.m_textureLoaded = true; }); 115 | //framebuffer 116 | var fbuffer = gl.createFramebuffer(); 117 | gl.bindFramebuffer(gl.FRAMEBUFFER, fbuffer); 118 | this.m_frameBuffer = fbuffer; 119 | gl.viewport(0, 0, SIM_SIZE_W, SIM_SIZE_H); 120 | gl.clearColor(0, 0, 0, 1); 121 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 122 | }; 123 | StableFluids.prototype.InitSimulator = function () { 124 | if (!this.m_textureLoaded) 125 | return; 126 | var glctx = this.glctx; 127 | var gl = this.gl; 128 | this.m_texV1 = glctx.createTexture(gl.RG32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 129 | this.m_texV2 = glctx.createTexture(gl.RG32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 130 | this.m_texV3 = glctx.createTexture(gl.RG32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 131 | this.m_texP1 = glctx.createTexture(gl.R32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 132 | this.m_texP2 = glctx.createTexture(gl.R32F, SIM_SIZE_W, SIM_SIZE_H, true, false); 133 | this.m_colRT1 = glctx.createTexture(gl.RGBA8, SIM_SIZE_W, SIM_SIZE_H, true, true); 134 | this.m_colRT2 = glctx.createTexture(gl.RGBA8, SIM_SIZE_W, SIM_SIZE_H, true, true); 135 | this.RenderToTexture(this.m_texImage, this.m_colRT1, true); 136 | this.ResetFrameBuffer(); 137 | this.m_inited = true; 138 | var dx = 1.0 / SIM_SIZE_H; 139 | this.m_dx = dx; 140 | this.m_difAlpha_prec = dx * dx / this.m_viscosity; 141 | }; 142 | StableFluids.prototype.onFrame = function (ts) { 143 | var _this = this; 144 | if (!this.m_inited) { 145 | this.InitSimulator(); 146 | return; 147 | } 148 | if (this.m_lastTimestamp == 0.0) { 149 | this.m_lastTimestamp = ts; 150 | return; 151 | } 152 | var deltaTime = (ts - this.m_lastTimestamp) / 1000.0; 153 | this.m_lastTimestamp = ts; 154 | var gl = this.gl; 155 | var debug_Velocity = false; 156 | var debug_enableProj = true; 157 | var debug_enableDiffuse = true; 158 | //Do simulation 159 | //Advection 160 | this.SetRenderTarget(this.m_texV2); 161 | this.DrawTexture(this.m_texV1, null, null, this.m_programAdvect, function (p) { 162 | var wgl = gl; 163 | var uDeltaTime = p['uDeltaTime']; 164 | gl.uniform1f(uDeltaTime, deltaTime); 165 | }); 166 | //Diffuse setup 167 | var dif_alpha = this.m_difAlpha_prec / deltaTime; //0.1 168 | var alpha = dif_alpha; 169 | var beta = alpha + 4; 170 | if (debug_enableDiffuse) { 171 | //copy v2 to v1 172 | this.RenderToTexture(this.m_texV2, this.m_texV1); 173 | //jacobi iteration 2D 174 | for (var i_1 = 0; i_1 < 20; i_1++) { 175 | this.SetRenderTarget(this.m_texV3); 176 | this.DrawTexture(this.m_texV2, this.m_texV1, null, this.m_programJacobi2D, function (p) { 177 | var wgl = gl; 178 | var ualpha = p['uAlpha']; 179 | var ubeta = p['uBeta']; 180 | wgl.uniform1f(ualpha, alpha); 181 | wgl.uniform1f(ubeta, beta); 182 | }); 183 | this.SetRenderTarget(this.m_texV2); 184 | this.DrawTexture(this.m_texV3, this.m_texV1, null, this.m_programJacobi2D, function (p) { 185 | var wgl = gl; 186 | var ualpha = p['uAlpha']; 187 | var ubeta = p['uBeta']; 188 | wgl.uniform1f(ualpha, alpha); 189 | wgl.uniform1f(ubeta, beta); 190 | }); 191 | } 192 | } 193 | //add external force V2->V3 194 | var forceX = 0; 195 | var forceY = 0; 196 | var force = this.m_force; 197 | if (this.m_inputOnDrag) { 198 | forceX = force * (this.m_inputX - this.m_inputX_pre); 199 | forceY = force * (this.m_inputY - this.m_inputY_pre); 200 | } 201 | else if (this.m_mouseDown) { 202 | force *= 0.1; 203 | var rvec = this.RandomVecIdentity(); 204 | forceX = force * rvec[0]; 205 | forceY = force * rvec[1]; 206 | this.m_mouseDown = false; 207 | } 208 | this.SetRenderTarget(this.m_texV3); 209 | this.DrawTexture(this.m_texV2, null, null, this.m_programForce, function (p) { 210 | var wgl = gl; 211 | var uForceExponent = p['uForceExponent']; 212 | var uForceOrigin = p['uForceOrigin']; 213 | var uForceVector = p['uForceVector']; 214 | wgl.uniform1f(uForceExponent, _this.m_exponent); 215 | wgl.uniform2f(uForceOrigin, _this.m_inputX, _this.m_inputY); 216 | wgl.uniform2f(uForceVector, forceX, forceY); 217 | }); 218 | this.RenderToTexture(this.m_texV3, this.m_texV1); 219 | //Proj setup V3->V1 220 | if (debug_enableProj) { 221 | this.SetRenderTarget(this.m_texV2); 222 | //V2(divergence of Velocity) 223 | this.DrawTexture(this.m_texV3, null, null, this.m_programProjSetup, null); 224 | //set P1 to 0 225 | this.SetRenderTarget(this.m_texP1); 226 | this.DrawColor([0, 0, 0, 0]); 227 | //Jacobi 1D 228 | var dx = this.m_dx; 229 | var alpha1d = -dx * dx; 230 | var beta1d = 4; 231 | for (var i = 0; i < 20; i++) { 232 | this.SetRenderTarget(this.m_texP2); 233 | this.DrawTexture(this.m_texP1, this.m_texV2, null, this.m_programJacobi1D, function (p) { 234 | var wgl = gl; 235 | wgl.uniform1f(p['uAlpha'], alpha1d); 236 | wgl.uniform1f(p['uBeta'], beta1d); 237 | }); 238 | this.SetRenderTarget(this.m_texP1); 239 | this.DrawTexture(this.m_texP2, this.m_texV2, null, this.m_programJacobi1D, function (p) { 240 | var wgl = gl; 241 | wgl.uniform1f(p['uAlpha'], alpha1d); 242 | wgl.uniform1f(p['uBeta'], beta1d); 243 | }); 244 | } 245 | //ProjFinish 246 | this.SetRenderTarget(this.m_texV1); 247 | this.DrawTexture(this.m_texP1, this.m_texV3, null, this.m_programProjFinish, null); 248 | } 249 | else { 250 | this.RenderToTexture(this.m_texV3, this.m_texV1); 251 | } 252 | //Use velocity to carry color 253 | if (debug_Velocity) { 254 | this.ResetFrameBuffer(); 255 | this.DrawTextureDefault(this.m_texV1); 256 | return; 257 | } 258 | else { 259 | this.SetRenderTarget(this.m_colRT2); 260 | this.DrawTexture(this.m_colRT1, this.m_texV1, null, this.m_programFluid, function (p) { 261 | var wgl = gl; 262 | wgl.uniform1f(p['uDeltaTime'], deltaTime); 263 | wgl.uniform1f(p['uTime'], ts / 1000.0); 264 | wgl.uniform1f(p['uForceExponent'], _this.m_exponent); 265 | if (_this.m_inputOnDrag) { 266 | wgl.uniform2f(p['uForceOrigin'], _this.m_inputX, _this.m_inputY); 267 | } 268 | else { 269 | wgl.uniform2f(p['uForceOrigin'], -10.0, -10.0); 270 | } 271 | }); 272 | this.ResetFrameBuffer(); 273 | this.DrawTextureDefault(this.m_colRT2); 274 | //swrap colorbuffer 275 | var temp = this.m_colRT1; 276 | this.m_colRT1 = this.m_colRT2; 277 | this.m_colRT2 = temp; 278 | } 279 | }; 280 | StableFluids.prototype.RandomVecIdentity = function () { 281 | var t = Math.random() * Math.PI * 2; 282 | var x = Math.sin(t); 283 | var y = Math.cos(t); 284 | return [x, y]; 285 | }; 286 | StableFluids.prototype.Clear = function () { 287 | var gl = this.gl; 288 | gl.clear(gl.COLOR_BUFFER_BIT); 289 | }; 290 | StableFluids.prototype.ResetFrameBuffer = function () { 291 | var gl = this.gl; 292 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 293 | }; 294 | StableFluids.prototype.SetRenderTarget = function (texture) { 295 | var gl = this.gl; 296 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_frameBuffer); 297 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); 298 | }; 299 | StableFluids.prototype.RenderToTexture = function (src, dest, flipY) { 300 | if (flipY === void 0) { flipY = false; } 301 | if (src === dest) { 302 | console.error('can not render to the same texture'); 303 | return; 304 | } 305 | this.SetRenderTarget(dest); 306 | this.DrawTextureDefault(src, flipY); 307 | }; 308 | StableFluids.prototype.DrawTextureDefault = function (tex0, flipY) { 309 | if (flipY === void 0) { flipY = false; } 310 | this.DrawTexture(tex0, null, null, flipY ? this.m_programDefaultFlipY : this.m_programDefault, null); 311 | }; 312 | StableFluids.prototype.DrawColor = function (color) { 313 | var gl = this.gl; 314 | gl.bindVertexArray(this.m_vaoQuad); 315 | gl.useProgram(this.m_programColor.Program); 316 | var ucolor = this.m_programColor['uColor']; 317 | gl.uniform4fv(ucolor, color); 318 | gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); 319 | }; 320 | StableFluids.prototype.DrawTexture = function (tex0, tex1, tex2, program, setUniform) { 321 | if (program == null) 322 | program = this.m_programDefault; 323 | var gl = this.gl; 324 | gl.bindVertexArray(this.m_vaoQuad); 325 | gl.useProgram(program.Program); 326 | if (tex0 != null) { 327 | gl.activeTexture(gl.TEXTURE0); 328 | gl.bindTexture(gl.TEXTURE_2D, tex0); 329 | var usampler0 = program['uSampler']; 330 | gl.uniform1i(usampler0, 0); 331 | } 332 | if (tex1 != null) { 333 | gl.activeTexture(gl.TEXTURE1); 334 | gl.bindTexture(gl.TEXTURE_2D, tex1); 335 | var usampler1 = program['uSampler1']; 336 | gl.uniform1i(usampler1, 1); 337 | } 338 | if (tex2 != null) { 339 | gl.activeTexture(gl.TEXTURE2); 340 | gl.bindTexture(gl.TEXTURE_2D, tex2); 341 | var usampler2 = program['uSampler2']; 342 | gl.uniform1i(usampler2, 2); 343 | } 344 | if (setUniform != null) { 345 | setUniform(program); 346 | } 347 | gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); 348 | }; 349 | return StableFluids; 350 | }()); 351 | exports.StableFluids = StableFluids; 352 | }); 353 | --------------------------------------------------------------------------------