summaryrefslogtreecommitdiff
path: root/light-fluid-simulation
diff options
context:
space:
mode:
authors1n <[email protected]>2019-10-07 18:17:38 -0700
committers1n <[email protected]>2019-10-07 18:17:38 -0700
commita7480ac79b5810c5b2689976ce06845c813e557d (patch)
treee6af3a78934ea632e0936517e41b4f9b20e53802 /light-fluid-simulation
parentadd halloween egg to dark mode, dark mode docs (diff)
downloads1n.pw-admin-a7480ac79b5810c5b2689976ce06845c813e557d.tar.xz
s1n.pw-admin-a7480ac79b5810c5b2689976ce06845c813e557d.zip
add light fluid simulation
Diffstat (limited to 'light-fluid-simulation')
-rw-r--r--light-fluid-simulation/css/main.css25
-rw-r--r--light-fluid-simulation/index.html21
-rw-r--r--light-fluid-simulation/js/main.js386
3 files changed, 432 insertions, 0 deletions
diff --git a/light-fluid-simulation/css/main.css b/light-fluid-simulation/css/main.css
new file mode 100644
index 0000000..32eefc3
--- /dev/null
+++ b/light-fluid-simulation/css/main.css
@@ -0,0 +1,25 @@
+@import url('https://fonts.googleapis.com/css?family=Open+Sans:600&display=swap');
+
+* {
+ margin: 0;
+ border: 0;
+ box-sizing:
+ border-box;
+}
+
+body {
+
+ margin: 0;
+ position: fixed;
+ overflow: hidden;
+ background-color: #090d13;
+ user-select: none;
+
+}
+
+canvas {
+
+ width: 100%;
+ height: 100%;
+
+} \ No newline at end of file
diff --git a/light-fluid-simulation/index.html b/light-fluid-simulation/index.html
new file mode 100644
index 0000000..cf6c05e
--- /dev/null
+++ b/light-fluid-simulation/index.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
+ <title>s1nical - Light Fluid Simulation</title>
+ <!-- CSS Links -->
+ <link rel="stylesheet" href="/light-fluid-simulation/css/main.css">
+ <!-- External Libraries -->
+ <script src="https://threejs.org/build/three.js"></script>
+ <!-- Invisible Script Links -->
+ </head>
+
+ <body>
+ <!-- Visable Script Links -->
+ <script src="/light-fluid-simulation/js/main.js"></script>
+ </body>
+
+</html> \ No newline at end of file
diff --git a/light-fluid-simulation/js/main.js b/light-fluid-simulation/js/main.js
new file mode 100644
index 0000000..f6c81bd
--- /dev/null
+++ b/light-fluid-simulation/js/main.js
@@ -0,0 +1,386 @@
+//
+// shaders
+//
+const basic_vert = `
+precision highp float;
+
+void main() {
+ gl_Position = vec4( position, vec2(1.0) );
+}
+`;
+
+const prep_frag = `
+precision highp float;
+
+void main() {
+ gl_FragColor.z = 0.012;
+}
+`;
+
+const physics_frag = `
+precision highp float;
+
+uniform vec3 mouse;
+uniform vec3 pmouse;
+uniform vec2 resolution;
+uniform sampler2D texture;
+
+float distToSegment( vec2 x1, vec2 x2, vec2 p ) {
+
+ vec2 v = x2 - x1;
+ vec2 w = p - x1;
+
+ float c1 = dot(w,v);
+ float c2 = dot(v,v);
+
+ // if c2 <= c1 == c1
+ // if c2 > c1 == c2
+ float div = mix( c2, c1, step( c2, c1 ) );
+
+ // if c1 < 0 == 0.0
+ float mult = step( 0.0, c1 );
+
+ float b = c1 * mult / div;
+ vec2 pb = x1 + b*v;
+
+ return distance( p, pb );
+
+}
+
+vec3 computeNormal( vec4 n ) {
+
+ // pixel scale
+ vec2 un = 1. / resolution;
+ vec2 uv = gl_FragCoord.xy * un;
+
+ // tex sample neighbour-4;
+ vec3 n_r = texture2D( texture, uv + vec2( 1, 0 ) * un ).xyz;
+ vec3 n_l = texture2D( texture, uv - vec2( 1, 0 ) * un ).xyz;
+ vec3 n_u = texture2D( texture, uv + vec2( 0, 1 ) * un ).xyz;
+ vec3 n_d = texture2D( texture, uv - vec2( 0, 1 ) * un ).xyz;
+
+ // partial differences n-4;
+ vec4 dn = vec4( n.z );
+ dn -= vec4( n_r.z, n_l.z, n_u.z, n_d.z );
+
+ // right - left, up - down;
+ vec2 xy = vec2( dn.x - dn.y, dn.z - dn.w );
+ xy += n_r.xy + n_l.xy + n_u.xy + n_d.xy;
+ xy *= 0.972; // energy dissipation
+
+ float z;
+ z += dot( n_r.xy, - vec2( 1, 0 ) );
+ z += dot( n_l.xy, + vec2( 1, 0 ) );
+ z += dot( n_u.xy, - vec2( 0, 1 ) );
+ z += dot( n_d.xy, + vec2( 0, 1 ) );
+
+ return vec3( xy , z ) * 0.25;
+
+}
+
+
+void main() {
+
+ vec2 uv = gl_FragCoord.xy / resolution;
+ float asp = resolution.x / resolution.y; // aspect
+
+ // normal sampling
+ vec4 h = texture2D( texture, uv );
+
+ // previous velocity
+ float vel = h.a;
+ // apply elastic-viscous acceleration
+ // acc = - offset*elasticity - vel*viscosity
+ vel += - ( h.z - 0.012 ) * 0.016 - vel * 0.059;
+
+ // compute normal advection
+ vec3 f = computeNormal( h );
+ f.z += h.z + vel;
+
+ // mouse interaction - continuous distance from mouse
+ float dist = distToSegment(
+ vec2( pmouse.x * asp, pmouse.y), // previous mouse
+ vec2( mouse.x * asp, mouse.y), // current mouse
+ vec2( uv.x * asp, uv.y) // fragcoord
+ );
+
+ float mSize = 0.065; // mouse radius
+ float peak = 0.9; // max-height
+
+ float isDisp = step( 0.5, mouse.z ); // is displaced
+
+ if ( mouse.z > 0.5 && dist <= mSize ) {
+
+ float dst = ( mSize - dist ) / mSize;
+ f.z += pow( abs(dst), 1.9 ) * peak * 2.5;
+ f.xy -= f.xy * pow( abs(dst), 3.9 ) * 0.1;
+ f.z = min( peak, f.z );
+
+ }
+
+ gl_FragColor = clamp( vec4( f, vel ), -1.0, 1.0);
+
+}
+`;
+
+const light_frag = `
+precision highp float;
+
+#define RECIPROCAL_PI 0.31830988618
+
+uniform vec2 resolution;
+uniform sampler2D texture;
+
+float rand(vec2 co){
+ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
+}
+
+// based on https://www.shadertoy.com/view/MslGR8
+vec3 dithering( vec3 color ) {
+ //Calculate grid position
+ float grid_position = rand( gl_FragCoord.xy );
+ //Shift the individual colors differently, thus making it even harder to see the dithering pattern
+ vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );
+ //modify shift acording to grid position.
+ dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );
+ //shift the color by dither_shift
+ return color + dither_shift_RGB;
+}
+
+void main() {
+
+ vec2 uv = gl_FragCoord.xy / resolution.xy;
+
+ vec3 N = texture2D( texture, uv ).xyz;
+
+ vec3 viewPos = vec3( 0.0, 0.0, 1.2 );
+ vec3 lightPos = vec3( 0.0, 1.5, 0.98 );
+ vec3 fragPos = vec3( ( 2.0 * uv - 1.0 ), N.z );
+
+ vec3 L = normalize( lightPos - fragPos );
+ vec3 H = normalize( L + normalize( viewPos - fragPos ) );
+ vec3 dN = vec3( N.xy, N.z/2.0 + 0.28 );
+
+ float dif = max( dot( dN, L ), 0.0 );
+ float spec = clamp( dot( normalize(N), H ), 0.0, 1.0 );
+
+ float attenuation = 1.0 - length( lightPos - fragPos ) / 3.1;
+ vec3 dif_int = vec3( dif * 0.056 * attenuation );
+
+ float shininess = 4.8;
+ float ref = RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( spec, shininess );
+ vec3 spec_int = vec3( ref * 0.38 * pow( attenuation, 3.0 ) );
+
+ vec3 col = dif_int + spec_int;
+
+ col = pow( col, vec3( 1.0 / 2.2 ) );
+
+ col.r = mix( col.r * 1.28, col.r, length( dif_int ) * 1.2 / 3.0 );
+ // col += 0.045;
+
+ gl_FragColor = vec4( dithering(col), 1.0 );
+
+}
+`;
+
+
+//
+// three.js setup
+//
+const w = window.innerWidth;
+const h = window.innerHeight;
+const res = new THREE.Vector2(w, h);
+const mousecoord = new THREE.Vector3();
+const mouse = new THREE.Vector3();
+const pmouse = new THREE.Vector3();
+
+var renderer = new THREE.WebGLRenderer();
+renderer.setSize(w, h);
+document.body.appendChild(renderer.domElement);
+
+const scene = new THREE.Scene();
+const camera = new THREE.Camera();
+
+
+// render targets
+let rtt = new THREE.WebGLRenderTarget(w, h, {
+ minFilter: THREE.LinearFilter,
+ magFilter: THREE.LinearFilter,
+ format: THREE.RGBAFormat,
+ type: (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) ? THREE.HalfFloatType : THREE.FloatType,
+ depthTest: false,
+ depthBuffer: false,
+ stencilBuffer: false
+});
+
+let rtt2 = rtt.clone();
+
+
+//
+// materials
+//
+const copyMaterial = new THREE.ShaderMaterial({
+ vertexShader: basic_vert,
+ fragmentShader: prep_frag,
+ blending: THREE.NoBlending,
+ transparent: false,
+ fog: false,
+ lights: false,
+ depthWrite: false,
+ depthTest: false
+});
+
+const physicsMaterial = new THREE.ShaderMaterial({
+ uniforms: {
+ mouse: { type: 'v3', value: mouse },
+ pmouse: { type: 'v3', value: pmouse },
+ resolution: { type: 'v2', value: res },
+ texture: { type: 't' },
+ },
+ vertexShader: basic_vert,
+ fragmentShader: physics_frag,
+ blending: THREE.NoBlending,
+ transparent: false,
+ fog: false,
+ lights: false,
+ depthWrite: false,
+ depthTest: false
+});
+
+const lightsMaterial = new THREE.ShaderMaterial({
+ uniforms: {
+ resolution: { type: 'v2', value: res },
+ texture: { type: 't' },
+ },
+ vertexShader: basic_vert,
+ fragmentShader: light_frag,
+ blending: THREE.NoBlending,
+ transparent: false,
+ fog: false,
+ lights: false,
+ depthWrite: false,
+ depthTest: false
+});
+
+
+//
+// mesh setup
+//
+const geometry = new THREE.BufferGeometry();
+const vertices = new Float32Array([
+ -1.0, -1.0,
+ 3.0, -1.0,
+ -1.0, 3.0
+]);
+
+geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 2));
+
+mesh = new THREE.Mesh(geometry, copyMaterial);
+mesh.frustumCulled = false;
+scene.add(mesh);
+
+
+//
+// pre-render to rtt
+//
+
+renderer.setRenderTarget(rtt);
+renderer.render(scene, camera);
+
+renderer.setRenderTarget(rtt2);
+renderer.render(scene, camera);
+
+mesh.material = physicsMaterial;
+
+
+//
+// listeners
+//
+
+renderer.domElement.addEventListener('mousemove', mousemove);
+renderer.domElement.addEventListener('mouseout', mouseout);
+
+renderer.domElement.addEventListener('touchmove', touch);
+renderer.domElement.addEventListener('touchstart', touch);
+renderer.domElement.addEventListener('touchend', touchend);
+
+window.addEventListener('resize', resize);
+
+renderer.setAnimationLoop(function () {
+
+ render();
+
+});
+
+function render() {
+
+ const tmp = rtt;
+ rtt = rtt2;
+ rtt2 = tmp;
+
+ pmouse.copy(mouse);
+ mouse.copy(mousecoord);
+
+ if (pmouse.z == 0) pmouse.copy(mouse);
+
+ mesh.material = physicsMaterial;
+ mesh.material.uniforms.texture.value = rtt2.texture;
+
+ renderer.setRenderTarget(rtt);
+ renderer.render(scene, camera);
+
+ mesh.material = lightsMaterial;
+ mesh.material.uniforms.texture.value = rtt.texture;
+
+ renderer.setRenderTarget(null);
+ renderer.render(scene, camera);
+
+}
+
+function resize() {
+
+ const h = window.innerHeight;
+ const w = window.innerWidth;
+
+ res.set(w, h);
+
+ camera.aspect = w / h;
+
+ rtt.setSize(w, h);
+ rtt2.setSize(w, h);
+
+ renderer.setSize(w, h);
+
+}
+
+function mousemove(evt) {
+
+ mousecoord.x = evt.pageX / window.innerWidth;
+ mousecoord.y = 1 - (evt.pageY / window.innerHeight);
+ mousecoord.z = 1;
+
+}
+
+
+function mouseout(evt) {
+
+ mousecoord.z = 0;
+
+}
+
+function touch(evt) {
+
+ evt.preventDefault();
+
+ mousecoord.x = evt.touches[0].pageX / window.innerWidth;
+ mousecoord.y = 1 - evt.touches[0].pageY / window.innerHeight;
+ mousecoord.z = 1;
+
+}
+
+
+function touchend(evt) {
+
+ mousecoord.z = 0;
+
+} \ No newline at end of file