mirror of
https://github.com/photonstorm/phaser
synced 2024-12-26 13:03:36 +00:00
661 lines
20 KiB
JavaScript
661 lines
20 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @copyright 2019 Photon Storm Ltd.
|
|
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
|
|
*/
|
|
|
|
var Class = require('../../utils/Class');
|
|
var Components = require('../components');
|
|
var GameObject = require('../GameObject');
|
|
var Merge = require('../../utils/object/Merge');
|
|
var ShaderRender = require('./ShaderRender');
|
|
var TransformMatrix = require('../components/TransformMatrix');
|
|
|
|
/**
|
|
* @classdesc
|
|
* A Shader Game Object.
|
|
*
|
|
* TODO:
|
|
*
|
|
* Test with sampler2D shader
|
|
*
|
|
* @class Shader
|
|
* @extends Phaser.GameObjects.GameObject
|
|
* @memberof Phaser.GameObjects
|
|
* @constructor
|
|
* @webglOnly
|
|
* @since 3.17.0
|
|
*
|
|
* @extends Phaser.GameObjects.Components.ComputedSize
|
|
* @extends Phaser.GameObjects.Components.Depth
|
|
* @extends Phaser.GameObjects.Components.GetBounds
|
|
* @extends Phaser.GameObjects.Components.Mask
|
|
* @extends Phaser.GameObjects.Components.Origin
|
|
* @extends Phaser.GameObjects.Components.ScrollFactor
|
|
* @extends Phaser.GameObjects.Components.Transform
|
|
* @extends Phaser.GameObjects.Components.Visible
|
|
*
|
|
* @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time.
|
|
* @param {string} key -
|
|
* @param {number} [x=0] - The horizontal position of this Game Object in the world.
|
|
* @param {number} [y=0] - The vertical position of this Game Object in the world.
|
|
* @param {number} [width=128] - The width of the Game Object.
|
|
* @param {number} [height=128] - The height of the Game Object.
|
|
*/
|
|
var Shader = new Class({
|
|
|
|
Extends: GameObject,
|
|
|
|
Mixins: [
|
|
Components.ComputedSize,
|
|
Components.Depth,
|
|
Components.GetBounds,
|
|
Components.Mask,
|
|
Components.Origin,
|
|
Components.ScrollFactor,
|
|
Components.Transform,
|
|
Components.Visible,
|
|
ShaderRender
|
|
],
|
|
|
|
initialize:
|
|
|
|
function Shader (scene, key, x, y, width, height)
|
|
{
|
|
if (x === undefined) { x = 0; }
|
|
if (y === undefined) { y = 0; }
|
|
if (width === undefined) { width = 128; }
|
|
if (height === undefined) { height = 128; }
|
|
|
|
GameObject.call(this, scene, 'Shader');
|
|
|
|
/**
|
|
* This Game Object cannot have a blend mode, so skip all checks.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#blendMode
|
|
* @type {integer}
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
this.blendMode = -1;
|
|
|
|
/**
|
|
* The underlying shader object being used.
|
|
* Empty by default and set during a call to the `setShader` method.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#shader
|
|
* @type {Phaser.Display.Shader}
|
|
* @since 3.17.0
|
|
*/
|
|
this.shader;
|
|
|
|
var renderer = scene.sys.renderer;
|
|
|
|
/**
|
|
* A reference to the current renderer.
|
|
* Shaders only work with the WebGL Renderer.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#renderer
|
|
* @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)}
|
|
* @since 3.17.0
|
|
*/
|
|
this.renderer = renderer;
|
|
|
|
/**
|
|
* The WebGL context belonging to the renderer.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#gl
|
|
* @type {WebGLRenderingContext}
|
|
* @since 3.17.0
|
|
*/
|
|
this.gl = renderer.gl;
|
|
|
|
/**
|
|
* Raw byte buffer of vertices this Shader uses.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#vertexData
|
|
* @type {ArrayBuffer}
|
|
* @since 3.17.0
|
|
*/
|
|
this.vertexData = new ArrayBuffer(6 * (Float32Array.BYTES_PER_ELEMENT * 2));
|
|
|
|
/**
|
|
* The WebGL vertex buffer object this shader uses.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#vertexBuffer
|
|
* @type {WebGLBuffer}
|
|
* @since 3.17.0
|
|
*/
|
|
this.vertexBuffer = renderer.createVertexBuffer(this.vertexData.byteLength, this.gl.STREAM_DRAW);
|
|
|
|
/**
|
|
* The WebGL shader program this shader uses.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#program
|
|
* @type {WebGLProgram}
|
|
* @since 3.17.0
|
|
*/
|
|
this.program = null;
|
|
|
|
/**
|
|
* Uint8 view to the vertex raw buffer. Used for uploading vertex buffer resources to the GPU.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#bytes
|
|
* @type {Uint8Array}
|
|
* @since 3.17.0
|
|
*/
|
|
this.bytes = new Uint8Array(this.vertexData);
|
|
|
|
/**
|
|
* Float32 view of the array buffer containing the shaders vertices.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#vertexViewF32
|
|
* @type {Float32Array}
|
|
* @since 3.17.0
|
|
*/
|
|
this.vertexViewF32 = new Float32Array(this.vertexData);
|
|
|
|
/**
|
|
* A temporary Transform Matrix, re-used internally during batching.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#_tempMatrix1
|
|
* @private
|
|
* @type {Phaser.GameObjects.Components.TransformMatrix}
|
|
* @since 3.17.0
|
|
*/
|
|
this._tempMatrix1 = new TransformMatrix();
|
|
|
|
/**
|
|
* A temporary Transform Matrix, re-used internally during batching.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#_tempMatrix2
|
|
* @private
|
|
* @type {Phaser.GameObjects.Components.TransformMatrix}
|
|
* @since 3.17.0
|
|
*/
|
|
this._tempMatrix2 = new TransformMatrix();
|
|
|
|
/**
|
|
* A temporary Transform Matrix, re-used internally during batching.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#_tempMatrix3
|
|
* @private
|
|
* @type {Phaser.GameObjects.Components.TransformMatrix}
|
|
* @since 3.17.0
|
|
*/
|
|
this._tempMatrix3 = new TransformMatrix();
|
|
|
|
/**
|
|
* The view matrix the shader uses during rendering.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#viewMatrix
|
|
* @type {Float32Array}
|
|
* @readonly
|
|
* @since 3.17.0
|
|
*/
|
|
this.viewMatrix = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]);
|
|
|
|
/**
|
|
* The projection matrix the shader uses during rendering.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#projectionMatrix
|
|
* @type {Float32Array}
|
|
* @readonly
|
|
* @since 3.17.0
|
|
*/
|
|
this.projectionMatrix = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]);
|
|
|
|
/**
|
|
* The default uniform mappings. These can be added to (or replaced) by specifying your own uniforms when
|
|
* creating this shader game object. The uniforms are updated automatically during the render step.
|
|
*
|
|
* The defaults are:
|
|
*
|
|
* `resolution` (2f) - Set to the size of this shader.
|
|
* `time` (1f) - The elapsed game time, in seconds.
|
|
* `mouse` (2f) - If a pointer has been bound (with `setPointer`), this uniform contains its position each frame.
|
|
* `date` (4fv) - A vec4 containing the year, month, day and time in seconds.
|
|
* `sampleRate` (1f) - Sound sample rate. 44100 by default.
|
|
* `iChannel0...3` (sampler2D) - Input channels 0 to 3. `null` by default.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#uniforms
|
|
* @type {any}
|
|
* @since 3.17.0
|
|
*/
|
|
this.uniforms = {};
|
|
|
|
/**
|
|
* The pointer bound to this shader, if any.
|
|
* Set via the chainable `setPointer` method, or by modifying this property directly.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#pointer
|
|
* @type {Phaser.Input.Pointer}
|
|
* @since 3.17.0
|
|
*/
|
|
this.pointer = null;
|
|
|
|
/**
|
|
* The cached width of the renderer.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#_rendererWidth
|
|
* @type {number}
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
this._rendererWidth = renderer.width;
|
|
|
|
/**
|
|
* The cached height of the renderer.
|
|
*
|
|
* @name Phaser.GameObjects.Shader#_rendererHeight
|
|
* @type {number}
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
this._rendererHeight = renderer.height;
|
|
|
|
var gl = this.gl;
|
|
|
|
/**
|
|
* Internal gl function mapping for uniform look-up.
|
|
* https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/uniform
|
|
*
|
|
* @name Phaser.GameObjects.Shader#_glFuncMap
|
|
* @type {any}
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
this._glFuncMap = {
|
|
|
|
mat2: { func: gl.uniformMatrix2fv, length: 1, matrix: true },
|
|
mat3: { func: gl.uniformMatrix3fv, length: 1, matrix: true },
|
|
mat4: { func: gl.uniformMatrix4fv, length: 1, matrix: true },
|
|
|
|
'1f': { func: gl.uniform1f, length: 1 },
|
|
'1fv': { func: gl.uniform1fv, length: 1 },
|
|
'1i': { func: gl.uniform1i, length: 1 },
|
|
'1iv': { func: gl.uniform1iv, length: 1 },
|
|
|
|
'2f': { func: gl.uniform2f, length: 2 },
|
|
'2fv': { func: gl.uniform2fv, length: 1 },
|
|
'2i': { func: gl.uniform2i, length: 2 },
|
|
'2iv': { func: gl.uniform2iv, length: 1 },
|
|
|
|
'3f': { func: gl.uniform3f, length: 3 },
|
|
'3fv': { func: gl.uniform3fv, length: 1 },
|
|
'3i': { func: gl.uniform3i, length: 3 },
|
|
'3iv': { func: gl.uniform3iv, length: 1 },
|
|
|
|
'4f': { func: gl.uniform4f, length: 4 },
|
|
'4fv': { func: gl.uniform4fv, length: 1 },
|
|
'4i': { func: gl.uniform4i, length: 4 },
|
|
'4iv': { func: gl.uniform4iv, length: 1 }
|
|
|
|
};
|
|
|
|
this.setPosition(x, y);
|
|
this.setSize(width, height);
|
|
this.setOrigin(0.5, 0.5);
|
|
this.setShader(key);
|
|
this.projOrtho(0, renderer.width, renderer.height, 0);
|
|
},
|
|
|
|
/**
|
|
* Sets the fragment and, optionally, the vertex shader source code that this Shader will use.
|
|
* This will immediately delete the active shader program, if set, and then create a new one
|
|
* with the given source. Finally, the shader uniforms are initialized.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#setShader
|
|
* @since 3.17.0
|
|
*
|
|
* @param {string} key - The key of the shader stored in the shader cache to use.
|
|
*
|
|
* @return {this} This Shader instance.
|
|
*/
|
|
setShader: function (key)
|
|
{
|
|
this.shader = this.scene.sys.cache.shader.get(key);
|
|
|
|
var gl = this.gl;
|
|
var renderer = this.renderer;
|
|
|
|
if (this.program)
|
|
{
|
|
gl.deleteProgram(this.program);
|
|
}
|
|
|
|
var program = renderer.createProgram(this.shader.vertexSrc, this.shader.fragmentSrc);
|
|
|
|
renderer.setMatrix4(program, 'uViewMatrix', false, this.viewMatrix);
|
|
renderer.setMatrix4(program, 'uProjectionMatrix', false, this.projectionMatrix);
|
|
|
|
this.program = program;
|
|
|
|
var d = new Date();
|
|
|
|
this.uniforms = Merge(this.shader.uniforms, {
|
|
resolution: { type: '2f', value: { x: this.width, y: this.height }},
|
|
time: { type: '1f', value: 0 },
|
|
mouse: { type: '2f', value: { x: this.width / 2, y: this.height / 2 } },
|
|
date: { type: '4fv', value: [ d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds() ] },
|
|
sampleRate: { type: '1f', value: 44100.0 },
|
|
iChannel0: { type: 'sampler2D', value: null, textureData: { repeat: true } },
|
|
iChannel1: { type: 'sampler2D', value: null, textureData: { repeat: true } },
|
|
iChannel2: { type: 'sampler2D', value: null, textureData: { repeat: true } },
|
|
iChannel3: { type: 'sampler2D', value: null, textureData: { repeat: true } }
|
|
});
|
|
|
|
this.initUniforms();
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Binds a Phaser Pointer object to this Shader.
|
|
*
|
|
* The screen position of the pointer will be set in to the shaders `mouse` uniform
|
|
* automatically every frame. Call this method with no arguments to unbind the pointer.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#setPointer
|
|
* @since 3.17.0
|
|
*
|
|
* @param {Phaser.Input.Pointer} [pointer] - The Pointer to bind to this shader.
|
|
*
|
|
* @return {this} This Shader instance.
|
|
*/
|
|
setPointer: function (pointer)
|
|
{
|
|
this.pointer = pointer;
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Sets this shader to use an orthographic projection matrix.
|
|
* This matrix is stored locally in the `projectionMatrix` property,
|
|
* as well as being bound to the `uProjectionMatrix` uniform.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#projOrtho
|
|
* @since 3.17.0
|
|
*
|
|
* @param {number} left - The left value.
|
|
* @param {number} right - The right value.
|
|
* @param {number} bottom - The bottom value.
|
|
* @param {number} top - The top value.
|
|
*/
|
|
projOrtho: function (left, right, bottom, top)
|
|
{
|
|
var near = -1000;
|
|
var far = 1000;
|
|
|
|
var leftRight = 1 / (left - right);
|
|
var bottomTop = 1 / (bottom - top);
|
|
var nearFar = 1 / (near - far);
|
|
|
|
var pm = this.projectionMatrix;
|
|
|
|
pm[0] = -2 * leftRight;
|
|
pm[5] = -2 * bottomTop;
|
|
pm[10] = 2 * nearFar;
|
|
pm[12] = (left + right) * leftRight;
|
|
pm[13] = (top + bottom) * bottomTop;
|
|
pm[14] = (far + near) * nearFar;
|
|
|
|
var program = this.program;
|
|
|
|
this.renderer.setMatrix4(program, 'uProjectionMatrix', false, this.projectionMatrix);
|
|
|
|
this._rendererWidth = right;
|
|
this._rendererHeight = bottom;
|
|
},
|
|
|
|
// 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
|
|
|
|
/**
|
|
* Initializes all of the uniforms this shader uses.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#initUniforms
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
initUniforms: function ()
|
|
{
|
|
var gl = this.gl;
|
|
var map = this._glFuncMap;
|
|
var program = this.program;
|
|
|
|
for (var key in this.uniforms)
|
|
{
|
|
var uniform = this.uniforms[key];
|
|
|
|
var type = uniform.type;
|
|
var data = map[type];
|
|
|
|
if (type === 'sampler2D')
|
|
{
|
|
// this.initSampler2D(uniform);
|
|
}
|
|
else
|
|
{
|
|
uniform.glMatrix = data.matrix;
|
|
uniform.glValueLength = data.length;
|
|
uniform.glFunc = data.func;
|
|
uniform.uniformLocation = gl.getUniformLocation(program, key);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Synchronizes all of the uniforms this shader uses.
|
|
* Each uniforms gl function is called in turn.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#syncUniforms
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
syncUniforms: function ()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
var uniforms = this.uniforms;
|
|
var uniform;
|
|
var length;
|
|
var glFunc;
|
|
var location;
|
|
var value;
|
|
|
|
// var textureCount = 1;
|
|
|
|
for (var key in uniforms)
|
|
{
|
|
uniform = uniforms[key];
|
|
|
|
glFunc = uniform.glFunc;
|
|
length = uniform.glValueLength;
|
|
location = uniform.uniformLocation;
|
|
value = uniform.value;
|
|
|
|
if (length === 1)
|
|
{
|
|
if (uniform.glMatrix)
|
|
{
|
|
glFunc.call(gl, location, uniform.transpose, value);
|
|
}
|
|
else
|
|
{
|
|
glFunc.call(gl, location, value);
|
|
}
|
|
}
|
|
else if (length === 2)
|
|
{
|
|
glFunc.call(gl, location, value.x, value.y);
|
|
}
|
|
else if (length === 3)
|
|
{
|
|
glFunc.call(gl, location, value.x, value.y, value.z);
|
|
}
|
|
else if (length === 4)
|
|
{
|
|
glFunc.call(gl, location, value.x, value.y, value.z, value.w);
|
|
}
|
|
else if (uniform.type === 'sampler2D')
|
|
{
|
|
if (uniform._init)
|
|
{
|
|
// gl.activeTexture(gl['TEXTURE' + this.textureCount]);
|
|
|
|
// gl.bindTexture(gl.TEXTURE_2D, value.baseTexture._glTextures[gl.id]);
|
|
|
|
// gl.uniform1i(location, textureCount);
|
|
|
|
// textureCount++;
|
|
}
|
|
else
|
|
{
|
|
// this.initSampler2D(uniform);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Called automatically during render.
|
|
*
|
|
* This method performs matrix ITRS and then stores the resulting value in the `uViewMatrix` uniform.
|
|
* It then sets up the vertex buffer and shader, updates and syncs the uniforms and finally draws it
|
|
* to a single quad.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#load
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
load: function (matrix2D)
|
|
{
|
|
// ITRS
|
|
|
|
var program = this.program;
|
|
|
|
var x = -this._displayOriginX;
|
|
var y = -this._displayOriginY;
|
|
var width = this.width;
|
|
var height = this.height;
|
|
|
|
var vm = this.viewMatrix;
|
|
|
|
vm[0] = matrix2D[0];
|
|
vm[1] = matrix2D[1];
|
|
vm[4] = matrix2D[2];
|
|
vm[5] = matrix2D[3];
|
|
vm[8] = matrix2D[4];
|
|
vm[9] = matrix2D[5];
|
|
vm[12] = vm[0] * x + vm[4] * y;
|
|
vm[13] = vm[1] * x + vm[5] * y;
|
|
|
|
this.renderer.setMatrix4(program, 'uViewMatrix', false, this.viewMatrix);
|
|
|
|
// Bind
|
|
|
|
var gl = this.gl;
|
|
var vertexBuffer = this.vertexBuffer;
|
|
var renderer = this.renderer;
|
|
var vertexSize = Float32Array.BYTES_PER_ELEMENT * 2;
|
|
|
|
renderer.setProgram(program);
|
|
renderer.setVertexBuffer(vertexBuffer);
|
|
|
|
var location = gl.getAttribLocation(program, 'inPosition');
|
|
|
|
if (location !== -1)
|
|
{
|
|
gl.enableVertexAttribArray(location);
|
|
|
|
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, vertexSize, 0);
|
|
}
|
|
|
|
// Update common uniforms
|
|
|
|
var uniforms = this.uniforms;
|
|
var res = uniforms.resolution;
|
|
|
|
res.value.x = width;
|
|
res.value.y = height;
|
|
|
|
uniforms.time.value = renderer.game.loop.getDuration();
|
|
|
|
var pointer = this.pointer;
|
|
|
|
if (pointer)
|
|
{
|
|
var mouse = uniforms.mouse;
|
|
|
|
var px = pointer.x / width;
|
|
var py = 1 - pointer.y / height;
|
|
|
|
mouse.value.x = px.toFixed(2);
|
|
mouse.value.y = py.toFixed(2);
|
|
}
|
|
|
|
this.syncUniforms();
|
|
|
|
// Draw
|
|
|
|
var vf = this.vertexViewF32;
|
|
|
|
vf[3] = height;
|
|
vf[4] = width;
|
|
vf[5] = height;
|
|
vf[8] = width;
|
|
vf[9] = height;
|
|
vf[10] = width;
|
|
|
|
// Flush
|
|
|
|
var vertexCount = 6;
|
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.bytes.subarray(0, vertexCount * vertexSize));
|
|
|
|
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
|
|
},
|
|
|
|
/**
|
|
* A NOOP method so you can pass a Shader to a Container.
|
|
* Calling this method will do nothing. It is intentionally empty.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#setAlpha
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
setAlpha: function ()
|
|
{
|
|
},
|
|
|
|
/**
|
|
* A NOOP method so you can pass a Shader to a Container.
|
|
* Calling this method will do nothing. It is intentionally empty.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#setBlendMode
|
|
* @private
|
|
* @since 3.17.0
|
|
*/
|
|
setBlendMode: function ()
|
|
{
|
|
},
|
|
|
|
/**
|
|
* Removes all object references in this WebGL Pipeline and removes its program from the WebGL context.
|
|
*
|
|
* @method Phaser.GameObjects.Shader#destroy
|
|
* @since 3.17.0
|
|
*/
|
|
destroy: function ()
|
|
{
|
|
var gl = this.gl;
|
|
|
|
gl.deleteProgram(this.program);
|
|
gl.deleteBuffer(this.vertexBuffer);
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = Shader;
|