Commit abfba202 authored by amandaghassaei's avatar amandaghassaei

reworking gpu stuff

parent 900122ea
/**
* Created by ghassaei on 2/24/16.
*/
function initGPUMath(){
var glBoilerplate = initBoilerPlate();
var canvas = document.getElementById("glcanvas");
var gl = canvas.getContext("webgl", {antialias:false}) || canvas.getContext("experimental-webgl", {antialias:false});
gl.getExtension('OES_texture_float');
gl.disable(gl.DEPTH_TEST);
var maxTexturesInFragmentShader = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
console.log(maxTexturesInFragmentShader + " textures max");
function GPUMath(){
this.reset();
}
GPUMath.prototype.createProgram = function(programName, vertexShader, fragmentShader){
var programs = this.programs;
var program = programs[programName];
if (program) {
console.warn("already a program with the name " + programName);
return;
}
program = glBoilerplate.createProgramFromScripts(gl, vertexShader, fragmentShader);
gl.useProgram(program);
glBoilerplate.loadVertexData(gl, program);
programs[programName] = {
program: program,
uniforms: {}
};
};
GPUMath.prototype.initTextureFromData = function(name, width, height, typeName, data, shouldReplace){
var texture = this.textures[name];
if (!shouldReplace && texture) {
console.warn("already a texture with the name " + name);
return;
}
texture = glBoilerplate.makeTexture(gl, width, height, gl[typeName], data);
this.textures[name] = texture;
};
GPUMath.prototype.initFrameBufferForTexture = function(textureName){
var framebuffer = this.frameBuffers[textureName];
if (framebuffer) {
console.warn("framebuffer already exists for texture " + textureName);
return;
}
var texture = this.textures[textureName];
if (!texture){
console.warn("texture " + textureName + " does not exist");
return;
}
framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
this.frameBuffers[textureName] = framebuffer;
};
GPUMath.prototype.setUniformForProgram = function(programName, name, val, type){
if (!this.programs[programName]){
console.warn("no program with name " + programName);
return;
}
var uniforms = this.programs[programName].uniforms;
var location = uniforms[name];
if (!location) {
location = gl.getUniformLocation(this.programs[programName].program, name);
uniforms[name] = location;
}
if (type == "1f") gl.uniform1f(location, val);
else if (type == "2f") gl.uniform2f(location, val[0], val[1]);
else if (type == "3f") gl.uniform3f(location, val[0], val[1], val[2]);
else if (type == "1i") gl.uniform1i(location, val);
else {
console.warn("no uniform for type " + type);
}
};
GPUMath.prototype.setSize = function(width, height){
gl.viewport(0, 0, width, height);
canvas.clientWidth = width;
canvas.clientHeight = height;
};
GPUMath.prototype.setProgram = function(programName){
gl.useProgram(this.programs[programName].program);
};
GPUMath.prototype.step = function(programName, inputTextures, outputTexture){
gl.useProgram(this.programs[programName].program);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffers[outputTexture]);
for (var i=0;i<inputTextures.length;i++){
gl.activeTexture(gl.TEXTURE0 + i);
gl.bindTexture(gl.TEXTURE_2D, this.textures[inputTextures[i]]);
}
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);//draw to framebuffer
};
GPUMath.prototype.swapTextures = function(texture1Name, texture2Name){
var temp = this.textures[texture1Name];
this.textures[texture1Name] = this.textures[texture2Name];
this.textures[texture2Name] = temp;
temp = this.frameBuffers[texture1Name];
this.frameBuffers[texture1Name] = this.frameBuffers[texture2Name];
this.frameBuffers[texture2Name] = temp;
};
GPUMath.prototype.swap3Textures = function(texture1Name, texture2Name, texture3Name){
var temp = this.textures[texture3Name];
this.textures[texture3Name] = this.textures[texture2Name];
this.textures[texture2Name] = this.textures[texture1Name];
this.textures[texture1Name] = temp;
temp = this.frameBuffers[texture3Name];
this.frameBuffers[texture3Name] = this.frameBuffers[texture2Name];
this.frameBuffers[texture2Name] = this.frameBuffers[texture1Name];
this.frameBuffers[texture1Name] = temp;
};
GPUMath.prototype.readyToRead = function(){
return gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
};
GPUMath.prototype.readPixels = function(xMin, yMin, width, height, array){
gl.readPixels(xMin, yMin, width, height, gl.RGBA, gl.UNSIGNED_BYTE, array);
};
GPUMath.prototype.reset = function(){
this.programs = {};
this.frameBuffers = {};
this.textures = {};
this.index = 0;
};
return new GPUMath;
}
\ No newline at end of file
//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");
function initBoilerPlate(){
/**
* 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 createShaderFromSource(gl, shaderSource, shaderType) {
return compileShader(gl, shaderSource, 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 createProgramFromSource(
gl, vertexShader, fragmentShader) {
var vertexShader = createShaderFromSource(gl, vertexShader, gl.VERTEX_SHADER);
var fragmentShader = createShaderFromSource(gl, fragmentShader, gl.FRAGMENT_SHADER);
return createProgram(gl, vertexShader, fragmentShader);
}
function createProgramFromScripts(gl, vertexShaderId, fragmentShaderId) {
var vertexShader = createShaderFromScript(gl, vertexShaderId);
var fragmentShader = createShaderFromScript(gl, fragmentShaderId);
return createProgram(gl, vertexShader, fragmentShader);
}
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);
}
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 makeTexture(gl, width, height, type, 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, type, data);
return texture;
}
return {
createProgramFromSource: createProgramFromSource,
createProgramFromScripts: createProgramFromScripts,
loadVertexData: loadVertexData,
makeTexture: makeTexture
}
}
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
};
\ No newline at end of file
......@@ -20,14 +20,36 @@
<script id="2d-render-shader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_textureSize;
//uniform sampler2D u_velocities;
//uniform vec2 u_textureSize;
void main() {
gl_FragColor = vec4(1, 0, 1, 1);
}
</script>
<script id="advectShader" type="x-shader/x-fragment">
precision mediump float;
const float COLOR_MIN = 0.2, COLOR_MAX = 0.4;
//uniform sampler2D u_velocities;
//uniform sampler2D u_material;
//uniform vec2 u_textureSize;
//uniform float u_dt;
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);
//vec2 fragCoord = gl_FragCoord.xy;
//vec2 currentVelocity = texture2D(u_velocities, fragCoord/u_textureSize).xy;
//implicitly solve advection
//vec2 pos = fragCoord - u_dt * currentVelocity;
// bilinear interp between nearest cells
//gl_FragColor = texture2D(u_material, pos/u_textureSize);
gl_FragColor = vec4(1,1,1,1);
}
</script>
......@@ -74,7 +96,8 @@
<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="GLBoilerplate.js"></script>
<script type="text/javascript" src="GPUMath.js"></script>
<script type="text/javascript" src="main.js"></script>
</head>
<body>
......@@ -89,9 +112,10 @@
<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><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 numerical methods for evaluating Navier-Stokes on a discrete grid.<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/>
......
//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 width, height;
var mouseCoordLocation;
var mouseCoordinates = [null, null];
......@@ -24,6 +9,8 @@ var mouseEnable = false;
var paused = false;//while window is resizing
var GPU;
window.onload = initGL;
function initGL() {
......@@ -33,10 +20,7 @@ function initGL() {
$("#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;
......@@ -45,125 +29,31 @@ function initGL() {
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');
GPU = initGPUMath();
// 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()];
// setup a GLSL programs
GPU.createProgram("advect", "2d-vertex-shader", "advectShader");
GPU.createProgram("render", "2d-vertex-shader", "2d-render-shader");
resetWindow();
gl.bindTexture(gl.TEXTURE_2D, states[0]);//original texture
GPU.initFrameBufferForTexture("velocities");
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);
}
// if (mouseEnable){
// gl.uniform1f(mouseEnableLocation, 1);
// gl.uniform2f(mouseCoordLocation, mouseCoordinates[0], mouseCoordinates[1]);
// } else gl.uniform1f(mouseEnableLocation, 0);
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);
GPU.step("render", []);
} else resetWindow();
......@@ -187,20 +77,14 @@ function resetWindow(){
width = canvas.clientWidth;
height = canvas.clientHeight;
gl.viewport(0, 0, width, height);
GPU.setSize(width, height);
// set the size of the texture
gl.useProgram(stepProgram);
gl.uniform2f(textureSizeLocation, width, height);
gl.useProgram(renderProgram);
gl.uniform2f(textureSizeLocationRender, width, height);
GPU.setUniformForProgram("advect" ,"u_textureSize", [width, height], "2f");
//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));
var velocities = new Float32Array(width*height*4);
GPU.initTextureFromData("velocities", width, height, "FLOAT", velocities, true);
paused = false;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment