|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Logger from '@/common/logger/Logger'; |
|
|
import {Tracklet} from '@/common/tracker/Tracker'; |
|
|
import invariant from 'invariant'; |
|
|
import {CanvasForm} from 'pts'; |
|
|
import {AbstractEffect, EffectFrameContext, EffectInit} from './Effect'; |
|
|
|
|
|
export default abstract class BaseGLEffect extends AbstractEffect { |
|
|
protected _canvas: OffscreenCanvas | null = null; |
|
|
protected _gl: WebGL2RenderingContext | null = null; |
|
|
protected _program: WebGLProgram | null = null; |
|
|
|
|
|
protected _frameTextureUnit: number = 0; |
|
|
protected _frameTexture: WebGLTexture | null = null; |
|
|
|
|
|
protected vertexShaderSource: string = ''; |
|
|
protected fragmentShaderSource: string = ''; |
|
|
|
|
|
protected _vertexShader: WebGLShader | null = null; |
|
|
protected _fragmentShader: WebGLShader | null = null; |
|
|
|
|
|
async setup(init: EffectInit): Promise<void> { |
|
|
const {canvas, gl} = init; |
|
|
|
|
|
if (canvas != null && gl != null) { |
|
|
this._canvas = canvas; |
|
|
this._gl = gl; |
|
|
} |
|
|
|
|
|
invariant(this._gl !== null, 'WebGL2 context is required'); |
|
|
|
|
|
const program = this._gl.createProgram(); |
|
|
this._program = program; |
|
|
|
|
|
{ |
|
|
const vertexShader = this._gl.createShader(this._gl.VERTEX_SHADER); |
|
|
this._vertexShader = vertexShader; |
|
|
invariant(vertexShader !== null, 'vertexShader required'); |
|
|
this._gl.shaderSource(vertexShader, this.vertexShaderSource); |
|
|
this._gl.compileShader(vertexShader); |
|
|
invariant(program !== null, 'program required'); |
|
|
this._gl.attachShader(program, vertexShader); |
|
|
|
|
|
const fragmentShader = this._gl.createShader(this._gl.FRAGMENT_SHADER); |
|
|
this._fragmentShader = fragmentShader; |
|
|
invariant(fragmentShader !== null, 'fragmentShader required'); |
|
|
this._gl.shaderSource(fragmentShader, this.fragmentShaderSource); |
|
|
this._gl.compileShader(fragmentShader); |
|
|
this._gl.attachShader(program, fragmentShader); |
|
|
|
|
|
this._gl.linkProgram(program); |
|
|
|
|
|
if (!this._gl.getProgramParameter(program, this._gl.LINK_STATUS)) { |
|
|
Logger.error(this._gl.getShaderInfoLog(vertexShader)); |
|
|
Logger.error(this._gl.getShaderInfoLog(fragmentShader)); |
|
|
} |
|
|
} |
|
|
this._gl.useProgram(program); |
|
|
|
|
|
this.setupBuffers(this._gl); |
|
|
this.setupUniforms(this._gl, program, init); |
|
|
} |
|
|
|
|
|
apply(form: CanvasForm, context: EffectFrameContext, _tracklets: Tracklet[]) { |
|
|
const gl = this._gl; |
|
|
invariant(gl !== null, 'WebGL2 context is required'); |
|
|
|
|
|
gl.clearColor(0.0, 0.0, 0.0, 1.0); |
|
|
gl.clear(gl.COLOR_BUFFER_BIT); |
|
|
|
|
|
gl.activeTexture(gl.TEXTURE0 + this._frameTextureUnit); |
|
|
gl.bindTexture(gl.TEXTURE_2D, this._frameTexture); |
|
|
gl.texImage2D( |
|
|
gl.TEXTURE_2D, |
|
|
0, |
|
|
gl.RGBA, |
|
|
context.frame.width, |
|
|
context.frame.height, |
|
|
0, |
|
|
gl.RGBA, |
|
|
gl.UNSIGNED_BYTE, |
|
|
context.frame, |
|
|
); |
|
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); |
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); |
|
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); |
|
|
|
|
|
|
|
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); |
|
|
|
|
|
const ctx = form.ctx; |
|
|
invariant(this._canvas !== null, 'canvas is required'); |
|
|
ctx.drawImage(this._canvas, 0, 0); |
|
|
} |
|
|
|
|
|
async cleanup(): Promise<void> { |
|
|
if (this._gl != null) { |
|
|
|
|
|
if (this._frameTexture != null) { |
|
|
this._gl.deleteTexture(this._frameTexture); |
|
|
this._frameTexture = null; |
|
|
} |
|
|
|
|
|
if ( |
|
|
this._program != null && |
|
|
this._vertexShader != null && |
|
|
this._fragmentShader != null |
|
|
) { |
|
|
this._gl.detachShader(this._program, this._vertexShader); |
|
|
this._gl.deleteShader(this._vertexShader); |
|
|
this._gl.detachShader(this._program, this._fragmentShader); |
|
|
this._gl.deleteShader(this._fragmentShader); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
protected setupBuffers(gl: WebGL2RenderingContext) { |
|
|
const vertexBufferData = new Float32Array([ |
|
|
1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, |
|
|
]); |
|
|
|
|
|
const texCoordBufferData = new Float32Array([ |
|
|
1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, |
|
|
]); |
|
|
|
|
|
const vertexBuffer = gl.createBuffer(); |
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); |
|
|
gl.bufferData(gl.ARRAY_BUFFER, vertexBufferData, gl.STATIC_DRAW); |
|
|
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); |
|
|
gl.enableVertexAttribArray(0); |
|
|
|
|
|
const texCoordBuffer = gl.createBuffer(); |
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); |
|
|
gl.bufferData(gl.ARRAY_BUFFER, texCoordBufferData, gl.STATIC_DRAW); |
|
|
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); |
|
|
gl.enableVertexAttribArray(1); |
|
|
} |
|
|
|
|
|
protected setupUniforms( |
|
|
gl: WebGL2RenderingContext, |
|
|
program: WebGLProgram, |
|
|
init: EffectInit, |
|
|
) { |
|
|
this._frameTexture = gl.createTexture(); |
|
|
|
|
|
gl.uniform1i( |
|
|
gl.getUniformLocation(program, 'uSampler'), |
|
|
this._frameTextureUnit, |
|
|
); |
|
|
|
|
|
gl.uniform2f( |
|
|
gl.getUniformLocation(program, 'uSize'), |
|
|
init.width, |
|
|
init.height, |
|
|
); |
|
|
} |
|
|
} |
|
|
|