/** * @author Mat Groves http://matgroves.com/ @Doormat23 * @author Richard Davey http://www.photonstorm.com @photonstorm */ /** * @class PIXI.PixiShader * @constructor */ PIXI.PixiShader = function() { /** * @property {any} program - The WebGL program. */ this.program; /** * @property {array} fragmentSrc - The fragment shader. */ this.fragmentSrc = [ "precision lowp float;", "varying vec2 vTextureCoord;", "varying float vColor;", "uniform sampler2D uSampler;", "void main(void) {", "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", "}" ]; /** * @property {number} textureCount - A local texture counter for multi-texture shaders. */ this.textureCount = 0; } /** * @method PIXI.PixiShader#init */ PIXI.PixiShader.prototype.init = function() { var program = PIXI.compileProgram(this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc) var gl = PIXI.gl; gl.useProgram(program); // get and store the uniforms for the shader this.uSampler = gl.getUniformLocation(program, "uSampler"); this.projectionVector = gl.getUniformLocation(program, "projectionVector"); this.offsetVector = gl.getUniformLocation(program, "offsetVector"); //this.dimensions = gl.getUniformLocation(this.program, "dimensions"); // get and store the attributes this.aVertexPosition = gl.getAttribLocation(program, "aVertexPosition"); this.colorAttribute = gl.getAttribLocation(program, "aColor"); this.aTextureCoord = gl.getAttribLocation(program, "aTextureCoord"); // add those custom shaders! for (var key in this.uniforms) { // get the uniform locations.. this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); } this.program = program; } /** * Updates the shader uniform values. * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf * * @method PIXI.PixiShader#syncUniforms */ PIXI.PixiShader.prototype.syncUniforms = function() { this.textureCount = 1; var gl = PIXI.gl; for (var key in this.uniforms) { var type = this.uniforms[key].type; var transpose = false; if (this.uniforms[key].transpose) { transpose = this.uniforms[key].transpose; } if (type == "1f") { // void uniform1f(WebGLUniformLocation? location, GLfloat x); gl.uniform1f(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "1fv") { // void uniform1fv(WebGLUniformLocation? location, Float32Array v); // void uniform1fv(WebGLUniformLocation? location, sequence<GLfloat> v); gl.uniform1fv(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "1i") { // void uniform1i(WebGLUniformLocation? location, GLint x); gl.uniform1i(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "1iv") { // void uniform1iv(WebGLUniformLocation? location, Int32Array v); // void uniform1iv(WebGLUniformLocation? location, sequence<long> v); gl.uniform1i(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "2f") { // void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y); gl.uniform2f(this.uniforms[key].uniformLocation, this.uniforms[key].value.x, this.uniforms[key].value.y); } else if (type == "2fv") { // void uniform2fv(WebGLUniformLocation? location, Float32Array v); // void uniform2fv(WebGLUniformLocation? location, sequence<GLfloat> v); gl.uniform2fv(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "2i") { // void uniform2i(WebGLUniformLocation? location, GLint x, GLint y); gl.uniform2i(this.uniforms[key].uniformLocation, this.uniforms[key].value.x, this.uniforms[key].value.y); } else if (type == "2iv") { // void uniform2iv(WebGLUniformLocation? location, Int32Array v); // void uniform2iv(WebGLUniformLocation? location, sequence<long> v); gl.uniform2iv(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "3f") { // void uniform3f(WebGLUniformLocation? location, GLfloat x, GLfloat y, GLfloat z); gl.uniform3f(this.uniforms[key].uniformLocation, this.uniforms[key].value.x, this.uniforms[key].value.y, this.uniforms[key].value.z); } else if (type == "3fv") { // void uniform3fv(WebGLUniformLocation? location, Float32Array v); // void uniform3fv(WebGLUniformLocation? location, sequence<GLfloat> v); gl.uniform3fv(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "3i") { // void uniform3i(WebGLUniformLocation? location, GLint x, GLint y, GLint z); gl.uniform3i(this.uniforms[key].uniformLocation, this.uniforms[key].value.x, this.uniforms[key].value.y, this.uniforms[key].value.z); } else if (type == "3iv") { // void uniform3iv(WebGLUniformLocation? location, Int32Array v); // void uniform3iv(WebGLUniformLocation? location, sequence<long> v); gl.uniform3iv(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "4f") { // void uniform4f(WebGLUniformLocation? location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); gl.uniform4f(this.uniforms[key].uniformLocation, this.uniforms[key].value.x, this.uniforms[key].value.y, this.uniforms[key].value.z, this.uniforms[key].value.w); } else if (type == "4fv") { // void uniform4fv(WebGLUniformLocation? location, Float32Array v); // void uniform4fv(WebGLUniformLocation? location, sequence<GLfloat> v); gl.uniform4fv(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "4i") { // void uniform4i(WebGLUniformLocation? location, GLint x, GLint y, GLint z, GLint w); gl.uniform4i(this.uniforms[key].uniformLocation, this.uniforms[key].value.x, this.uniforms[key].value.y, this.uniforms[key].value.z, this.uniforms[key].value.w); } else if (type == "4iv") { // void uniform4iv(WebGLUniformLocation? location, Int32Array v); // void uniform4iv(WebGLUniformLocation? location, sequence<long> v); gl.uniform4iv(this.uniforms[key].uniformLocation, this.uniforms[key].value); } else if (type == "mat2") { // void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, Float32Array value); // void uniformMatrix2fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value); gl.uniformMatrix2fv(this.uniforms[key].uniformLocation, transpose, this.uniforms[key].value); } else if (type == "mat3") { // void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, Float32Array value); // void uniformMatrix3fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value); gl.uniformMatrix3fv(this.uniforms[key].uniformLocation, transpose, this.uniforms[key].value); } else if (type == "mat4") { // void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32Array value); // void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, sequence<GLfloat> value); gl.uniformMatrix4fv(this.uniforms[key].uniformLocation, transpose, this.uniforms[key].value); } else if (type == "sampler2D") { if (this.uniforms[key].value && this.uniforms[key].value.baseTexture.hasLoaded) { var texture = this.uniforms[key].value.baseTexture._glTexture; var image = this.uniforms[key].value.baseTexture.source; var format = gl.RGBA; if (this.uniforms[key].format && this.uniforms[key].format == 'luminance') { format = gl.LUMINANCE; } gl.activeTexture(gl['TEXTURE' + this.textureCount]); if (this.uniforms[key].wrap) { if (this.uniforms[key].wrap == 'no-repeat' || this.uniforms[key].wrap === false) { this.createGLTextureLinear(gl, image, texture); } else if (this.uniforms[key].wrap == 'repeat' || this.uniforms[key].wrap === true) { this.createGLTexture(gl, image, format, texture); } else if (this.uniforms[key].wrap == 'nearest-repeat') { this.createGLTextureNearestRepeat(gl, image, texture); } else if (this.uniforms[key].wrap == 'nearest') { this.createGLTextureNearest(gl, image, texture); } else if (this.uniforms[key].wrap == 'audio') { this.createAudioTexture(gl, texture); } else if (this.uniforms[key].wrap == 'keyboard') { this.createKeyboardTexture(gl, texture); } } else { this.createGLTextureLinear(gl, image, texture); } gl.uniform1i(this.uniforms[key].uniformLocation, this.textureCount); this.textureCount++; } } } }; /** * Binds the given texture and image data. The texture is set to REPEAT. * Code based on Effects.js from ShaderToy.com * @method PIXI.PixiShader#createGLTexture */ PIXI.PixiShader.prototype.createGLTexture = function(gl, image, format, texture) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); gl.generateMipmap(gl.TEXTURE_2D); } /** * Binds the given texture and image data. The texture is set to CLAMP_TO_EDGE. * Code based on Effects.js from ShaderToy.com * @method PIXI.PixiShader#createGLTextureLinear */ PIXI.PixiShader.prototype.createGLTextureLinear = function(gl, image, texture) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 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); } /** * Binds the given texture and image data. The texture is set to REPEAT with NEAREST. * Code based on Effects.js from ShaderToy.com * @method PIXI.PixiShader#createGLTextureNearestRepeat */ PIXI.PixiShader.prototype.createGLTextureNearestRepeat = function(gl, image, texture) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } /** * Binds the given texture and image data. The texture is set to CLAMP_TO_EDGE with NEAREST. * Code based on Effects.js from ShaderToy.com * @method PIXI.PixiShader#createGLTextureNearest */ PIXI.PixiShader.prototype.createGLTextureNearest = function(gl, image, texture) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 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); } /** * Binds the given texture data. The texture is set to CLAMP_TO_EDGE with LUMINANCE. Designed for use with real-time audio data. * Code based on Effects.js from ShaderToy.com * @method PIXI.PixiShader#createAudioTexture */ PIXI.PixiShader.prototype.createAudioTexture = function(gl, texture) { gl.bindTexture(gl.TEXTURE_2D, texture ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR ); 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.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, 512, 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, null); } /** * Binds the given texture data. The texture is set to CLAMP_TO_EDGE with LUMINANCE. Designed for use with keyboard input data. * Code based on Effects.js from ShaderToy.com * @method PIXI.PixiShader#createKeyboardTexture */ PIXI.PixiShader.prototype.createKeyboardTexture = function(gl, texture) { gl.bindTexture(gl.TEXTURE_2D, texture ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); 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.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, 256, 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, null); } PIXI.PixiShader.defaultVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", "uniform vec2 projectionVector;", "uniform vec2 offsetVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "const vec2 center = vec2(-1.0, 1.0);", "void main(void) {", "gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", "}" ];