<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Fluid Simulation</title> <link rel="stylesheet" type="text/css" href="dependencies/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="dependencies/flat-ui.min.css"> <link rel="stylesheet" type="text/css" href="main.css"> <script id="2d-vertex-shader" type="x-shader/x-vertex"> attribute vec2 a_position; void main() { gl_Position = vec4(a_position, 0, 1); } </script> <script id="boundaryShader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_texture; uniform float u_scale; uniform vec2 u_textureSize; void main() { vec2 fragCoord = gl_FragCoord.xy; if (fragCoord.x < 1.0){ gl_FragColor = u_scale*texture2D(u_texture, (fragCoord + vec2(1.0, 0.0))/u_textureSize); return; } else if (fragCoord.x >= u_textureSize.x-1.0){ gl_FragColor = u_scale*texture2D(u_texture, (fragCoord + vec2(-1.0, 0.0))/u_textureSize); return; } else if (fragCoord.y < 1.0){ gl_FragColor = u_scale*texture2D(u_texture, (fragCoord + vec2(0.0, 1.0))/u_textureSize); return; } else if (fragCoord.y >= u_textureSize.y-1.0){ gl_FragColor = u_scale*texture2D(u_texture, (fragCoord + vec2(0.0, -1.0))/u_textureSize); return; } gl_FragColor = texture2D(u_texture, (fragCoord)/u_textureSize); } </script> <script id="2d-render-shader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_material; uniform vec2 u_textureSize; void main() { vec2 fragCoord = gl_FragCoord.xy; vec3 background = vec3(0.96, 0.87, 0.68); vec3 material1 = vec3(0.925, 0, 0.55); vec3 material2 = vec3(0.0, 0.70, 0.63); vec3 material3 = vec3(0.52, 0.81, 0.70); vec3 material4 = vec3(1.0, 0.7, 0.07); float val = texture2D(u_material, fragCoord/u_textureSize).x/2.0; if (val > 1.0) val = 1.0; if (val < 0.0) val = 0.0; vec3 color = vec3(0.0); if (val < 0.25) { val *= 4.0; color = background*(1.0-val) + material1*val; } else if (val < 0.5) { val -= 0.25; val *= 4.0; color = material1*(1.0-val) + material2*val; } else if (val < 0.75) { val -= 0.50; val *= 4.0; color = material2*(1.0-val) + material3*val; } else { val -= 0.75; val *= 4.0; color = material3*(1.0-val) + material4*val; } gl_FragColor = vec4(color, 1); } </script> <script id="gradientSubtractionShader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_velocity; uniform sampler2D u_pressure; uniform vec2 u_textureSize; uniform float u_const; void main() { vec2 fragCoord = gl_FragCoord.xy; vec2 currentVelocity = texture2D(u_velocity, fragCoord/u_textureSize).xy; float n = texture2D(u_pressure, (fragCoord+vec2(0.0, 1.0))/u_textureSize).x; float s = texture2D(u_pressure, (fragCoord+vec2(0.0, -1.0))/u_textureSize).x; float e = texture2D(u_pressure, (fragCoord+vec2(1.0, 0.0))/u_textureSize).x; float w = texture2D(u_pressure, (fragCoord+vec2(-1.0, 0.0))/u_textureSize).x; gl_FragColor = vec4(currentVelocity-u_const*vec2(e-w, n-s), 0, 0); } </script> <script id="divergenceShader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_velocity; uniform vec2 u_textureSize; uniform float u_const; void main() { vec2 fragCoord = gl_FragCoord.xy; //finite difference formulation of divergence float n = texture2D(u_velocity, (fragCoord+vec2(0.0, 1.0))/u_textureSize).y; float s = texture2D(u_velocity, (fragCoord+vec2(0.0, -1.0))/u_textureSize).y; float e = texture2D(u_velocity, (fragCoord+vec2(1.0, 0.0))/u_textureSize).x; float w = texture2D(u_velocity, (fragCoord+vec2(-1.0, 0.0))/u_textureSize).x; float div = u_const*(e-w + n-s); gl_FragColor = vec4(div, 0, 0, 0); } </script> <script id="addMaterialShader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_material; uniform vec2 u_textureSize; uniform vec2 u_mouseCoord; uniform float u_mouseLength; uniform float u_mouseEnable; uniform float u_reciprocalRadius; void main() { vec2 fragCoord = gl_FragCoord.xy; float currentMaterial = texture2D(u_material, fragCoord/u_textureSize).x; if (u_mouseEnable == 1.0){ vec2 pxDist = fragCoord - u_mouseCoord; currentMaterial += u_mouseLength*0.1*exp(-(pxDist.x*pxDist.x+pxDist.y*pxDist.y)*u_reciprocalRadius); } if (currentMaterial > 0.0) currentMaterial -= 0.002;//material disappears over time gl_FragColor = vec4(currentMaterial, 0, 0, 0); } </script> <script id="forceShader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_velocity; uniform vec2 u_textureSize; uniform vec2 u_mouseCoord; uniform vec2 u_mouseDir; uniform float u_mouseEnable; uniform float u_reciprocalRadius; uniform float u_dt; void main() { vec2 fragCoord = gl_FragCoord.xy; vec2 currentVelocity = texture2D(u_velocity, fragCoord/u_textureSize).xy; if (u_mouseEnable == 1.0){ vec2 pxDist = fragCoord - u_mouseCoord; currentVelocity += u_mouseDir*u_dt*exp(-(pxDist.x*pxDist.x+pxDist.y*pxDist.y)*u_reciprocalRadius); } gl_FragColor = vec4(currentVelocity, 0, 0); } </script> <script id="jacobiShader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_b; uniform sampler2D u_x; uniform vec2 u_textureSize; uniform float u_alpha; uniform float u_reciprocalBeta; void main() { vec2 fragCoord = gl_FragCoord.xy; vec2 currentState = texture2D(u_b, fragCoord/u_textureSize).xy; //implicitly solve diffusion via jacobi iteration vec2 n = texture2D(u_x, (fragCoord+vec2(0.0, 1.0))/u_textureSize).xy; vec2 s = texture2D(u_x, (fragCoord+vec2(0.0, -1.0))/u_textureSize).xy; vec2 e = texture2D(u_x, (fragCoord+vec2(1.0, 0.0))/u_textureSize).xy; vec2 w = texture2D(u_x, (fragCoord+vec2(-1.0, 0.0))/u_textureSize).xy; vec2 nextState = (n + s + e + w + u_alpha * currentState) * u_reciprocalBeta; gl_FragColor = vec4(nextState, 0, 0); } </script> <script id="advectShader" type="x-shader/x-fragment"> precision mediump float; uniform sampler2D u_velocity; uniform sampler2D u_material; uniform vec2 u_textureSize; uniform float u_scale; uniform float u_dt; vec2 bilinearInterp(vec2 pos, sampler2D texture, vec2 size){ //bilinear interp between nearest cells vec2 pxCenter = vec2(0.5, 0.5); vec2 ceiled = ceil(pos); vec2 floored = floor(pos); vec2 n = texture2D(texture, (ceiled+pxCenter)/size).xy;//actually ne vec2 s = texture2D(texture, (floored+pxCenter)/size).xy;//actually sw if (ceiled.x != floored.x){ vec2 se = texture2D(texture, (vec2(ceiled.x, floored.y)+pxCenter)/size).xy; vec2 nw = texture2D(texture, (vec2(floored.x, ceiled.y)+pxCenter)/size).xy; n = n*(pos.x-floored.x) + nw*(ceiled.x-pos.x); s = se*(pos.x-floored.x) + s*(ceiled.x-pos.x); } vec2 materialVal = n; if (ceiled.y != floored.y){ materialVal = n*(pos.y-floored.y) + s*(ceiled.y-pos.y); } return materialVal; } void main() { vec2 fragCoord = gl_FragCoord.xy; vec2 pxCenter = vec2(0.5, 0.5); vec2 currentVelocity; if (u_scale == 1.0) currentVelocity = 1.0/u_scale*texture2D(u_velocity, fragCoord/u_textureSize).xy; else { vec2 scaledCoord = (fragCoord-pxCenter)*u_scale; vec2 scaledSize = u_textureSize*u_scale; currentVelocity = 1.0/u_scale*bilinearInterp(vec2(1.0, 1.0) + scaledCoord/scaledSize*(scaledSize-vec2(3.0, 3.0)), u_velocity, scaledSize); } //implicitly solve advection if (length(currentVelocity) == 0.0) {//no velocity gl_FragColor = vec4(texture2D(u_material, fragCoord/u_textureSize).xy, 0, 0); return; } vec2 pos = fragCoord - pxCenter - u_dt*currentVelocity; vec2 materialVal; //empty boundary if (pos.x < 0.0 || pos.x >= u_textureSize.x-1.0 || pos.y < 0.0 || pos.y >= u_textureSize.y-1.0) materialVal = vec2(0.0); else materialVal = bilinearInterp(pos, u_material, u_textureSize); gl_FragColor = vec4(materialVal, 0, 0); } </script> <script type="text/javascript" src="dependencies/jquery-3.1.0.min.js"></script> <script type="text/javascript" src="dependencies/flat-ui.min.js"></script> <script type="text/javascript" src="js/GLBoilerplate.js"></script> <script type="text/javascript" src="js/GPUMath.js"></script> <script type="text/javascript" src="js/main.js"></script> </head> <body> <canvas id="glcanvas"></canvas> <a href="#" id="about">?</a> <div class="modal fade" id="aboutModal" tabindex="-1" role="dialog" aria-labelledby="basicModal" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-body"> <b>Fluid Simulation Shader</b><br/><br/> I used the following sources to write this simulation:<br/><br/> <a href="https://pdfs.semanticscholar.org/84b8/c7b7eecf90ebd9d54a51544ca0f8ff93c137.pdf" target="_blank">Real-time ink simulation using a grid-particle method</a> - a method for real-time simulation of ink in water using a coarse-grained fluid simulation with a particle simulation on top.<br/> <a href="http://http.developer.nvidia.com/GPUGems/gpugems_ch38.html" target="_blank">Fast Fluid Dynamics Simulation on the GPU</a> - a very well written tutorial about programming the Navier-Stokes equations on a GPU.<br/> <a href="http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/ns.pdf" target="_blank">Stable Fluids</a> - a paper about stable numerical methods for evaluating Navier-Stokes on a discrete grid.<br/> <br/> By <a href="http://www.amandaghassaei.com/" target="_blank">Amanda Ghassaei</a>, code on <a href="https://github.com/amandaghassaei/FluidSimulation" target="_blank">Github</a>. <br/><br/> </div> </div> </div> </div> </body> </html>