├── .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 | 
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 |
--------------------------------------------------------------------------------