Commit 983c1d00 authored by amandaghassaei's avatar amandaghassaei

setup

parents
//from http://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
/**
* Creates and compiles a shader.
*
* @param {!WebGLRenderingContext} gl The WebGL Context.
* @param {string} shaderSource The GLSL source code for the shader.
* @param {number} shaderType The type of shader, VERTEX_SHADER or
* FRAGMENT_SHADER.
* @return {!WebGLShader} The shader.
*/
function compileShader(gl, shaderSource, shaderType) {
// Create the shader object
var shader = gl.createShader(shaderType);
// Set the shader source code.
gl.shaderSource(shader, shaderSource);
// Compile the shader
gl.compileShader(shader);
// Check if it compiled
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!success) {
// Something went wrong during compilation; get the error
throw "could not compile shader:" + gl.getShaderInfoLog(shader);
}
return shader;
}
/**
* Creates a program from 2 shaders.
*
* @param {!WebGLRenderingContext) gl The WebGL context.
* @param {!WebGLShader} vertexShader A vertex shader.
* @param {!WebGLShader} fragmentShader A fragment shader.
* @return {!WebGLProgram} A program.
*/
function createProgram(gl, vertexShader, fragmentShader) {
// create a program.
var program = gl.createProgram();
// attach the shaders.
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// link the program.
gl.linkProgram(program);
// Check if it linked.
var success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!success) {
// something went wrong with the link
throw ("program filed to link:" + gl.getProgramInfoLog (program));
}
return program;
}
/**
* Creates a shader from the content of a script tag.
*
* @param {!WebGLRenderingContext} gl The WebGL Context.
* @param {string} scriptId The id of the script tag.
* @param {string} opt_shaderType. The type of shader to create.
* If not passed in will use the type attribute from the
* script tag.
* @return {!WebGLShader} A shader.
*/
function createShaderFromScript(gl, scriptId, opt_shaderType) {
// look up the script tag by id.
var shaderScript = document.getElementById(scriptId);
if (!shaderScript) {
throw("*** Error: unknown script element" + scriptId);
}
// extract the contents of the script tag.
var shaderSource = shaderScript.text;
// If we didn't pass in a type, use the 'type' from
// the script tag.
if (!opt_shaderType) {
if (shaderScript.type == "x-shader/x-vertex") {
opt_shaderType = gl.VERTEX_SHADER;
} else if (shaderScript.type == "x-shader/x-fragment") {
opt_shaderType = gl.FRAGMENT_SHADER;
} else if (!opt_shaderType) {
throw("*** Error: shader type not set");
}
}
return compileShader(gl, shaderSource, opt_shaderType);
}
/**
* Creates a program from 2 script tags.
*
* @param {!WebGLRenderingContext} gl The WebGL Context.
* @param {string} vertexShaderId The id of the vertex shader script tag.
* @param {string} fragmentShaderId The id of the fragment shader script tag.
* @return {!WebGLProgram} A program
*/
function createProgramFromScripts(
gl, vertexShaderId, fragmentShaderId) {
var vertexShader = createShaderFromScript(gl, vertexShaderId);
var fragmentShader = createShaderFromScript(gl, fragmentShaderId);
return createProgram(gl, vertexShader, fragmentShader);
}
\ No newline at end of file
# ReactionDiffusionShader
WebGL Shader for a Gray-Scott reaction diffusion system
<img src="https://raw.githubusercontent.com/amandaghassaei/ReactionDiffusionShader/master/img.png"/>
Live demo at <a href="http://git.amandaghassaei.com/ReactionDiffusionShader/" target="_blank">git.amandaghassaei.com/ReactionDiffusionShader/</a>
This is a simulation of a <a href="https://en.wikipedia.org/wiki/Reaction%E2%80%93diffusion_system" target="_blank">Gray-Scott reaction-diffusion system</a>, running in a GPU shader.
The parameters for this model are F = 0.0545 and K = 0.062. With these parameters the system forms a mitosis-like pattern, where small cells divide and spread across space.<br/><br/>
Reaction diffusion patterns are interesting, but they can be difficult to control in meaningful ways for design purposes.
In this project I added an underlying vector field that controls the orientation of diffusion across the system to produce directed, global patterns.
Click on the screen to change the location of the sinks in the vector field.<br/>
<br/>
If this simulation is not performing well on your computer, resize your browser's window to make the simulation smaller.<br/><br/>
The math used to created oriented diffusion is this system is discussed in the papers <a href="https://www.sci.utah.edu/publications/SCITechReports/UUSCI-2003-002.pdf" target="_blank">Display of Vector Fields Using a Reaction-Diffusion Model</a>
and <a href="http://www.cs.cmu.edu/~jkh/462_s07/reaction_diffusion.pdf" target="_blank">Reaction-Diffusion Textures</a>.<br/><br/>
More info about ways to control these systems can also be found on <a href="http://www.karlsims.com/rd.html" target="_blank">Karl Sims' Webpage</a>.<br/><br/>
Information about programming a reaction diffusion system on the GPU is here: <a href="https://bl.ocks.org/robinhouston/ed597847175cf692ecce" target="_blank">A reaction-diffusion simulation using WebGL</a>.
<br/><br/>
By <a href="http://www.amandaghassaei.com/" target="_blank">Amanda Ghassaei</a>, code on <a href="https://github.com/amandaghassaei/ReactionDiffusionShader" target="_blank">Github</a>.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
<!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="2d-render-shader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_textureSize;
const float COLOR_MIN = 0.2, COLOR_MAX = 0.4;
void main() {
float v = (COLOR_MAX - texture2D(u_image, gl_FragCoord.xy / u_textureSize)).y / (COLOR_MAX - COLOR_MIN);
gl_FragColor = vec4(v, v, v, 1);
}
</script>
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
#define M_PI 3.1415926535897932384626433832795
//texture array
uniform sampler2D u_image;//u, v, --, -- = r, g, b, a
uniform vec2 u_textureSize;
uniform vec2 u_mouseCoord;
uniform float u_mouseEnable;
const float dt = 1.0;
void main() {
vec2 fragCoord = gl_FragCoord.xy;
vec2 currentState = texture2D(u_image, fragCoord/u_textureSize).xy;
float u = currentState.x;
float v = currentState.y;
vec2 n = texture2D(u_image, (fragCoord + vec2(0.0, 1.0))/u_textureSize).xy;//north
vec2 s = texture2D(u_image, (fragCoord + vec2(0.0, -1.0))/u_textureSize).xy;//south
vec2 e = texture2D(u_image, (fragCoord + vec2(-1.0, 0.0))/u_textureSize).xy;//east
vec2 w = texture2D(u_image, (fragCoord + vec2(1.0, 0.0))/u_textureSize).xy;//west
if (u_mouseEnable == 1.0){
vec2 pxDistFromMouse = (fragCoord - u_mouseCoord)*(fragCoord - u_mouseCoord);
float tol = 10.0;
if (length(pxDistFromMouse)<tol){
gl_FragColor = vec4(0.5, 0.35, 0, 1);
return;
}
}
gl_FragColor = vec4(0,0, 0, 1);
}
</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="GlBoilerplate.js"></script>
<script type="text/javascript" src="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/>
<br/><br/>
By <a href="http://www.amandaghassaei.com/" target="_blank">Amanda Ghassaei</a>, code on <a href="https://github.com/amandaghassaei/ReactionDiffusionShader" target="_blank">Github</a>.
<br/><br/>
</div>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
html {
width:100%;
height:100%;
}
body {
padding:0;
margin:0;
width:100%;
height:100%;
overflow: hidden;
}
canvas {
width:100%;
height:100%;
overflow: hidden;
}
#about{
position: absolute;
z-index: 5;
top:0;
right:0;
background: #444;
width: 30px;
text-align: center;
position: absolute;
color: #fff;
}
.sliderInput{
float:right;
display: inline-block;
margin-bottom:15px;
}
.sliderContainer{
display: block;
margin-bottom:15px;
}
.indent{
padding-left:20px;
}
a, a:hover, a:visited, a:focus{
color:#8cbaed;
}
a.btn:visited, a.btn:focus{
color: white;
}
#controls .radio{
height: 42px;
display: inline-block;
width: 100%;
}
input.form-control {
width: 80px;
display: inline-block;
}
.radioSlider{
display: inline-block;
float:right;
}
.floatRight{
float:right;
}
.modal-body li{
margin-bottom:10px;
}
.slider.slider-horizontal {
width: 200px;
margin: 0;
padding: 20px;
top: 10px;
}
.slider.slider-vertical {
height: 300px;
margin: 0;
padding: 20px;
}
.flat-slider.ui-corner-all,
.flat-slider .ui-corner-all {
border-radius: 0;
}
.flat-slider.ui-slider {
border: 0;
background: #ffffff;
border-radius: 7px;
}
.flat-slider.ui-slider-horizontal {
height: 6px;
}
.flat-slider.ui-slider-vertical {
width: 6px;
height:300px;
}
.flat-slider .ui-slider-handle {
width: 20px;
height: 20px;
background: #666;
border-radius: 50%;
border: none;
cursor: pointer;
}
.flat-slider.ui-slider-vertical .ui-slider-handle{
background: #8cbaed;
}
.flat-slider.ui-slider-horizontal .ui-slider-handle {
top: 50%;
margin-top: -10px;
opacity: 0.8;
}
.flat-slider.ui-slider-vertical .ui-slider-handle {
left: 50%;
margin-left: -10px;
opacity: 0.8;
}
.flat-slider .ui-slider-handle:hover {
opacity: 0.7;
}
.flat-slider .ui-slider-handle:focus { outline: none; }
.flat-slider{
vertical-align: middle;
display: inline-block;
width: 130px;
position: relative;
margin: 10px 10px 10px 10px;
/*padding: 4px 0;*/
}
.modal-content{
border-radius: 0;
}
.modal-body{
padding: 20px 35px;
padding-top: 40px;
}
span.modal-close{
display: none;
}
.titleSpan{
display: inline-block;
width:100%;
margin-top:15px;
}
.form-control:focus{
border-color: #666;
}
.checkbox input.custom-checkbox[type="checkbox"]:checked + .icons .icon-checked, .radio input.custom-checkbox[type="checkbox"]:checked + .icons .icon-checked, .checkbox input.custom-radio[type="radio"]:checked + .icons .icon-checked, .radio input.custom-radio[type="radio"]:checked + .icons .icon-checked {
color: #666;
}
.radio .icon-checked{
transition-duration: 0s;
}
.radio .icon-unchecked{
transition-duration: 0s;
}
#controls .checkbox input.custom-radio[type="radio"]:checked + .icons, #controls .radio input.custom-radio[type="radio"]:checked + .icons {
margin-top:9px;
}
#controls .radio .icons {
margin-top:9px;
}
.sliderInput>.flat-slider{
width: 132px;
}
.label-slider{
font-size: 15px;
}
.extraSpace{
display: inline-block;
width:100%;
height:15px;
}
.flipped
{
direction: rtl;
}
.flipped>div
{
direction: ltr;
}
.paddingBottom {
margin-bottom: 20px;
}
\ No newline at end of file
//used a lot of ideas from https://bl.ocks.org/robinhouston/ed597847175cf692ecce to clean this code up
var gl;
var canvas;
var frameBuffers;
var states = [null, null];
var resizedLastState;
var resizedCurrentState;
var width;
var height;
var stepProgram;
var renderProgram;
var textureSizeLocation;
var textureSizeLocationRender;
var mouseCoordLocation;
var mouseCoordinates = [null, null];
var mouseEnableLocation;
var mouseEnable = true;
var paused = false;//while window is resizing
window.onload = initGL;
function initGL() {
$("#about").click(function(e){
e.preventDefault();
$("#aboutModal").modal('show');
});
// Get A WebGL context
canvas = document.getElementById("glcanvas");
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
canvas.onmousemove = onMouseMove;
canvas.onmousedown = onMouseDown;
// canvas.onmouseup = onMouseUp;
canvas.onmouseout = onMouseUp;
canvas.onmouseenter = function(){
mouseEnable = 1;
};
window.onresize = onResize;
gl = canvas.getContext("webgl", {antialias:false}) || canvas.getContext("experimental-webgl", {antialias:false});
if (!gl) {
alert('Could not initialize WebGL, try another browser');
return;
}
gl.disable(gl.DEPTH_TEST);
gl.getExtension('OES_texture_float');
// setup a GLSL program
stepProgram = createProgramFromScripts(gl, "2d-vertex-shader", "2d-fragment-shader");
renderProgram = createProgramFromScripts(gl, "2d-vertex-shader", "2d-render-shader");
gl.useProgram(renderProgram);
loadVertexData(gl, renderProgram);
textureSizeLocationRender = gl.getUniformLocation(renderProgram, "u_textureSize");
gl.useProgram(stepProgram);
loadVertexData(gl, stepProgram);
textureSizeLocation = gl.getUniformLocation(stepProgram, "u_textureSize");
mouseCoordLocation = gl.getUniformLocation(stepProgram, "u_mouseCoord");
mouseEnableLocation = gl.getUniformLocation(stepProgram, "u_mouseEnable");
frameBuffers = [makeFrameBuffer(), makeFrameBuffer()];
resetWindow();
gl.bindTexture(gl.TEXTURE_2D, states[0]);//original texture
render();
}
function loadVertexData(gl, program) {
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -1,-1, 1,-1, -1, 1, 1, 1]), gl.STATIC_DRAW);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
}
function makeFrameBuffer(){
return gl.createFramebuffer();
}
function makeRandomArray(rgba){
for (var x=0;x<width;x++) {
for (var y=0;y<height;y++) {
var ii = (y*width + x) * 4;
var central_square = (x > width/2-10 && x < width/2+10 && y > height/2-10 && y < height/2+10);
if (central_square) {
rgba[ii] = 0.5 + Math.random() * 0.02 - 0.01;
rgba[ii + 1] = 0.25 + Math.random() * 0.02 - 0.01;
} else {
rgba[ii] = 1.0;
rgba[ii + 1] = 0;
}
}
}
return rgba;
}
function makeTexture(gl, data){
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, data);
return texture;
}
function render(){
if (!paused) {
gl.useProgram(stepProgram);
if (mouseEnable){
gl.uniform1f(mouseEnableLocation, 1);
gl.uniform2f(mouseCoordLocation, mouseCoordinates[0], mouseCoordinates[1]);
} else gl.uniform1f(mouseEnableLocation, 0);
if (resizedLastState) {
states[0] = resizedLastState;
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffers[0]);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, states[0], 0);
resizedLastState = null;
}
if (resizedCurrentState) {
states[1] = resizedCurrentState;
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffers[1]);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, states[1], 0);
resizedCurrentState = null;
}
for (var i=0;i<2;i++) {
if (paused) {
window.requestAnimationFrame(render);
return;
}
step(i);
}
gl.useProgram(renderProgram);
//draw to canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, states[0]);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
} else resetWindow();
window.requestAnimationFrame(render);
}
function step(i){
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffers[(i+1)%2]);
gl.bindTexture(gl.TEXTURE_2D, states[i%2]);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);//draw to framebuffer
}
function onResize(){
paused = true;
}
function resetWindow(){
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
width = canvas.clientWidth;
height = canvas.clientHeight;
gl.viewport(0, 0, width, height);
// set the size of the texture
gl.useProgram(stepProgram);
gl.uniform2f(textureSizeLocation, width, height);
gl.useProgram(renderProgram);
gl.uniform2f(textureSizeLocationRender, width, height);
//texture for saving output from frag shader
resizedCurrentState = makeTexture(gl, null);
//fill with random pixels
var rgba = new Float32Array(width*height*4);
resizedLastState = makeTexture(gl, makeRandomArray(rgba));
paused = false;
}