Skip to content
Snippets Groups Projects
Commit b357ae13 authored by Hyung-Taik Choi's avatar Hyung-Taik Choi
Browse files

Initial commit

parent 54ed78b0
No related branches found
No related tags found
No related merge requests found
MIT License
Copyright (c) 2017 Pavel Dobryakov
Copyright (c) 2021 Hyung-Taik Choi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
# WebGL Fluid Simulation
[Play here](https://paveldogreat.github.io/WebGL-Fluid-Simulation/)
<img src="/screenshot.jpg?raw=true" width="880">
## References
http://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch38.html
https://github.com/mharrys/fluids-2d
https://github.com/haxiomic/GPU-Fluid-Experiments
## License
The code is available under the [MIT license](LICENSE)
# WebGL Fluid Simulation Tutorial
Source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -9,24 +9,12 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<link rel="apple-touch-icon" href="logo.png">
<link rel="icon" href="logo.png">
<title>WebGL Fluid Simulation</title>
<meta name="description" content="A WebGL fluid simulation that works in mobile browsers.">
<meta property="og:type" content="website">
<meta property="og:title" content="Webgl Fluid Simulation">
<meta property="og:description" content="A WebGL fluid simulation that works in mobile browsers.">
<meta property="og:url" content="https://paveldogreat.github.io/WebGL-Fluid-Simulation/">
<meta property="og:image" content="https://paveldogreat.github.io/WebGL-Fluid-Simulation/logo.png">
<script type="text/javascript" src="dat.gui.min.js"></script>
<!-- <script type="text/javascript" src="dat.gui.min.js"></script> -->
<style>
@font-face {
font-family: 'iconfont';
src: url('iconfont.ttf') format('truetype');
}
* {
user-select: none;
......@@ -49,176 +37,19 @@
height: 100%;
}
.dg {
opacity: 0.9;
}
.dg .property-name {
overflow: visible;
}
.bigFont {
font-size: 150%;
color: #8C8C8C;
}
.cr.function.appBigFont {
font-size: 150%;
line-height: 27px;
color: #A5F8D3;
background-color: #023C40;
}
.cr.function.appBigFont .property-name {
float: none;
}
.cr.function.appBigFont .icon {
position: sticky;
bottom: 27px;
}
.icon {
font-family: 'iconfont';
font-size: 130%;
float: right;
}
.twitter:before {
content: 'a';
}
.github:before {
content: 'b';
}
.app:before {
content: 'c';
}
.discord:before {
content: 'd';
}
.promo {
display: none;
/* display: table; */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
overflow: auto;
color: lightblue;
background-color: rgba(0,0,0,0.4);
animation: promo-appear-animation 0.35s ease-out;
}
.promo-middle {
display: table-cell;
vertical-align: middle;
}
.promo-content {
width: 80vw;
height: 80vh;
max-width: 80vh;
max-height: 80vw;
margin: auto;
padding: 0;
font-size: 2.8vmax;
font-family: Futura, "Trebuchet MS", Arial, sans-serif;
text-align: center;
background-image: url("promo_back.png");
background-position: center;
background-repeat: no-repeat;
background-size: cover;
border-radius: 15px;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}
.promo-header {
height: 10%;
padding: 2px 16px;
}
.promo-close {
width: 10%;
height: 100%;
text-align: left;
float: left;
font-size: 1.3em;
/* transition: 0.2s; */
}
.promo-close:hover {
/* transform: scale(1.25); */
cursor: pointer;
}
.promo-body {
padding: 8px 16px 16px 16px;
margin: auto;
}
.promo-body p {
margin-top: 0;
mix-blend-mode: color-dodge;
}
.link {
width: 100%;
display: inline-block;
}
.link img {
width: 100%;
}
@keyframes promo-appear-animation {
0% {
transform: scale(2.0);
opacity: 0;
}
100% {
transform: scale(1.0);
opacity: 1;
}
}
</style>
<script>
<!-- <script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-105392568-1', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics.js"></script>
<script async src="https://www.google-analytics.com/analytics.js"></script> -->
</head>
<body>
<canvas></canvas>
<!-- Mother of God, pls forgive me -->
<div class="promo">
<div class="promo-middle">
<div class="promo-content">
<div class="promo-header">
<span class="promo-close">&times;</span>
</div>
<div class="promo-body">
<p>Try Fluid Simulation app!</p>
<div class="links-container">
<a class="link" id="apple_link" target="_blank">
<img class="link-img" alt="Download on the App Store" src="app_badge.png"/>
</a>
<a class="link" id="google_link" target="_blank">
<img class="link-img" alt="Get it on Google Play" src="gp_badge.png"/>
</a>
</div>
</div>
</div>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
\ No newline at end of file
/*
MIT License
Copyright (c) 2017 Pavel Dobryakov
Copyright (c) 2021 Hyung-Taik Choi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......@@ -26,30 +26,30 @@ SOFTWARE.
// Mobile promo section
const promoPopup = document.getElementsByClassName('promo')[0];
const promoPopupClose = document.getElementsByClassName('promo-close')[0];
// const promoPopup = document.getElementsByClassName('promo')[0];
// const promoPopupClose = document.getElementsByClassName('promo-close')[0];
if (isMobile()) {
setTimeout(() => {
promoPopup.style.display = 'table';
}, 20000);
}
// if (isMobile()) {
// setTimeout(() => {
// promoPopup.style.display = 'table';
// }, 20000);
// }
promoPopupClose.addEventListener('click', e => {
promoPopup.style.display = 'none';
});
// promoPopupClose.addEventListener('click', e => {
// promoPopup.style.display = 'none';
// });
const appleLink = document.getElementById('apple_link');
appleLink.addEventListener('click', e => {
ga('send', 'event', 'link promo', 'app');
window.open('https://apps.apple.com/us/app/fluid-simulation/id1443124993');
});
// const appleLink = document.getElementById('apple_link');
// appleLink.addEventListener('click', e => {
// ga('send', 'event', 'link promo', 'app');
// window.open('https://apps.apple.com/us/app/fluid-simulation/id1443124993');
// });
const googleLink = document.getElementById('google_link');
googleLink.addEventListener('click', e => {
ga('send', 'event', 'link promo', 'app');
window.open('https://play.google.com/store/apps/details?id=games.paveldogreat.fluidsimfree');
});
// const googleLink = document.getElementById('google_link');
// googleLink.addEventListener('click', e => {
// ga('send', 'event', 'link promo', 'app');
// window.open('https://play.google.com/store/apps/details?id=games.paveldogreat.fluidsimfree');
// });
// Simulation section
......@@ -113,7 +113,7 @@ if (!ext.supportLinearFiltering) {
config.SUNRAYS = false;
}
startGUI();
// startGUI();
function getWebGLContext (canvas) {
const params = { alpha: true, depth: false, stencil: false, antialias: false, preserveDrawingBuffer: false };
......@@ -153,7 +153,7 @@ function getWebGLContext (canvas) {
formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);
}
ga('send', 'event', isWebGL2 ? 'webgl2' : 'webgl', formatRGBA == null ? 'not supported' : 'supported');
// ga('send', 'event', isWebGL2 ? 'webgl2' : 'webgl', formatRGBA == null ? 'not supported' : 'supported');
return {
gl,
......@@ -205,80 +205,40 @@ function supportRenderTextureFormat (gl, internalFormat, format, type) {
return status == gl.FRAMEBUFFER_COMPLETE;
}
function startGUI () {
var gui = new dat.GUI({ width: 300 });
gui.add(config, 'DYE_RESOLUTION', { 'high': 1024, 'medium': 512, 'low': 256, 'very low': 128 }).name('quality').onFinishChange(initFramebuffers);
gui.add(config, 'SIM_RESOLUTION', { '32': 32, '64': 64, '128': 128, '256': 256 }).name('sim resolution').onFinishChange(initFramebuffers);
gui.add(config, 'DENSITY_DISSIPATION', 0, 4.0).name('density diffusion');
gui.add(config, 'VELOCITY_DISSIPATION', 0, 4.0).name('velocity diffusion');
gui.add(config, 'PRESSURE', 0.0, 1.0).name('pressure');
gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
gui.add(config, 'SPLAT_RADIUS', 0.01, 1.0).name('splat radius');
gui.add(config, 'SHADING').name('shading').onFinishChange(updateKeywords);
gui.add(config, 'COLORFUL').name('colorful');
gui.add(config, 'PAUSED').name('paused').listen();
gui.add({ fun: () => {
splatStack.push(parseInt(Math.random() * 20) + 5);
} }, 'fun').name('Random splats');
let bloomFolder = gui.addFolder('Bloom');
bloomFolder.add(config, 'BLOOM').name('enabled').onFinishChange(updateKeywords);
bloomFolder.add(config, 'BLOOM_INTENSITY', 0.1, 2.0).name('intensity');
bloomFolder.add(config, 'BLOOM_THRESHOLD', 0.0, 1.0).name('threshold');
let sunraysFolder = gui.addFolder('Sunrays');
sunraysFolder.add(config, 'SUNRAYS').name('enabled').onFinishChange(updateKeywords);
sunraysFolder.add(config, 'SUNRAYS_WEIGHT', 0.3, 1.0).name('weight');
let captureFolder = gui.addFolder('Capture');
captureFolder.addColor(config, 'BACK_COLOR').name('background color');
captureFolder.add(config, 'TRANSPARENT').name('transparent');
captureFolder.add({ fun: captureScreenshot }, 'fun').name('take screenshot');
let github = gui.add({ fun : () => {
window.open('https://github.com/PavelDoGreat/WebGL-Fluid-Simulation');
ga('send', 'event', 'link button', 'github');
} }, 'fun').name('Github');
github.__li.className = 'cr function bigFont';
github.__li.style.borderLeft = '3px solid #8C8C8C';
let githubIcon = document.createElement('span');
github.domElement.parentElement.appendChild(githubIcon);
githubIcon.className = 'icon github';
let twitter = gui.add({ fun : () => {
ga('send', 'event', 'link button', 'twitter');
window.open('https://twitter.com/PavelDoGreat');
} }, 'fun').name('Twitter');
twitter.__li.className = 'cr function bigFont';
twitter.__li.style.borderLeft = '3px solid #8C8C8C';
let twitterIcon = document.createElement('span');
twitter.domElement.parentElement.appendChild(twitterIcon);
twitterIcon.className = 'icon twitter';
let discord = gui.add({ fun : () => {
ga('send', 'event', 'link button', 'discord');
window.open('https://discordapp.com/invite/CeqZDDE');
} }, 'fun').name('Discord');
discord.__li.className = 'cr function bigFont';
discord.__li.style.borderLeft = '3px solid #8C8C8C';
let discordIcon = document.createElement('span');
discord.domElement.parentElement.appendChild(discordIcon);
discordIcon.className = 'icon discord';
let app = gui.add({ fun : () => {
ga('send', 'event', 'link button', 'app');
window.open('http://onelink.to/5b58bn');
} }, 'fun').name('Check out mobile app');
app.__li.className = 'cr function appBigFont';
app.__li.style.borderLeft = '3px solid #00FF7F';
let appIcon = document.createElement('span');
app.domElement.parentElement.appendChild(appIcon);
appIcon.className = 'icon app';
if (isMobile())
gui.close();
}
// function startGUI () {
// var gui = new dat.GUI({ width: 300 });
// gui.add(config, 'DYE_RESOLUTION', { 'high': 1024, 'medium': 512, 'low': 256, 'very low': 128 }).name('quality').onFinishChange(initFramebuffers);
// gui.add(config, 'SIM_RESOLUTION', { '32': 32, '64': 64, '128': 128, '256': 256 }).name('sim resolution').onFinishChange(initFramebuffers);
// gui.add(config, 'DENSITY_DISSIPATION', 0, 4.0).name('density diffusion');
// gui.add(config, 'VELOCITY_DISSIPATION', 0, 4.0).name('velocity diffusion');
// gui.add(config, 'PRESSURE', 0.0, 1.0).name('pressure');
// gui.add(config, 'CURL', 0, 50).name('vorticity').step(1);
// gui.add(config, 'SPLAT_RADIUS', 0.01, 1.0).name('splat radius');
// gui.add(config, 'SHADING').name('shading').onFinishChange(updateKeywords);
// gui.add(config, 'COLORFUL').name('colorful');
// gui.add(config, 'PAUSED').name('paused').listen();
// gui.add({ fun: () => {
// splatStack.push(parseInt(Math.random() * 20) + 5);
// } }, 'fun').name('Random splats');
// let bloomFolder = gui.addFolder('Bloom');
// bloomFolder.add(config, 'BLOOM').name('enabled').onFinishChange(updateKeywords);
// bloomFolder.add(config, 'BLOOM_INTENSITY', 0.1, 2.0).name('intensity');
// bloomFolder.add(config, 'BLOOM_THRESHOLD', 0.0, 1.0).name('threshold');
// let sunraysFolder = gui.addFolder('Sunrays');
// sunraysFolder.add(config, 'SUNRAYS').name('enabled').onFinishChange(updateKeywords);
// sunraysFolder.add(config, 'SUNRAYS_WEIGHT', 0.3, 1.0).name('weight');
// let captureFolder = gui.addFolder('Capture');
// captureFolder.addColor(config, 'BACK_COLOR').name('background color');
// captureFolder.add(config, 'TRANSPARENT').name('transparent');
// captureFolder.add({ fun: captureScreenshot }, 'fun').name('take screenshot');
// if (isMobile())
// gui.close();
// }
function isMobile () {
return /Mobi|Android/i.test(navigator.userAgent);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment