761 lines
23 KiB
JavaScript
761 lines
23 KiB
JavaScript
|
/*
|
||
|
* Copyright 2012, Gregg Tavares.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are
|
||
|
* met:
|
||
|
*
|
||
|
* * Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* * Redistributions in binary form must reproduce the above
|
||
|
* copyright notice, this list of conditions and the following disclaimer
|
||
|
* in the documentation and/or other materials provided with the
|
||
|
* distribution.
|
||
|
* * Neither the name of Gregg Tavares. nor the names of his
|
||
|
* contributors may be used to endorse or promote products derived from
|
||
|
* this software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
"use strict";
|
||
|
|
||
|
/** @module webgl-utils */
|
||
|
// These funcitions are meant solely to help unclutter the tutorials.
|
||
|
// They are not meant as production type functions.
|
||
|
|
||
|
//(function() {
|
||
|
|
||
|
/**
|
||
|
* Wrapped logging function.
|
||
|
* @param {string} msg The message to log.
|
||
|
*/
|
||
|
var log = function(msg) {
|
||
|
if (window.console && window.console.log) {
|
||
|
window.console.log(msg);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Wrapped logging function.
|
||
|
* @param {string} msg The message to log.
|
||
|
*/
|
||
|
var error = function(msg) {
|
||
|
if (window.console) {
|
||
|
if (window.console.error) {
|
||
|
window.console.error(msg);
|
||
|
}
|
||
|
else if (window.console.log) {
|
||
|
window.console.log(msg);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Turn off all logging.
|
||
|
*/
|
||
|
var loggingOff = function() {
|
||
|
log = function() {};
|
||
|
error = function() {};
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Check if the page is embedded.
|
||
|
* @return {boolean} True of we are in an iframe
|
||
|
*/
|
||
|
var isInIFrame = function() {
|
||
|
return window != window.top;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Converts a WebGL enum to a string
|
||
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
||
|
* @param {number} value The enum value.
|
||
|
* @return {string} The enum as a string.
|
||
|
*/
|
||
|
var glEnumToString = function(gl, value) {
|
||
|
for (var p in gl) {
|
||
|
if (gl[p] == value) {
|
||
|
return p;
|
||
|
}
|
||
|
}
|
||
|
return "0x" + value.toString(16);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates the HTLM for a failure message
|
||
|
* @param {string} canvasContainerId id of container of th
|
||
|
* canvas.
|
||
|
* @return {string} The html.
|
||
|
*/
|
||
|
var makeFailHTML = function(msg) {
|
||
|
return '' +
|
||
|
'<table style="background-color: #8CE; width: 100%; height: 100%;"><tr>' +
|
||
|
'<td align="center">' +
|
||
|
'<div style="display: table-cell; vertical-align: middle;">' +
|
||
|
'<div style="">' + msg + '</div>' +
|
||
|
'</div>' +
|
||
|
'</td></tr></table>';
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Mesasge for getting a webgl browser
|
||
|
* @type {string}
|
||
|
*/
|
||
|
var GET_A_WEBGL_BROWSER = '' +
|
||
|
'This page requires a browser that supports WebGL.<br/>' +
|
||
|
'<a href="http://get.webgl.org">Click here to upgrade your browser.</a>';
|
||
|
|
||
|
/**
|
||
|
* Mesasge for need better hardware
|
||
|
* @type {string}
|
||
|
*/
|
||
|
var OTHER_PROBLEM = '' +
|
||
|
"It doesn't appear your computer can support WebGL.<br/>" +
|
||
|
'<a href="http://get.webgl.org/troubleshooting/">Click here for more information.</a>';
|
||
|
|
||
|
/**
|
||
|
* Creates a webgl context. If creation fails it will
|
||
|
* change the contents of the container of the <canvas>
|
||
|
* tag to an error message with the correct links for WebGL.
|
||
|
* @param {HTMLCanvasElement} canvas. The canvas element to
|
||
|
* create a context from.
|
||
|
* @param {WebGLContextCreationAttirbutes} opt_attribs Any
|
||
|
* creation attributes you want to pass in.
|
||
|
* @return {WebGLRenderingContext} The created context.
|
||
|
*/
|
||
|
var setupWebGL = function(canvas, opt_attribs) {
|
||
|
function showLink(str) {
|
||
|
var container = canvas.parentNode;
|
||
|
if (container) {
|
||
|
container.innerHTML = makeFailHTML(str);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
if (!window.WebGLRenderingContext) {
|
||
|
showLink(GET_A_WEBGL_BROWSER);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var context = create3DContext(canvas, opt_attribs);
|
||
|
if (!context) {
|
||
|
showLink(OTHER_PROBLEM);
|
||
|
}
|
||
|
return context;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates a webgl context.
|
||
|
* @param {HTMLCanvasElement} canvas The canvas tag to get
|
||
|
* context from. If one is not passed in one will be
|
||
|
* created.
|
||
|
* @return {WebGLRenderingContext} The created context.
|
||
|
*/
|
||
|
var create3DContext = function(canvas, opt_attribs) {
|
||
|
var names = ["webgl", "experimental-webgl"];
|
||
|
var context = null;
|
||
|
for (var ii = 0; ii < names.length; ++ii) {
|
||
|
try {
|
||
|
context = canvas.getContext(names[ii], opt_attribs);
|
||
|
} catch(e) {}
|
||
|
if (context) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return context;
|
||
|
}
|
||
|
|
||
|
var updateCSSIfInIFrame = function() {
|
||
|
if (isInIFrame()) {
|
||
|
document.body.className = "iframe";
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Gets a WebGL context.
|
||
|
* makes its backing store the size it is displayed.
|
||
|
*/
|
||
|
var getWebGLContext = function(canvas, opt_attribs, opt_options) {
|
||
|
var options = opt_options || {}
|
||
|
|
||
|
if (isInIFrame()) {
|
||
|
updateCSSIfInIFrame();
|
||
|
|
||
|
// make the canvas backing store the size it's displayed.
|
||
|
if (!options.dontResize) {
|
||
|
var width = canvas.clientWidth;
|
||
|
var height = canvas.clientHeight;
|
||
|
canvas.width = width;
|
||
|
canvas.height = height;
|
||
|
}
|
||
|
} else {
|
||
|
var title = document.title;
|
||
|
var h1 = document.createElement("h1");
|
||
|
h1.innerText = title;
|
||
|
document.body.insertBefore(h1, document.body.children[0]);
|
||
|
}
|
||
|
|
||
|
var gl = setupWebGL(canvas, opt_attribs);
|
||
|
return gl;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Loads a shader.
|
||
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
||
|
* @param {string} shaderSource The shader source.
|
||
|
* @param {number} shaderType The type of shader.
|
||
|
* @param {function(string): void) opt_errorCallback callback for errors.
|
||
|
* @return {WebGLShader} The created shader.
|
||
|
*/
|
||
|
var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
|
||
|
var errFn = opt_errorCallback || error;
|
||
|
// Create the shader object
|
||
|
var shader = gl.createShader(shaderType);
|
||
|
|
||
|
// Load the shader source
|
||
|
gl.shaderSource(shader, shaderSource);
|
||
|
|
||
|
// Compile the shader
|
||
|
gl.compileShader(shader);
|
||
|
|
||
|
// Check the compile status
|
||
|
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
||
|
if (!compiled) {
|
||
|
// Something went wrong during compilation; get the error
|
||
|
var lastError = gl.getShaderInfoLog(shader);
|
||
|
errFn("*** Error compiling shader '" + shader + "':" + lastError);
|
||
|
gl.deleteShader(shader);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return shader;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a program, attaches shaders, binds attrib locations, links the
|
||
|
* program and calls useProgram.
|
||
|
* @param {WebGLShader[]} shaders The shaders to attach
|
||
|
* @param {string[]?} opt_attribs The attribs names.
|
||
|
* @param {number[]?} opt_locations The locations for the
|
||
|
* attribs.
|
||
|
* @param {function(string): void) opt_errorCallback callback for errors.
|
||
|
*/
|
||
|
var loadProgram = function(
|
||
|
gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
|
||
|
var errFn = opt_errorCallback || error;
|
||
|
var program = gl.createProgram();
|
||
|
for (var ii = 0; ii < shaders.length; ++ii) {
|
||
|
gl.attachShader(program, shaders[ii]);
|
||
|
}
|
||
|
if (opt_attribs) {
|
||
|
for (var ii = 0; ii < opt_attribs.length; ++ii) {
|
||
|
gl.bindAttribLocation(
|
||
|
program,
|
||
|
opt_locations ? opt_locations[ii] : ii,
|
||
|
opt_attribs[ii]);
|
||
|
}
|
||
|
}
|
||
|
gl.linkProgram(program);
|
||
|
|
||
|
// Check the link status
|
||
|
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
|
||
|
if (!linked) {
|
||
|
// something went wrong with the link
|
||
|
var lastError = gl.getProgramInfoLog (program);
|
||
|
errFn("Error in program linking:" + lastError);
|
||
|
|
||
|
gl.deleteProgram(program);
|
||
|
return null;
|
||
|
}
|
||
|
return program;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Loads a shader from a script tag.
|
||
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
||
|
* @param {string} scriptId The id of the script tag.
|
||
|
* @param {number} opt_shaderType The type of shader. If not passed in it will
|
||
|
* be derived from the type of the script tag.
|
||
|
* @param {function(string): void) opt_errorCallback callback for errors.
|
||
|
* @return {WebGLShader} The created shader.
|
||
|
*/
|
||
|
var createShaderFromScript = function(
|
||
|
gl, scriptId, opt_shaderType, opt_errorCallback) {
|
||
|
var shaderSource = "";
|
||
|
var shaderType;
|
||
|
var shaderScript = document.getElementById(scriptId);
|
||
|
if (!shaderScript) {
|
||
|
throw("*** Error: unknown script element" + scriptId);
|
||
|
}
|
||
|
shaderSource = shaderScript.text;
|
||
|
|
||
|
if (!opt_shaderType) {
|
||
|
if (shaderScript.type == "x-shader/x-vertex") {
|
||
|
shaderType = gl.VERTEX_SHADER;
|
||
|
} else if (shaderScript.type == "x-shader/x-fragment") {
|
||
|
shaderType = gl.FRAGMENT_SHADER;
|
||
|
} else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
|
||
|
throw("*** Error: unknown shader type");
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return loadShader(
|
||
|
gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
|
||
|
opt_errorCallback);
|
||
|
};
|
||
|
|
||
|
var defaultShaderType = [
|
||
|
"VERTEX_SHADER",
|
||
|
"FRAGMENT_SHADER"
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Creates a program from 2 script tags.
|
||
|
*
|
||
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
|
||
|
* to use.
|
||
|
* @param {string[]} shaderScriptIds Array of ids of the script
|
||
|
* tags for the shaders. The first is assumed to be the
|
||
|
* vertex shader, the second the fragment shader.
|
||
|
* @param {string[]?} opt_attribs The attribs names.
|
||
|
* @param {number[]?} opt_locations The locations for the
|
||
|
* attribs.
|
||
|
* @param {function(string): void) opt_errorCallback callback for errors.
|
||
|
* @return {WebGLProgram} The created program.
|
||
|
*/
|
||
|
var createProgramFromScripts = function(
|
||
|
gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) {
|
||
|
var shaders = [];
|
||
|
for (var ii = 0; ii < shaderScriptIds.length; ++ii) {
|
||
|
shaders.push(createShaderFromScript(
|
||
|
gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], opt_errorCallback));
|
||
|
}
|
||
|
return loadProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates a program from 2 sources.
|
||
|
*
|
||
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
|
||
|
* to use.
|
||
|
* @param {string[]} shaderSourcess Array of sources for the
|
||
|
* shaders. The first is assumed to be the vertex shader,
|
||
|
* the second the fragment shader.
|
||
|
* @param {string[]?} opt_attribs The attribs names.
|
||
|
* @param {number[]?} opt_locations The locations for the
|
||
|
* attribs.
|
||
|
* @param {function(string): void) opt_errorCallback callback for errors.
|
||
|
* @return {WebGLProgram} The created program.
|
||
|
*/
|
||
|
var createProgramFromSources = function(
|
||
|
gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
|
||
|
var shaders = [];
|
||
|
for (var ii = 0; ii < shaderSources.length; ++ii) {
|
||
|
shaders.push(loadShader(
|
||
|
gl, shaderSources[ii], gl[defaultShaderType[ii]], opt_errorCallback));
|
||
|
}
|
||
|
return loadProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns the corresponding bind point for a given sampler type
|
||
|
*/
|
||
|
var getBindPointForSamplerType = function(gl, type) {
|
||
|
if (type == gl.SAMPLER_2D) return gl.TEXTURE_2D;
|
||
|
if (type == gl.SAMPLER_CUBE) return gl.TEXTURE_CUBE_MAP;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @typedef {Object.<string, function>} Setters
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Creates setter functions for all uniforms of a shader
|
||
|
* program.
|
||
|
*
|
||
|
* @see setUniforms for example
|
||
|
*
|
||
|
* @param {WebGLProgram} program the program to create setters
|
||
|
* for.
|
||
|
* @returns {Setters} an object with a setter for each uniform
|
||
|
* by name.
|
||
|
*/
|
||
|
var createUniformSetters = function(gl, program) {
|
||
|
var textureUnit = 0;
|
||
|
|
||
|
/**
|
||
|
* Creates a setter for a uniform of the given program with it's
|
||
|
* location embedded in the setter.
|
||
|
* @param {WebGLProgram} program
|
||
|
* @param {WebGLUniformInfo} uniformInfo
|
||
|
* @returns {function} the created setter.
|
||
|
*/
|
||
|
var createUniformSetter = function(program, uniformInfo) {
|
||
|
var location = gl.getUniformLocation(program, uniformInfo.name);
|
||
|
var type = uniformInfo.type;
|
||
|
// Check if this uniform is an array
|
||
|
var isArray = (uniformInfo.size > 1 && uniformInfo.name.substr(-3) == "[0]");
|
||
|
if (type == gl.FLOAT && isArray)
|
||
|
return function(v) { gl.uniform1fv(location, v); };
|
||
|
if (type == gl.FLOAT)
|
||
|
return function(v) { gl.uniform1f(location, v); };
|
||
|
if (type == gl.FLOAT_VEC2)
|
||
|
return function(v) { gl.uniform2fv(location, v); };
|
||
|
if (type == gl.FLOAT_VEC3)
|
||
|
return function(v) { gl.uniform3fv(location, v); };
|
||
|
if (type == gl.FLOAT_VEC4)
|
||
|
return function(v) { gl.uniform4fv(location, v); };
|
||
|
if (type == gl.INT && isArray)
|
||
|
return function(v) { gl.uniform1iv(location, v); };
|
||
|
if (type == gl.INT)
|
||
|
return function(v) { gl.uniform1i(location, v); };
|
||
|
if (type == gl.INT_VEC2)
|
||
|
return function(v) { gl.uniform2iv(location, v); };
|
||
|
if (type == gl.INT_VEC3)
|
||
|
return function(v) { gl.uniform3iv(location, v); };
|
||
|
if (type == gl.INT_VEC4)
|
||
|
return function(v) { gl.uniform4iv(location, v); };
|
||
|
if (type == gl.BOOL)
|
||
|
return function(v) { gl.uniform1iv(location, v); };
|
||
|
if (type == gl.BOOL_VEC2)
|
||
|
return function(v) { gl.uniform2iv(location, v); };
|
||
|
if (type == gl.BOOL_VEC3)
|
||
|
return function(v) { gl.uniform3iv(location, v); };
|
||
|
if (type == gl.BOOL_VEC4)
|
||
|
return function(v) { gl.uniform4iv(location, v); };
|
||
|
if (type == gl.FLOAT_MAT2)
|
||
|
return function(v) { gl.uniformMatrix2fv(location, false, v); };
|
||
|
if (type == gl.FLOAT_MAT3)
|
||
|
return function(v) { gl.uniformMatrix3fv(location, false, v); };
|
||
|
if (type == gl.FLOAT_MAT4)
|
||
|
return function(v) { gl.uniformMatrix4fv(location, false, v); };
|
||
|
if ((type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) && isArray) {
|
||
|
var units = [];
|
||
|
for (var ii = 0; ii < info.size; ++ii) {
|
||
|
units.push(textureUnit++);
|
||
|
}
|
||
|
return function(bindPoint, units) {
|
||
|
return function(textures) {
|
||
|
gl.uniform1iv(location, units);
|
||
|
textures.forEach(function(texture, index) {
|
||
|
gl.activeTexture(gl.TEXTURE0 + units[index]);
|
||
|
gl.bindTexture(bindPoint, tetxure);
|
||
|
});
|
||
|
}
|
||
|
}(getBindPointForSamplerType(gl, type), units);
|
||
|
}
|
||
|
if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE)
|
||
|
return function(bindPoint, unit) {
|
||
|
return function(texture) {
|
||
|
gl.uniform1i(location, unit);
|
||
|
gl.activeTexture(gl.TEXTURE0 + unit);
|
||
|
gl.bindTexture(bindPoint, texture);
|
||
|
};
|
||
|
}(getBindPointForSamplerType(gl, type), textureUnit++);
|
||
|
throw ("unknown type: 0x" + type.toString(16)); // we should never get here.
|
||
|
};
|
||
|
|
||
|
var uniformSetters = { };
|
||
|
var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
|
||
|
|
||
|
for (var ii = 0; ii < numUniforms; ++ii) {
|
||
|
var uniformInfo = gl.getActiveUniform(program, ii);
|
||
|
if (!uniformInfo) {
|
||
|
break;
|
||
|
}
|
||
|
var name = uniformInfo.name;
|
||
|
// remove the array suffix.
|
||
|
if (name.substr(-3) == "[0]") {
|
||
|
name = name.substr(0, name.length - 3);
|
||
|
}
|
||
|
var setter = createUniformSetter(program, uniformInfo);
|
||
|
uniformSetters[name] = setter;
|
||
|
}
|
||
|
return uniformSetters;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Set uniforms and binds related textures.
|
||
|
*
|
||
|
* @example
|
||
|
*
|
||
|
* var program = createProgramFromScripts(
|
||
|
* gl, ["some-vs", "some-fs");
|
||
|
*
|
||
|
* var uniformSetters = createUniformSetters(program);
|
||
|
*
|
||
|
* var tex1 = gl.createTexture();
|
||
|
* var tex2 = gl.createTexture();
|
||
|
*
|
||
|
* ... assume we setup the textures with data ...
|
||
|
*
|
||
|
* var uniforms = {
|
||
|
* u_someSampler: tex1,
|
||
|
* u_someOtherSampler: tex2,
|
||
|
* u_someColor: [1,0,0,1],
|
||
|
* u_somePosition: [0,1,1],
|
||
|
* u_someMatrix: [
|
||
|
* 1,0,0,0,
|
||
|
* 0,1,0,0,
|
||
|
* 0,0,1,0,
|
||
|
* 0,0,0,0,
|
||
|
* ],
|
||
|
* }
|
||
|
*
|
||
|
* gl.useProgram(program);
|
||
|
*
|
||
|
* This will automatically bind the textures AND set the
|
||
|
* uniforms.
|
||
|
*
|
||
|
* setUniforms(uniformSetters, uniforms);
|
||
|
*
|
||
|
* @param {Setters} setters the setters returned from
|
||
|
* createUniformSettersForProgram
|
||
|
* @param {Object.<string, value>} an object with values for the
|
||
|
* uniforms.
|
||
|
*/
|
||
|
var setUniforms = function(setters, values) {
|
||
|
Object.keys(values).forEach(function(name) {
|
||
|
var setter = setters[name];
|
||
|
if (setter) {
|
||
|
setter(values[name]);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates setter functions for all attributes of a shader
|
||
|
* program
|
||
|
*
|
||
|
* @see setAttributes for example
|
||
|
*
|
||
|
* @param {WebGLProgram} program the program to create setters
|
||
|
* for.
|
||
|
* @returns {Setters} an object with a setter for each uniform
|
||
|
* by name.
|
||
|
*/
|
||
|
var createAttributeSetters = function(gl, program) {
|
||
|
var attribSetters = {
|
||
|
};
|
||
|
|
||
|
function createAttribSetter(index) {
|
||
|
return function(b) {
|
||
|
gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer);
|
||
|
gl.enableVertexAttribArray(index);
|
||
|
gl.vertexAttribPointer(
|
||
|
index, b.numComponents || b.size, b.type || gl.FLOAT, b.normalize || false, b.stride || 0, b.offset || 0);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
|
||
|
for (var ii = 0; ii < numAttribs; ++ii) {
|
||
|
var attribInfo = gl.getActiveAttrib(program, ii);
|
||
|
if (!attribInfo) {
|
||
|
break;
|
||
|
}
|
||
|
var index = gl.getAttribLocation(program, attribInfo.name);
|
||
|
attribSetters[attribInfo.name] = createAttribSetter(index);
|
||
|
}
|
||
|
|
||
|
return attribSetters;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Sets attributes and binds buffers.
|
||
|
*
|
||
|
* @example
|
||
|
*
|
||
|
* var program = createProgramFromScripts(
|
||
|
* gl, ["some-vs", "some-fs");
|
||
|
*
|
||
|
* var attribSetters = createAttributeSetters(program);
|
||
|
*
|
||
|
* var positionBuffer = gl.createBuffer();
|
||
|
* var texcoordBuffer = gl.createBuffer();
|
||
|
*
|
||
|
* var attribs = {
|
||
|
* a_position: {buffer: positionBuffer, numComponents: 3},
|
||
|
* a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
|
||
|
* };
|
||
|
*
|
||
|
* gl.useProgram(program);
|
||
|
*
|
||
|
* This will automatically bind the buffers AND set the
|
||
|
* attributes.
|
||
|
*
|
||
|
* setAttributes(attribSetters, attribs);
|
||
|
*
|
||
|
* Properties of attribs. For each attrib you can add
|
||
|
* properties:
|
||
|
*
|
||
|
* type: the type of data in the buffer. Default = gl.FLOAT
|
||
|
* normalize: whether or not to normalize the data. Default =
|
||
|
* false
|
||
|
* stride: the stride. Default = 0
|
||
|
* offset: offset into the buffer. Default = 0
|
||
|
*
|
||
|
* For example if you had 3 value float positions, 2 value
|
||
|
* float texcoord and 4 value uint8 colors you'd setup your
|
||
|
* attribs like this
|
||
|
*
|
||
|
* var attribs = {
|
||
|
* a_position: {buffer: positionBuffer, numComponents: 3},
|
||
|
* a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
|
||
|
* a_color: {
|
||
|
* buffer: colorBuffer,
|
||
|
* numComponents: 4,
|
||
|
* type: gl.UNSIGNED_BYTE,
|
||
|
* normalize: true,
|
||
|
* },
|
||
|
* };
|
||
|
*
|
||
|
*/
|
||
|
var setAttributes = function(setters, buffers) {
|
||
|
Object.keys(buffers).forEach(function(name) {
|
||
|
var setter = setters[name];
|
||
|
if (setter) {
|
||
|
setter(buffers[name]);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
// Add your prefix here.
|
||
|
var browserPrefixes = [
|
||
|
"",
|
||
|
"MOZ_",
|
||
|
"OP_",
|
||
|
"WEBKIT_"
|
||
|
];
|
||
|
|
||
|
/**
|
||
|
* Given an extension name like WEBGL_compressed_texture_s3tc
|
||
|
* returns the supported version extension, like
|
||
|
* WEBKIT_WEBGL_compressed_teture_s3tc
|
||
|
* @param {string} name Name of extension to look for
|
||
|
* @return {WebGLExtension} The extension or undefined if not
|
||
|
* found.
|
||
|
*/
|
||
|
var getExtensionWithKnownPrefixes = function(gl, name) {
|
||
|
for (var ii = 0; ii < browserPrefixes.length; ++ii) {
|
||
|
var prefixedName = browserPrefixes[ii] + name;
|
||
|
var ext = gl.getExtension(prefixedName);
|
||
|
if (ext) {
|
||
|
return ext;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Resize a canvas to match the size it's displayed.
|
||
|
* @param {HTMLCanvasElement} canvas The canvas to resize.
|
||
|
* @param {boolean} true if the canvas was resized.
|
||
|
*/
|
||
|
var resizeCanvasToDisplaySize = function(canvas) {
|
||
|
var width = canvas.clientWidth;
|
||
|
var height = canvas.clientHeight;
|
||
|
if (canvas.width != width ||
|
||
|
canvas.height != height) {
|
||
|
canvas.width = width;
|
||
|
canvas.height = height;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* export functions */
|
||
|
window.createAttributeSetters = createAttributeSetters;
|
||
|
window.createProgram = loadProgram;
|
||
|
window.createProgramFromScripts = createProgramFromScripts;
|
||
|
window.createProgramFromSources = createProgramFromSources;
|
||
|
window.createShaderFromScriptElement = createShaderFromScript;
|
||
|
window.createUniformSetters = createUniformSetters;
|
||
|
window.getWebGLContext = getWebGLContext;
|
||
|
window.updateCSSIfInIFrame = updateCSSIfInIFrame;
|
||
|
window.getExtensionWithKnownPrefixes = getExtensionWithKnownPrefixes;
|
||
|
window.resizeCanvasToDisplaySize = resizeCanvasToDisplaySize;
|
||
|
window.setAttributes = setAttributes;
|
||
|
window.setUniforms = setUniforms;
|
||
|
window.setupWebGL = setupWebGL;
|
||
|
|
||
|
// All browsers that support WebGL support requestAnimationFrame
|
||
|
window.requestAnimFrame = window.requestAnimationFrame; // just to stay backward compatible.
|
||
|
window.cancelRequestAnimFrame = window.cancelAnimationFrame; // just to stay backward compatible.
|
||
|
|
||
|
//}());
|
||
|
|
||
|
|
||
|
|
||
|
function onLoad () {
|
||
|
var canvas = document.getElementById("canvas");
|
||
|
var gl = canvas.getContext("experimental-webgl");
|
||
|
|
||
|
var vertexShaderNode = createShaderFromScriptElement(gl, "node-vertex-shader");
|
||
|
var fragmentShaderNode = createShaderFromScriptElement(gl, "node-fragment-shader");
|
||
|
var programNode = createProgram(gl, [vertexShaderNode, fragmentShaderNode]);
|
||
|
gl.useProgram(programNode);
|
||
|
|
||
|
|
||
|
var ATTRIBUTES = 5;
|
||
|
var j = 0;
|
||
|
var data = [];
|
||
|
var circle = {x: 50, y: 50, r: 45};
|
||
|
|
||
|
data[j++] = (circle.x - circle.r);
|
||
|
data[j++] = (circle.y - circle.r);
|
||
|
data[j++] = circle.x;
|
||
|
data[j++] = circle.y;
|
||
|
data[j++] = circle.r;
|
||
|
|
||
|
data[j++] = (circle.x + (1 + Math.sqrt(2)) * circle.r);
|
||
|
data[j++] = circle.y - circle.r;
|
||
|
data[j++] = circle.x;
|
||
|
data[j++] = circle.y;
|
||
|
data[j++] = circle.r;
|
||
|
|
||
|
data[j++] = (circle.x - circle.r);
|
||
|
data[j++] = (circle.y + (1 + Math.sqrt(2)) * circle.r);
|
||
|
data[j++] = circle.x;
|
||
|
data[j++] = circle.y;
|
||
|
data[j++] = circle.r;
|
||
|
|
||
|
var dataBuffer = new Float32Array(data);
|
||
|
|
||
|
var buffer = gl.createBuffer();
|
||
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||
|
gl.bufferData(
|
||
|
gl.ARRAY_BUFFER,
|
||
|
dataBuffer,
|
||
|
gl.STATIC_DRAW);
|
||
|
|
||
|
var resolutionLocation = gl.getUniformLocation(programNode, "u_resolution");
|
||
|
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
||
|
|
||
|
var positionLocation = gl.getAttribLocation(programNode, "a_position");
|
||
|
var centerLocation = gl.getAttribLocation(programNode, "a_center");
|
||
|
var radiusLocation = gl.getAttribLocation(programNode, "a_radius");
|
||
|
|
||
|
|
||
|
gl.enableVertexAttribArray(positionLocation);
|
||
|
gl.enableVertexAttribArray(centerLocation);
|
||
|
gl.enableVertexAttribArray(radiusLocation);
|
||
|
|
||
|
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0);
|
||
|
gl.vertexAttribPointer(centerLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8);
|
||
|
gl.vertexAttribPointer(radiusLocation, 1, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 16);
|
||
|
|
||
|
gl.drawArrays(gl.TRIANGLES, 0, data.length/ATTRIBUTES);
|
||
|
}
|
||
|
onLoad();
|