diff --git a/README.md b/README.md index f5ea1a9..1ea11e2 100644 --- a/README.md +++ b/README.md @@ -8,44 +8,68 @@ The core of [gl-shader](https://github.com/mikolalysenko/gl-shader), without the ## API -### `var shader = require("gl-shader-core")(gl, vertexSource, fragmentSource, uniforms, attributes)` -Constructs a packaged gl-shader object with shims for all of the uniforms and attributes in the program. +```javascript +var createShader = require('gl-shader-core') +``` + +### Constructor + +There are two main usages for the constructor. First, + +#### `var shader = createShader(vertexSource, fragmentSource[, uniforms, attributes])` + +Constructs a wrapped shader object with shims for all of the uniforms and attributes in the program. * `gl` is the webgl context in which the program will be created * `vertexSource` is the source code for the vertex shader * `fragmentSource` is the source code for the fragment shader -* `uniforms` is a list of all uniforms exported by the shader program -* `attributes` is a list of all attributes exported by the shader program +* `uniforms` is an (optional) list of all uniforms exported by the shader program +* `attributes` is an (optional) list of all attributes exported by the shader program -The uniform and attributes variables have output which is consistent with [glsl-extract](https://npmjs.org/package/glsl-extract). +The format of `uniforms` and `attributes` is consistent with `glslify`'s output **Returns** A compiled shader object. You can specify a default `location` number for each attribute, otherwise WebGL will bind it automatically. -## Methods +#### `var shader = createShader(gl, glslifyResult)` + +Constructs a shader object from the output of `glslify`. -### `shader.bind()` +* `gl` is a WebGL context +* `glslify` is the output of `glslify` + +**Returns** A wrapped shader object + +### Methods + +#### `shader.bind()` Binds the shader for rendering -### `shader.dispose()` +#### `shader.update(vertSource, fragSource[, uniforms, attributes])` +Rebuilds the shader object with new vertex and fragment shaders (same behavior as constructor) + +#### `shader.update(glslifyResult)` +Rebuilds the shader object with new vertex and fragment shaders (same behavior as constructor) + +#### `shader.dispose()` Deletes the shader program and associated resources. -## Properties +### Properties -### `gl` +#### `gl` The WebGL context associated to the shader -### `handle` -A handle to the underlying WebGL program object +#### `program` +A reference to the underlying program object in the WebGL context -### `vertexShader` -A handle to the underlying WebGL fragment shader object +#### `vertShader` +A reference to the underlying vertex shader object -### `fragmentShader` -A handle to the underlying WebGL vertex shader object +#### `fragShader` +A reference to the underlying fragment shader object -## Uniforms +### Uniforms The uniforms for the shader program are packaged up as properties in the `shader.uniforms` object. For example, to update a scalar uniform you can just assign to it: ```javascript @@ -81,11 +105,11 @@ Struct uniforms can also be accessed using the normal dot property syntax. For shader.uniforms.light[0].color = [1, 0, 0, 1] ``` -## Attributes +### Attributes The basic idea behind the attribute interface is similar to that for uniforms, however because attributes can be either a constant value or get values from a vertex array they have a slightly more complicated interface. All of the attributes are stored in the `shader.attributes` property. -### `attrib = constant` +#### `attrib = constant` For non-array attributes you can set the constant value to be broadcast across all vertices. For example, to set the vertex color of a shader to a constant you could do: ```javascript @@ -107,9 +131,7 @@ Or you can read the currently bound location back by just accessing it: console.log(attrib.location) ``` -Internally, these methods just call [`gl.bindAttribLocation`](http://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindAttribLocation.xml) and access the stored location. - -**WARNING** Changing the attribute location requires recompiling the program. Do not dynamically modify this variable in your render loop. +**WARNING** Changing the attribute location requires recompiling the program. This recompilation is deferred until the next call to `.bind()` ### `attrib.pointer([type, normalized, stride, offset])` A shortcut for `gl.vertexAttribPointer`/`gl.enableVertexAttribArray`. See the [OpenGL man page for details on how this works](http://www.khronos.org/opengles/sdk/docs/man/xhtml/glVertexAttribPointer.xml). The main difference here is that the WebGL context, size and index are known and so these parameters are bound. @@ -119,7 +141,7 @@ A shortcut for `gl.vertexAttribPointer`/`gl.enableVertexAttribArray`. See the [ * `stride` the byte offset between consecutive generic vertex attributes. (Default: `0`) * `offset` offset of the first element of the array in bytes. (Default `0`) -## Reflection +### Reflection Finally, the library supports some reflection capabilities. The set of all uniforms and data types are stored in the "type" property of the shader object, @@ -129,6 +151,5 @@ console.log(shader.types) This reflects the uniform and attribute parameters that were passed to the shader constructor. - ## Credits -(c) 2013 Mikola Lysenko. MIT License \ No newline at end of file +(c) 2013-2014 Mikola Lysenko. MIT License \ No newline at end of file diff --git a/lib/create-attributes.js b/lib/create-attributes.js index 1806bbd..1efb93a 100644 --- a/lib/create-attributes.js +++ b/lib/create-attributes.js @@ -2,42 +2,66 @@ module.exports = createAttributeWrapper -//Shader attribute class -function ShaderAttribute(gl, program, location, dimension, name, constFunc, relink) { - this._gl = gl - this._program = program - this._location = location +function ShaderAttribute( + gl + , wrapper + , index + , locations + , dimension) { + this._gl = gl + this._wrapper = wrapper + this._index = index + this._locations = locations this._dimension = dimension - this._name = name - this._constFunc = constFunc - this._relink = relink } var proto = ShaderAttribute.prototype -proto.pointer = function setAttribPointer(type, normalized, stride, offset) { - var gl = this._gl - gl.vertexAttribPointer(this._location, this._dimension, type||gl.FLOAT, !!normalized, stride||0, offset||0) - this._gl.enableVertexAttribArray(this._location) +proto.pointer = function setAttribPointer( + type + , normalized + , stride + , offset) { + + var self = this + var gl = self._gl + var location = self._locations[self._index] + + gl.vertexAttribPointer( + location + , self._dimension + , type || gl.FLOAT + , !!normalized + , stride || 0 + , offset || 0) + + this._gl.enableVertexAttribArray(location) } Object.defineProperty(proto, 'location', { get: function() { - return this._location + return this._locations[this._index] } , set: function(v) { - if(v !== this._location) { - this._location = v - this._gl.bindAttribLocation(this._program, v, this._name) - this._gl.linkProgram(this._program) - this._relink() + if(v !== this._locations[this._index]) { + this._locations[this._index] = v + this._wrapper.program = null } } }) //Adds a vector attribute to obj -function addVectorAttribute(gl, program, location, dimension, obj, name, doLink) { +function addVectorAttribute( + gl + , wrapper + , index + , locations + , dimension + , obj + , name) { + + //Construct constant function var constFuncArgs = [ 'gl', 'v' ] var varNames = [] for(var i=0; i 4) { throw new Error('gl-shader: Invalid data type for attribute ' + name + ': ' + type) } - addVectorAttribute(gl, program, location, d, obj, name, doLink) + addVectorAttribute( + gl + , wrapper + , i + , locations + , d + , obj + , name) } else { throw new Error('gl-shader: Unknown data type for attribute ' + name + ': ' + type) } diff --git a/lib/create-uniforms.js b/lib/create-uniforms.js index 8185354..fc82ee4 100644 --- a/lib/create-uniforms.js +++ b/lib/create-uniforms.js @@ -12,12 +12,15 @@ function identity(x) { } //Create shims for uniforms -function createUniformWrapper(gl, program, uniforms, locations) { +function createUniformWrapper(gl, wrapper, uniforms, locations) { function makeGetter(index) { - var proc = new Function('gl', 'prog', 'locations', - 'return function(){return gl.getUniform(prog,locations[' + index + '])}') - return proc(gl, program, locations) + var proc = new Function( + 'gl' + , 'wrapper' + , 'locations' + , 'return function(){return gl.getUniform(wrapper.program,locations[' + index + '])}') + return proc(gl, wrapper, locations) } function makePropSetter(path, index, type) { @@ -92,8 +95,8 @@ function createUniformWrapper(gl, program, uniforms, locations) { } } code.push('return obj}') - var proc = new Function('gl', 'prog', 'locations', code.join('\n')) - return proc(gl, program, locations) + var proc = new Function('gl', 'locations', code.join('\n')) + return proc(gl, locations) } function defaultValue(type) { diff --git a/lib/runtime-reflect.js b/lib/runtime-reflect.js new file mode 100644 index 0000000..f7f7d13 --- /dev/null +++ b/lib/runtime-reflect.js @@ -0,0 +1,68 @@ +'use strict' + +exports.uniforms = runtimeUniforms +exports.attributes = runtimeAttributes + +var GL_TO_GLSL_TYPES = { + 'FLOAT': 'float', + 'FLOAT_VEC2': 'vec2', + 'FLOAT_VEC3': 'vec3', + 'FLOAT_VEC4': 'vec4', + 'INT': 'int', + 'INT_VEC2': 'ivec2', + 'INT_VEC3': 'ivec3', + 'INT_VEC4': 'ivec4', + 'BOOL': 'bool', + 'BOOL_VEC2': 'bvec2', + 'BOOL_VEC3': 'bvec3', + 'BOOL_VEC4': 'bvec4', + 'FLOAT_MAT2': 'mat2', + 'FLOAT_MAT3': 'mat3', + 'FLOAT_MAT4': 'mat4', + 'SAMPLER_2D': 'sampler2D', + 'SAMPLER_CUBE':'samplerCube' +} + +var GL_TABLE = null + +function getType(gl, type) { + if(!GL_TABLE) { + var typeNames = Object.keys(GL_TO_GLSL_TYPES) + GL_TABLE = {} + for(var i=0; i b.name) { + return 1 + } + return 0 + }) + + //Extract names + var attributeNames = attributes.map(function(attr) { + return attr.name + }) + + //Get default location + var attributeLocations = attributes.map(function(attr) { + if('location' in attr) { + return attr.location|0 + } else { + return -1 + } + }) + + //For all unspecified attributes, assign them lexicographically min attribute + var curLocation = 0 + for(var i=0; i= 0) { + curLocation += 1 + } + attributeLocations[i] = curLocation + } + } + + //Rebuild program and recompute all uniform locations + var uniformLocations = new Array(uniforms.length) + function relink(prog) { + wrapper.program = shaderCache.program( + gl + , wrapper._vref + , wrapper._fref + , attributeNames + , attributeLocations + , prog) + + for(var i=0; i