Put shaders in their own separate files, implement glow and drop shadow in webgl.

This commit is contained in:
Michael Bebenita 2013-01-30 17:11:29 -08:00
Родитель f564b7ca36
Коммит 1fe394805a
11 изменённых файлов: 504 добавлений и 299 удалений

Просмотреть файл

@ -4,8 +4,12 @@
<title></title>
</head>
<body style="background-color: #2c2c2c">
<script>
SHUMWAY_ROOT = "../../src/";
</script>
<script src="../../src/avm2/util.js"></script>
<script src="../../src/swf/filter.js"></script>
<script src="../../src/swf/filters/gl.js"></script>
<script src="../../src/swf/filters/filter.js"></script>
<style TYPE="text/css">
body {
color: white;
@ -65,6 +69,9 @@
<td>
GL Glow
</td>
<td>
GL Shadow
</td>
<td>
GL Color
</td>
@ -79,6 +86,9 @@
<td>
<canvas width="256" height="256" id="glow-canvas-gl"></canvas>
</td>
<td>
<canvas width="256" height="256" id="shadow-canvas-gl"></canvas>
</td>
<td>
<canvas width="256" height="256" id="color-canvas-gl"></canvas>
</td>
@ -86,65 +96,6 @@
</table>
<img src="firefox.png" id="firefox" style="visibility: hidden;">
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform vec2 u_resolution;
uniform float u_flipY;
attribute vec2 a_textureCoordinate;
varying vec2 v_texCoord;
void main() {
vec2 p = (a_position / u_resolution) * 2.0 - 1.0;
gl_Position = vec4(p * vec2(1, u_flipY), 0, 1);
v_texCoord = a_textureCoordinate;
}
</script>
<script id="2d-blur-fragment-shader-h" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_textureSize;
varying vec2 v_texCoord;
void main() {
const int sampleRadius = 9;
const int samples = sampleRadius * 2 + 1;
vec2 one = vec2(1.0, 1.0) / u_textureSize;
vec4 color = vec4(0, 0, 0, 0);
for (int i = -sampleRadius; i <= sampleRadius; i++) {
color += texture2D(u_image, v_texCoord + vec2(float(i) * one.x, 0));
}
color /= float(samples);
gl_FragColor = color;
}
</script>
<script id="2d-blur-fragment-shader-v" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_textureSize;
varying vec2 v_texCoord;
void main() {
const int sampleRadius = 9;
const int samples = sampleRadius * 2 + 1;
vec2 one = vec2(1.0, 1.0) / u_textureSize;
vec4 color = vec4(0, 0, 0, 0);
for (int i = -sampleRadius; i <= sampleRadius; i++) {
color += texture2D(u_image, v_texCoord + vec2(0, float(i) * one.y));
}
color /= float(samples);
gl_FragColor = color;
}
</script>
<script id="2d-color-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
uniform mat4 u_matrix;
uniform vec4 u_vector;
varying vec2 v_texCoord;
void main() {
gl_FragColor = u_matrix * texture2D(u_image, v_texCoord) + u_vector;
}
</script>
<script src="filter_driver.js"></script>

Просмотреть файл

@ -9,7 +9,7 @@ function drawShape(ctx, length, x, y) {
ctx.fillStyle = '#ff0000';
ctx.strokeStyle = 'green';
ctx.translate(x, y);
ctx.rotate(r += 0.01);
ctx.rotate(r += 0.02);
ctx.rotate((Math.PI * 1 / 10));
ctx.beginPath();
for (var i = 5; i--;) {
@ -28,20 +28,6 @@ function drawShape(ctx, length, x, y) {
var colorMatrix4x5 = new Float32Array([0.748939, 1.044984, -0.793923, 0.000000, 0.000000, -0.008795, 0.713845, 0.294950, 0.000000, 0.000000, 0.827417, -0.240804, 0.413387, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000]);
function transpose(r, c, m) {
var result = new Float32Array(16);
for (var i = 0; i < r; i++) {
for (var j = 0; j < c; j++) {
result[j * r + i] = m[i * c + j];
}
}
return result;
}
var colorMatrix5x4 = transpose(4, 5, colorMatrix4x5);
var colorMatrix4x4 = colorMatrix5x4.subarray(0, 16);
var colorMatrixVector = colorMatrix5x4.subarray(16, 20);
var blurCanvasJS = document.getElementById("blur-canvas-js");
var blurCanvasGL = document.getElementById("blur-canvas-gl");
@ -55,73 +41,14 @@ var colorCanvasJS = document.getElementById("color-canvas-js");
var colorCanvasGL = document.getElementById("color-canvas-gl");
var glCanvas = document.getElementById("canvas-gl");
var glFilters = new WebGLFilters(glCanvas);
var gl = new WebGLCanvas(glCanvas);
var vShader = gl.createShaderFromElement("2d-vertex-shader");
var blurFragmentShaderH = gl.createShaderFromElement("2d-blur-fragment-shader-h");
var blurFragmentShaderV = gl.createShaderFromElement("2d-blur-fragment-shader-v");
var colorFragmentShader = gl.createShaderFromElement("2d-color-fragment-shader");
var blurProgramH = gl.createProgram([vShader, blurFragmentShaderH]);
var blurProgramV = gl.createProgram([vShader, blurFragmentShaderV]);
var colorProgram = gl.createProgram([vShader, colorFragmentShader]);
var texture = gl.createTexture();
var vertices = gl.createVertexBuffer(gl.rectangleVertices(width, height));
var textureCoordinates = gl.createVertexBuffer(gl.rectangleTextureCoordinates());
gl.useProgram(blurProgramH);
gl.setUniform2f(blurProgramH, "u_resolution", width, height);
gl.setUniform2f(blurProgramH, "u_textureSize", width, height);
gl.setUniform1f(blurProgramH, "u_flipY", 1);
gl.setVertexAttribute(blurProgramH, "a_position", vertices);
gl.setVertexAttribute(blurProgramH, "a_textureCoordinate", textureCoordinates);
gl.useProgram(blurProgramV);
gl.setUniform2f(blurProgramV, "u_resolution", width, height);
gl.setUniform2f(blurProgramV, "u_textureSize", width, height);
gl.setUniform1f(blurProgramV, "u_flipY", -1);
gl.setVertexAttribute(blurProgramV, "a_textureCoordinate", textureCoordinates);
gl.setVertexAttribute(blurProgramV, "a_position", vertices);
gl.useProgram(colorProgram);
gl.setUniform2f(colorProgram, "u_resolution", width, height);
gl.setUniformMatrix4fv(colorProgram, "u_matrix", colorMatrix4x4);
gl.setUniform4fv(colorProgram, "u_vector", colorMatrixVector);
// gl.setUniform2f(colorProgram, "u_textureSize", canvas.width, canvas.height);
gl.setUniform1f(colorProgram, "u_flipY", -1);
gl.setVertexAttribute(colorProgram, "a_textureCoordinate", textureCoordinates);
gl.setVertexAttribute(colorProgram, "a_position", vertices);
var framebuffer = gl.createFramebuffer();
var firefoxImage = document.getElementById("firefox");
function blurGL(data, width, height) {
gl.initializeTexture(texture, width, height, new Uint8Array(data.buffer));
gl.useProgram(blurProgramH);
gl.bindTexture(texture);
gl.bindFramebuffer(framebuffer);
gl.drawTriangles(0, 6);
gl.useProgram(blurProgramV);
gl.bindTexture(framebuffer.texture);
gl.bindFramebuffer(null);
gl.drawTriangles(0, 6);
}
function colorGL(data, width, height) {
gl.initializeTexture(texture, width, height, new Uint8Array(data.buffer));
gl.useProgram(colorProgram);
gl.bindTexture(texture);
gl.bindFramebuffer(null);
gl.drawTriangles(0, 6);
}
function getImageData(straightAlpha) {
var imageData = context.getImageData(0, 0, width, height);
if (!straightAlpha) {
@ -137,26 +64,29 @@ function putImageData(imageData, context, straightAlpha) {
context.putImageData(imageData, 0, 0);
}
var runJS = true;
var runGL = true;
var run = {
blur: {
js: false,
gl: false
js: runJS && true,
gl: runGL && true
},
glow: {
js: false,
gl: false
js: runJS && true,
gl: runGL && true
},
shadow: {
js: true,
gl: false
js: runJS && true,
gl: runGL && true
},
color: {
js: false,
gl: false
js: runJS && true,
gl: runGL && true
}
};
var k = 0;
var frameCount = 0;
setInterval(function () {
@ -164,37 +94,54 @@ setInterval(function () {
context.fillStyle = "white";
// context.fillRect(0, 0, 256, 256);
context.clearRect(0, 0, 256, 256);
drawShape(context, 50, 120, 120);
drawShape(context, 50, 140, 140);
context.drawImage(firefoxImage, 40, 40);
var imageData;
var blurX = 10, blurY = 10;
if (run.blur.js) {
// Run JS Blur Filter
imageData = getImageData();
blurFilter(imageData.data, width, height, 10, 10);
blurFilter(imageData.data, width, height, blurX, blurY);
putImageData(imageData, blurCanvasJS.getContext('2d'));
}
if (run.blur.gl) {
// Run WebGL Blur Filter
imageData = getImageData(true);
blurGL(imageData.data, width, height);
glFilters.blurFilter(imageData.data, width, height);
drawImage(glCanvas, blurCanvasGL);
}
if (run.glow.js) {
imageData = getImageData();
glowFilter(imageData.data, width, height, [0, 0, 255, 0], 20, 20, 1);
glowFilter(imageData.data, width, height, [0, 0, 255, 0], blurX, blurY, 1);
putImageData(imageData, glowCanvasJS.getContext('2d'));
}
if (run.glow.gl) {
imageData = getImageData(true);
glFilters.glowFilter(imageData.data, width, height, [0, 0, 255, 255], blurX, blurY, 1);
drawImage(glCanvas, glowCanvasGL);
}
if (run.shadow.js) {
imageData = getImageData();
dropShadowFilter(imageData.data, width, height, [0, 0, 0, 0], 5, 5, Math.PI / 4, 10, 0.5);
dropShadowFilter(imageData.data, width, height, [0, 0, 255, 0], blurX, blurY, Math.PI / 4, 20, 0.5);
putImageData(imageData, shadowCanvasJS.getContext('2d'));
}
if (run.shadow.gl) {
imageData = getImageData();
glFilters.dropShadowFilter(imageData.data, width, height, [0, 0, 255, 255], blurX, blurY, Math.PI / 4, 20, 0.5);
drawImage(glCanvas, shadowCanvasGL);
}
colorMatrix4x5[0] = Math.sin(frameCount / 100);
if (run.color.js) {
// Run JS Color Filter
imageData = getImageData();
@ -205,10 +152,12 @@ setInterval(function () {
if (run.color.gl) {
// Run WebGL Color Filter
imageData = getImageData();
colorGL(imageData.data, width, height);
glFilters.colorFilter(imageData.data, width, height, colorMatrix4x5);
drawImage(glCanvas, colorCanvasGL);
}
frameCount ++;
}, 1000 / 60);
function drawImage(src, dst) {

Просмотреть файл

@ -5,6 +5,17 @@
* Resources: http://www.m2osw.com/swf_struct_any_filter#swf_filter_colormatrix
*/
function transpose(r, c, m) {
assert (r * c === m.length);
var result = new Float32Array(m.length);
for (var i = 0; i < r; i++) {
for (var j = 0; j < c; j++) {
result[j * r + i] = m[i * c + j];
}
}
return result;
}
/**
* Applies a blur box-filter, averaging pixel values in a box with radius (blurX x blurY).
*
@ -148,7 +159,7 @@ function blurFilterV(buffer, w, h, blurY) {
}
}
function getAlphaChannel(buffer, color) {
function alphaFilter(buffer, color) {
if (!color) {
color = [0, 0, 0, 0];
}
@ -232,7 +243,7 @@ function panFilter(buffer, w, h, angle, distance) {
}
function dropShadowFilter(buffer, w, h, color, blurX, blurY, angle, distance, strength) {
var tmp = getAlphaChannel(buffer, color);
var tmp = alphaFilter(buffer, color);
panFilter(tmp, w, h, angle, distance);
blurFilter(tmp, w, h, blurX, blurY);
scaleAlphaChannel(tmp, strength);
@ -240,6 +251,10 @@ function dropShadowFilter(buffer, w, h, color, blurX, blurY, angle, distance, st
buffer.set(tmp);
}
function clamp(n) {
return (((255 - n) >> 31) | n) & 0xFF;
}
/**
* Applies a color transformation. The |matrix| is a 5x5 matrix whose
* last row is always [0, 0, 0, 0, 1]. The |matrix| is specified in row-major order;
@ -252,10 +267,6 @@ function dropShadowFilter(buffer, w, h, color, blurX, blurY, angle, distance, st
*
*/
function clamp(n) {
return (((255 - n) >> 31) | n) & 0xFF;
}
function colorFilter(buffer, w, h, matrix) {
var r0 = matrix[0], r1 = matrix[1], r2 = matrix[2], r3 = matrix[3], r4 = matrix[4];
var g0 = matrix[5], g1 = matrix[6], g2 = matrix[7], g3 = matrix[8], g4 = matrix[9];
@ -273,157 +284,188 @@ function colorFilter(buffer, w, h, matrix) {
}
}
/**
* Yet another abstraction over WebGL APIs.
*/
var WebGLCanvas = (function () {
var WebGLFilters = (function () {
var shaderRoot = SHUMWAY_ROOT + "/swf/filters/shaders/";
function colorVector(color) {
return [color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255];
}
function makeTranslation(tx, ty) {
return transpose(3, 3, [
1, 0, tx,
0, 1, ty,
0, 0, 1
]);
}
var counter = 0;
function constructor(canvas) {
this.canvas = canvas;
var w = this.width = canvas.width;
var h = this.height = canvas.height;
assert (w && h && isPowerOfTwo(w) && isPowerOfTwo(h));
this.gl = this.canvas.getContext("experimental-webgl");
assert (this.gl);
assert (canvas);
var gl = this.gl = new WebGLCanvas(canvas);
var width = canvas.width;
var height = canvas.height;
function loadShader(name) {
return gl.createShaderFromFile(shaderRoot + name);
}
// Create shader programs.
var canvasVert = loadShader("canvas.vert");
this.blurHProgram = gl.createProgram([canvasVert, loadShader("blurh.frag")]);
this.blurVProgram = gl.createProgram([canvasVert, loadShader("blurv.frag")]);
this.colorProgram = gl.createProgram([canvasVert, loadShader("color.frag")]);
this.alphaProgram = gl.createProgram([canvasVert, loadShader("alpha.frag")]);
this.multiplyProgram = gl.createProgram([canvasVert, loadShader("multiply.frag")]);
this.identityProgram = gl.createProgram([canvasVert, loadShader("identity.frag")]);
this.programs = [
this.blurHProgram,
this.blurVProgram,
this.colorProgram,
this.alphaProgram,
this.multiplyProgram,
this.identityProgram
];
this.texture = gl.createTexture();
this.framebufferA = gl.createFramebuffer();
this.framebufferB = gl.createFramebuffer();
var vertices = gl.createVertexBuffer(gl.rectangleVertices(width, height));
var textureCoordinates = gl.createVertexBuffer(gl.rectangleTextureCoordinates());
var matrix = makeTranslation(0, 0);
this.programs.forEach(setDefaultAttributesAndUniforms);
function setDefaultAttributesAndUniforms(program) {
gl.useProgram(program);
if (gl.hasUniform(program, "u_time")) {
gl.setUniform1f(program, "u_time", 0.0);
}
gl.setUniformMatrix3fv(program, "u_transformMatrix", matrix);
gl.setUniform2f(program, "u_resolution", width, height);
gl.setUniform1f(program, "u_flipY", 1);
gl.setVertexAttribute(program, "a_textureCoordinate", textureCoordinates);
gl.setVertexAttribute(program, "a_position", vertices);
}
gl.useProgram(this.blurHProgram);
gl.setUniform2f(this.blurHProgram, "u_textureSize", width, height);
gl.useProgram(this.blurVProgram);
gl.setUniform2f(this.blurVProgram, "u_textureSize", width, height);
this.startTime = new Date();
}
constructor.prototype = {
rectangleVertices: function rectangleVertices(w, h) {
return new Float32Array([0, 0, w, 0, 0, h, 0, h, w, 0, w, h]);
getElapsedTime: function getElapsedTime() {
return new Date() - this.startTime;
},
rectangleTextureCoordinates: function rectangleTextureCoordinates() {
return new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);
},
useProgram: function (program) {
this.gl.useProgram(program);
},
setVertexAttribute: function setVertexAttribute(program, name, buffer) {
blurFilter: function blurFilterGL(buffer, w, h, blurX, blurY) {
var gl = this.gl;
var location = gl.getAttribLocation(program, name);
assert (location >= 0, "Attribute " + name + " not found.");
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
gl.bindFramebuffer(null);
gl.clear([0, 0, 0, 0]);
gl.initializeTexture(this.texture, w, h, new Uint8Array(buffer.buffer));
gl.bindTexture(this.texture);
gl.useProgram(this.blurHProgram);
gl.bindFramebuffer(this.framebufferA);
gl.clear([0, 0, 0, 0]);
gl.drawTriangles(0, 6);
gl.useProgram(this.blurVProgram);
gl.setUniform1f(this.blurVProgram, "u_flipY", -1);
gl.bindTexture(this.framebufferA.texture);
gl.bindFramebuffer(null);
gl.drawTriangles(0, 6);
gl.setUniform1f(this.blurVProgram, "u_flipY", 1);
},
setUniform4fv: function setUniform2f(program, name, vector) {
var gl = this.gl
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
gl.uniform4fv(location, vector);
colorFilter: function colorFilter(buffer, w, h, matrix) {
var colorMatrix5x4 = transpose(4, 5, matrix);
var colorMatrix4x4 = colorMatrix5x4.subarray(0, 16);
var colorMatrixVector = colorMatrix5x4.subarray(16, 20);
gl.initializeTexture(this.texture, w, h, new Uint8Array(buffer.buffer));
gl.useProgram(this.colorProgram);
gl.setUniformMatrix4fv(this.colorProgram, "u_colorMatrix", colorMatrix4x4);
gl.setUniform4fv(this.colorProgram, "u_vector", colorMatrixVector);
gl.setUniform1f(this.colorProgram, "u_flipY", -1);
gl.bindTexture(this.texture);
gl.bindFramebuffer(null);
gl.clear([0, 0, 0, 0]);
gl.drawTriangles(0, 6);
gl.setUniform1f(this.colorProgram, "u_flipY", 1);
},
setUniform2f: function setUniform2f(program, name, x, y) {
var gl = this.gl
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
gl.uniform2f(location, x, y);
alphaFilter: function alphaFilter(buffer, w, h, color) {
gl.initializeTexture(this.texture, w, h, new Uint8Array(buffer.buffer));
gl.useProgram(this.alphaProgram);
gl.setUniform4fv(this.alphaProgram, "u_color", colorVector(color));
gl.bindTexture(this.texture);
gl.bindFramebuffer(null);
gl.drawTriangles(0, 6);
},
setUniform1f: function setUniform2f(program, name, x) {
var gl = this.gl
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
gl.uniform1f(location, x);
glowFilter: function dropShadowFilter(buffer, w, h, color, blurX, blurY, strength) {
this.dropShadowFilter(buffer, w, h, color, blurX, blurY, 0, 0, strength);
},
setUniformMatrix4fv: function setUniform(program, name, matrix) {
var gl = this.gl
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
assert (matrix.length === 16, "Invalid matrix size.");
gl.uniformMatrix4fv(location, false, matrix);
},
createVertexBuffer: function createBuffer(vertices) {
var gl = this.gl;
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
return buffer;
},
createFramebuffer: function createFramebuffer() {
var gl = this.gl;
var texture = this.createTexture();
this.initializeTexture(texture, this.width, this.height, null);
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
framebuffer.texture = texture;
return framebuffer;
},
createTexture: function createAndBindTexture() {
var gl = this.gl;
var texture = gl.createTexture();
this.bindTexture(texture);
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);
return texture;
},
initializeTexture: function initializeTexture(texture, width, height, data) {
var gl = this.gl;
this.bindTexture(texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
},
bindTexture: function bindTexture(texture) {
var gl = this.gl;
gl.bindTexture(gl.TEXTURE_2D, texture);
},
bindFramebuffer: function bindFramebuffer(framebuffer) {
var gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
},
createShader: function createShader(gl, shaderType, shaderSource) {
var gl = this.gl;
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
var lastError = gl.getShaderInfoLog(shader);
unexpected("Cannot compile shader: " + lastError);
gl.deleteShader(shader);
return null;
}
return shader;
},
createShaderFromElement: function createShaderFromElement(id) {
var gl = this.gl;
var shaderScript = document.getElementById(id);
if (!shaderScript) {
unexpected("Shader Script Element: " + id + " not found.");
}
var shaderType;
switch (shaderScript.type) {
case "x-shader/x-vertex":
shaderType = gl.VERTEX_SHADER;
break;
case "x-shader/x-fragment":
shaderType = gl.FRAGMENT_SHADER;
break;
default:
throw "Shader Type: " + shaderScript.type + " not supported.";
break;
}
return this.createShader(gl, shaderType, shaderScript.text);
},
createProgram: function createProgram(shaders) {
var gl = this.gl;
var program = gl.createProgram();
shaders.forEach(function (shader) {
gl.attachShader(program, shader);
});
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
var lastError = gl.getProgramInfoLog(program);
unexpected("Cannot link program: " + lastError);
gl.deleteProgram(program);
}
return program;
},
drawTriangles: function drawArrays(first, count) {
var gl = this.gl;
gl.drawArrays(gl.TRIANGLES, first, count);
dropShadowFilter: function dropShadowFilter(buffer, w, h, color, blurX, blurY, angle, distance, strength) {
gl.bindFramebuffer(this.framebufferA);
gl.clear([0, 0, 0, 0]);
gl.bindFramebuffer(this.framebufferB);
gl.clear([0, 0, 0, 0]);
gl.bindFramebuffer(null);
gl.clear([0, 0, 0, 0]);
// Alpha
gl.initializeTexture(this.texture, w, h, new Uint8Array(buffer.buffer));
gl.useProgram(this.alphaProgram);
gl.setUniform4fv(this.alphaProgram, "u_color", colorVector(color));
gl.bindTexture(this.texture);
gl.bindFramebuffer(this.framebufferA);
gl.drawTriangles(0, 6);
// Blur H
gl.bindTexture(this.framebufferA.texture);
gl.useProgram(this.blurHProgram);
gl.bindFramebuffer(this.framebufferB);
gl.drawTriangles(0, 6);
// Blur V
gl.useProgram(this.blurVProgram);
gl.bindTexture(this.framebufferB.texture);
gl.bindFramebuffer(this.framebufferA);
gl.drawTriangles(0, 6);
// Multiply Alpha
gl.bindTexture(this.framebufferA.texture);
gl.useProgram(this.multiplyProgram);
var dy = (Math.sin(angle) * distance) | 0;
var dx = (Math.cos(angle) * distance) | 0;
gl.setUniformMatrix3fv(this.multiplyProgram, "u_transformMatrix", makeTranslation(dx, dy));
gl.setUniform4fv(this.multiplyProgram, "u_color", [strength, strength, strength, strength]);
gl.bindFramebuffer(this.framebufferB);
gl.drawTriangles(0, 6);
gl.bindTexture(this.texture);
gl.useProgram(this.identityProgram);
gl.enableBlend();
gl.gl.blendFunc(gl.gl.ONE, gl.gl.ONE_MINUS_SRC_ALPHA);
gl.bindFramebuffer(this.framebufferB);
gl.drawTriangles(0, 6);
gl.disableBlend();
gl.bindTexture(this.framebufferB.texture);
gl.useProgram(this.identityProgram);
gl.setUniform1f(this.identityProgram, "u_flipY", -1);
gl.bindFramebuffer(null);
gl.clear([0, 0, 0, 0]);
gl.drawTriangles(0, 6);
gl.setUniform1f(this.identityProgram, "u_flipY", 1);
return;
}
};
return constructor;
})();
function trace(s) {
console.info(s);
}

189
src/swf/filters/gl.js Normal file
Просмотреть файл

@ -0,0 +1,189 @@
/**
* Yet another abstraction over WebGL APIs.
*/
var WebGLCanvas = (function () {
function constructor(canvas) {
this.canvas = canvas;
var w = this.width = canvas.width;
var h = this.height = canvas.height;
assert (w && h && isPowerOfTwo(w) && isPowerOfTwo(h));
this.gl = this.canvas.getContext("experimental-webgl");
assert (this.gl);
}
constructor.prototype = {
rectangleVertices: function rectangleVertices(w, h) {
return new Float32Array([0, 0, w, 0, 0, h, 0, h, w, 0, w, h]);
},
rectangleTextureCoordinates: function rectangleTextureCoordinates() {
return new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);
},
useProgram: function (program) {
this.gl.useProgram(program);
},
hasUniform: function hasUniform(program, name) {
return !!this.gl.getUniformLocation(program, name);
},
setVertexAttribute: function setVertexAttribute(program, name, buffer) {
var gl = this.gl;
var location = gl.getAttribLocation(program, name);
assert (location >= 0, "Attribute " + name + " not found.");
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, 2, gl.FLOAT, false, 0, 0);
},
setUniform4fv: function setUniform2f(program, name, vector) {
var gl = this.gl;
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
gl.uniform4fv(location, vector);
},
setUniform2f: function setUniform2f(program, name, x, y) {
var gl = this.gl;
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
gl.uniform2f(location, x, y);
},
setUniform1f: function setUniform2f(program, name, value) {
var gl = this.gl;
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
gl.uniform1f(location, value);
},
setUniformMatrix4fv: function setUniform(program, name, matrix) {
var gl = this.gl;
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
assert (matrix.length === 16, "Invalid matrix size.");
gl.uniformMatrix4fv(location, false, matrix);
},
setUniformMatrix3fv: function setUniformMatrix3fv(program, name, matrix) {
var gl = this.gl;
var location = gl.getUniformLocation(program, name);
assert (location, "Uniform " + name + " not found.");
assert (matrix.length === 9, "Invalid matrix size.");
gl.uniformMatrix3fv(location, false, matrix);
},
createVertexBuffer: function createBuffer(vertices) {
var gl = this.gl;
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
return buffer;
},
createFramebuffer: function createFramebuffer() {
var gl = this.gl;
var texture = this.createTexture();
this.initializeTexture(texture, this.width, this.height, null);
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
framebuffer.texture = texture;
return framebuffer;
},
createTexture: function createAndBindTexture() {
var gl = this.gl;
var texture = gl.createTexture();
this.bindTexture(texture);
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);
return texture;
},
initializeTexture: function initializeTexture(texture, width, height, data) {
var gl = this.gl;
this.bindTexture(texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
},
bindTexture: function bindTexture(texture) {
var gl = this.gl;
gl.bindTexture(gl.TEXTURE_2D, texture);
},
bindFramebuffer: function bindFramebuffer(framebuffer) {
var gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
},
createShader: function createShader(gl, shaderType, shaderSource) {
var gl = this.gl;
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
var lastError = gl.getShaderInfoLog(shader);
unexpected("Cannot compile shader: " + lastError);
gl.deleteShader(shader);
return null;
}
return shader;
},
createShaderFromFile: function createShaderFromFile(file) {
var gl = this.gl;
var request = new XMLHttpRequest();
request.open("GET", file, false);
request.send();
assert (request.status === 200, "File : " + file + " not found.");
var shaderType;
if (file.endsWith(".vert")) {
shaderType = gl.VERTEX_SHADER;
} else if (file.endsWith(".frag")) {
shaderType = gl.FRAGMENT_SHADER;
} else {
throw "Shader Type: not supported.";
}
return this.createShader(gl, shaderType, request.responseText);
},
createShaderFromElement: function createShaderFromElement(id) {
var gl = this.gl;
var shaderScript = document.getElementById(id);
if (!shaderScript) {
unexpected("Shader Script Element: " + id + " not found.");
}
var shaderType;
switch (shaderScript.type) {
case "x-shader/x-vertex":
shaderType = gl.VERTEX_SHADER;
break;
case "x-shader/x-fragment":
shaderType = gl.FRAGMENT_SHADER;
break;
default:
throw "Shader Type: " + shaderScript.type + " not supported.";
break;
}
return this.createShader(gl, shaderType, shaderScript.text);
},
createProgram: function createProgram(shaders) {
var gl = this.gl;
var program = gl.createProgram();
shaders.forEach(function (shader) {
gl.attachShader(program, shader);
});
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
var lastError = gl.getProgramInfoLog(program);
unexpected("Cannot link program: " + lastError);
gl.deleteProgram(program);
}
return program;
},
drawTriangles: function drawArrays(first, count) {
var gl = this.gl;
gl.drawArrays(gl.TRIANGLES, first, count);
},
clear: function clear(color) {
var gl = this.gl;
gl.clearColor(color[0], color[1], color[2], color[3]);
gl.clear(gl.COLOR_BUFFER_BIT);
},
enableBlend: function enableBlend() {
var gl = this.gl;
gl.enable(gl.BLEND);
},
disableBlend: function disableBlend() {
var gl = this.gl;
gl.disable(gl.BLEND);
}
};
return constructor;
})();

Просмотреть файл

@ -0,0 +1,8 @@
precision mediump float;
uniform sampler2D u_image;
uniform vec4 u_color;
varying vec2 v_texCoord;
void main() {
// gl_FragColor = u_matrix * texture2D(u_image, v_texCoord) + u_vector;
gl_FragColor = u_color * texture2D(u_image, v_texCoord).a;
}

Просмотреть файл

@ -0,0 +1,15 @@
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_textureSize;
varying vec2 v_texCoord;
void main() {
const int sampleRadius = 10;
const int samples = sampleRadius * 2 + 1;
vec2 one = vec2(1.0, 1.0) / u_textureSize;
vec4 color = vec4(0, 0, 0, 0);
for (int i = -sampleRadius; i <= sampleRadius; i++) {
color += texture2D(u_image, v_texCoord + vec2(float(i) * one.x, 0));
}
color /= float(samples);
gl_FragColor = color;
}

Просмотреть файл

@ -0,0 +1,15 @@
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_textureSize;
varying vec2 v_texCoord;
void main() {
const int sampleRadius = 10;
const int samples = sampleRadius * 2 + 1;
vec2 one = vec2(1.0, 1.0) / u_textureSize;
vec4 color = vec4(0, 0, 0, 0);
for (int i = -sampleRadius; i <= sampleRadius; i++) {
color += texture2D(u_image, v_texCoord + vec2(0, float(i) * one.y));
}
color /= float(samples);
gl_FragColor = color;
}

Просмотреть файл

@ -0,0 +1,14 @@
attribute vec2 a_position;
uniform vec2 u_resolution;
uniform float u_flipY;
uniform float u_time;
attribute vec2 a_textureCoordinate;
uniform mat3 u_transformMatrix;
varying vec2 v_texCoord;
void main() {
vec2 position = ((u_transformMatrix * vec3(a_position, 1.0)).xy / u_resolution) * 2.0 - 1.0;
position *= vec2(1.0, u_flipY);
gl_Position = vec4(vec3(position, 1.0), 1.0);
v_texCoord = a_textureCoordinate;
}

Просмотреть файл

@ -0,0 +1,8 @@
precision mediump float;
uniform sampler2D u_image;
uniform mat4 u_colorMatrix;
uniform vec4 u_vector;
varying vec2 v_texCoord;
void main() {
gl_FragColor = u_colorMatrix * texture2D(u_image, v_texCoord) + u_vector;
}

Просмотреть файл

@ -0,0 +1,6 @@
precision mediump float;
uniform sampler2D u_image;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
}

Просмотреть файл

@ -0,0 +1,8 @@
precision mediump float;
uniform sampler2D u_image;
uniform vec4 u_color;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord) * vec4(1.9, 1.0, 1.0, 1.0);
gl_FragColor = texture2D(u_image, v_texCoord) * u_color;
}