wcjs-multiscreen-renderer
Version:
renderer for WebChimera.js, that supported multiscreen rendering
226 lines (195 loc) • 7.81 kB
JavaScript
function Texture(gl) {
this.gl = gl;
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.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);
}
Texture.prototype.bind = function (n, program, name) {
var gl = this.gl;
gl.activeTexture([gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2][n]);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.uniform1i(gl.getUniformLocation(program, name), n);
}
Texture.prototype.fill = function (width, height, data) {
var gl = this.gl;
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data);
}
function renderFrame(gl, videoFrame, width, height, uOffset, vOffset) {
gl.y.fill(width, height,
videoFrame.subarray(0, uOffset));
gl.u.fill(width >> 1, height >> 1,
videoFrame.subarray(uOffset, vOffset));
gl.v.fill(width >> 1, height >> 1,
videoFrame.subarray(vOffset, videoFrame.length));
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function setupCanvas(canvas, options) {
var gl =
canvas.getContext(
"webgl",
{preserveDrawingBuffer: Boolean(options.preserveDrawingBuffer)}
);
if (!gl)
return gl;
var program = gl.createProgram();
var vertexShaderSource = [
"attribute highp vec4 aVertexPosition;",
"attribute vec2 aTextureCoord;",
"varying highp vec2 vTextureCoord;",
"void main(void) {",
" gl_Position = aVertexPosition;",
" vTextureCoord = aTextureCoord;",
"}"
].join("\n");
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
var fragmentShaderSource = [
"precision highp float;",
"varying lowp vec2 vTextureCoord;",
"uniform sampler2D YTexture;",
"uniform sampler2D UTexture;",
"uniform sampler2D VTexture;",
"const mat4 YUV2RGB = mat4",
"(",
" 1.1643828125, 0, 1.59602734375, -.87078515625,",
" 1.1643828125, -.39176171875, -.81296875, .52959375,",
" 1.1643828125, 2.017234375, 0, -1.081390625,",
" 0, 0, 0, 1",
");",
"void main(void) {",
" gl_FragColor = vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;",
"}"
].join("\n");
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.log("Shader link failed.");
}
var vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute);
var textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord");
gl.enableVertexAttribArray(textureCoordAttribute);
var verticesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer);
gl.bufferData(gl.ARRAY_BUFFER,
new Float32Array([1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0]),
gl.STATIC_DRAW);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER,
new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]),
gl.STATIC_DRAW);
gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
gl.y = new Texture(gl);
gl.u = new Texture(gl);
gl.v = new Texture(gl);
gl.y.bind(0, program, "YTexture");
gl.u.bind(1, program, "UTexture");
gl.v.bind(2, program, "VTexture");
return gl;
}
function frameSetup(canvas, gl, width, height) {
canvas.width = width;
canvas.height = height;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}
module.exports = {
main_context: null,
contexts: [],
vlc: null,
options_g: {
fallbackRenderer: false,
preserveDrawingBuffer: false
},
setMainContext: function (vlc, context) {
this.main_context = context;
this.vlc = vlc;
this.main_context.gl = setupCanvas(context.canvas, this.options_g);
},
addAdditionalContext: function (context) {
for (var i = 0; i < this.contexts.length; i++) {
if (this.contexts[i].id == context.id) {
this.contexts[i] = context;
this.contexts[i].gl = setupCanvas(context.canvas, this.options_g);
return;
}
}
context.gl = setupCanvas(context.canvas, this.options_g);
this.contexts.push(context);
},
removeContext: function (context) {
for (var i = 0; i < this.contexts.length; i++) {
if (this.contexts[i].id == context.id) {
this.clearCanvas(this.contexts[i]);
this.contexts.splice(i, 1);
return;
}
}
},
deinit: function (only_additional) {
do {
var context = this.contexts.pop;
this.clearCanvas(context);
} while (this.contexts.length > 0);
if (only_additional == true) {
return;
}
this.clearCanvas(this.main_context);
},
init: function () {
var that = this;
if (this.main_context == null) {
console.log("Set main context first");
return;
}
this.vlc.onFrameReady =
function (frame) {
if (frame.width != that.main_context.canvas.width || frame.height != that.main_context.canvas.height) {
frameSetup(that.main_context.canvas, that.main_context.gl, frame.width, frame.height);
}
renderFrame(that.main_context.gl, frame, frame.width, frame.height, frame.uOffset, frame.vOffset);
for (var i = 0; i < that.contexts.length; i++) {
if (frame.width != that.contexts[i].canvas.width || frame.height != that.contexts[i].canvas.height) {
frameSetup(that.contexts[i].canvas, that.contexts[i].gl, frame.width, frame.height);
}
renderFrame(that.contexts[i].gl, frame, frame.width, frame.height, frame.uOffset, frame.vOffset);
}
};
},
clearCanvas: function (context) {
var gl = context.canvas.gl;
var arr1 = new Uint8Array(1);
var arr2 = new Uint8Array(1);
arr1[0] = 0;
arr2[0] = 128;
gl.y.fill(1, 1, arr1);
gl.u.fill(1, 1, arr2);
gl.v.fill(1, 1, arr2);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// for (var i = 0; i < this.contexts.length; i++) {
// gl = this.contexts[i].canvas.gl;
// arr1 = new Uint8Array(1);
// arr2 = new Uint8Array(1);
//
// arr1[0] = 0;
// arr2[0] = 128;
//
// gl.y.fill(1, 1, arr1);
// gl.u.fill(1, 1, arr2);
// gl.v.fill(1, 1, arr2);
//
// gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// }
},
};