gl renderer: use a frame buffer

webgl-renderer
cpsdqs 7 years ago
parent 46a3bddcb0
commit 7fe6495a27
Signed by untrusted user: cpsdqs
GPG Key ID: 3F59586BB7448DD1
  1. 1
      js/term/screen_layout.js
  2. 165
      js/term/webgl_renderer.js

@ -13,6 +13,7 @@ module.exports = class ScreenLayout extends EventEmitter {
try {
this.renderer = new WebGLRenderer(this.canvas)
} catch (err) {
console.error(err)
this.renderer = new CanvasRenderer(this.canvas)
}

@ -56,19 +56,23 @@ module.exports = class WebGLRenderer extends EventEmitter {
this.reverseVideo = false
this.hasBlinkingCells = false
this.statusScreen = null
this.backgroundImage = null
this.blinkStyleOn = false
this.blinkInterval = null
this.cursorBlinkOn = false
this.cursorBlinkInterval = null
this.redrawLoop = false
this.resetDrawn(100, 100)
this.initTime = Date.now()
// start blink timers
this.init()
// start loops and timers
this.resetBlink()
this.resetCursorBlink()
this.init()
this.startDrawLoop()
}
render (reason, data) {
@ -82,7 +86,13 @@ module.exports = class WebGLRenderer extends EventEmitter {
}
resetDrawn (width, height) {
this.gl.clearColor(...this.getColor(this.defaultBG))
if (this.backgroundImage) {
this.gl.clearColor(0, 0, 0, 0)
this.canvas.style.backgroundColor = getColor(this.defaultBG, this.palette)
} else {
this.gl.clearColor(...this.getColor(this.defaultBG))
this.canvas.style.backgroundColor = null
}
if (width && height) {
this.gl.viewport(0, 0, width, height)
}
@ -283,6 +293,43 @@ void main() {
}
`)
let fboShader = this.compileShader(`
precision mediump float;
attribute vec2 position;
uniform mat4 projection;
varying highp vec2 tex_coord;
void main() {
gl_Position = projection * vec4(position, 0.0, 1.0);
tex_coord = position;
}
`, `
precision highp float;
uniform sampler2D texture;
uniform vec2 pixel_scale;
uniform float time;
varying highp vec2 tex_coord;
void main() {
gl_FragColor = texture2D(texture, tex_coord);
/* uncomment for CRT-ish effect
vec4 sum = vec4(0);
for (int i = -4; i <= 4; i++) {
for (int j = -4; j <= 4; j++) {
sum += texture2D(texture, tex_coord + vec2(j, i) * pixel_scale) * 0.07;
}
}
float factor = 0.05 + 0.02 * sin(time * 5.0);
if (mod(tex_coord.y / pixel_scale.y, 10.0) < 5.0) {
factor += 0.1;
}
float beam_y = (mod(-time, 9.0) - 1.5) / 6.0;
if (abs(tex_coord.y - beam_y) < 0.05) {
factor += 0.2 * cos((tex_coord.y - beam_y) / 0.05 * 1.57);
}
gl_FragColor = sum * sum * factor + texture2D(texture, tex_coord); */
}
`)
this.bgShader = {
shader: bgShader,
attributes: {
@ -314,6 +361,19 @@ void main() {
}
}
this.fboShader = {
shader: fboShader,
attributes: {
position: gl.getAttribLocation(fboShader, 'position')
},
uniforms: {
projection: gl.getUniformLocation(fboShader, 'projection'),
pixelScale: gl.getUniformLocation(fboShader, 'pixel_scale'),
time: gl.getUniformLocation(fboShader, 'time'),
texture: gl.getUniformLocation(fboShader, 'texture')
}
}
let buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
@ -333,14 +393,50 @@ void main() {
this.drawSquare = () => {
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
}
// frame buffers
let maxBuffers = gl.getParameter(gl.getExtension('WEBGL_draw_buffers').MAX_COLOR_ATTACHMENTS_WEBGL)
let createBuffer = i => {
let buffer = gl.createFramebuffer()
let texture = gl.createTexture()
gl.bindFramebuffer(gl.FRAMEBUFFER, buffer)
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, texture, 0)
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)
return { buffer, texture }
}
if (maxBuffers >= 2) {
this.buffers = {
drawing: createBuffer(0),
display: createBuffer(1)
}
} else {
let buffer = createBuffer(0)
this.buffers = { drawing: buffer, display: buffer }
}
}
draw (reason) {
const { gl, width, height, padding, devicePixelRatio, statusScreen } = this
let { screen, screenFG, screenBG, screenAttrs } = this
// ;[this.buffers.drawing, this.buffers.display] = [this.buffers.display, this.buffers.drawing]
let drawingBuffer = this.buffers.drawing
gl.bindFramebuffer(gl.FRAMEBUFFER, drawingBuffer.buffer)
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, drawingBuffer.texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.drawingBufferWidth, gl.drawingBufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
if (statusScreen) {
this.startDrawLoop()
this.redrawLoop = true
screen = new Array(width * height).fill(' ')
screenFG = new Array(width * height).fill(this.defaultFG)
@ -361,7 +457,7 @@ void main() {
screen[i] = '*'
}
}
}
} else this.redrawLoop = false
if (this.debug && this._debug) this._debug.drawStart(reason)
@ -401,12 +497,12 @@ void main() {
if (!(cell in screen)) continue
if (statusScreen) isCursor = false
// let isDefaultBG = false
let isDefaultBG = false
if (!(attrs & ATTR_FG)) fg = this.defaultFG
if (!(attrs & ATTR_BG)) {
bg = this.defaultBG
// isDefaultBG = true
isDefaultBG = true
}
if (attrs & ATTR_INVERSE) [fg, bg] = [bg, fg] // swap - reversed character colors
@ -423,20 +519,22 @@ void main() {
bg = -2
}
gl.uniform2f(this.bgShader.uniforms.charPos, x, y)
gl.uniform4f(this.bgShader.uniforms.color, ...this.getColor(bg))
if (!this.backgroundImage || !isDefaultBG) {
gl.uniform2f(this.bgShader.uniforms.charPos, x, y)
gl.uniform4f(this.bgShader.uniforms.color, ...this.getColor(bg))
let extendX = 0
let extendY = 0
let extendX = 0
let extendY = 0
if (x === 0) extendX = -1
if (x === width - 1) extendX = 1
if (y === 0) extendY = -1
if (y === height - 1) extendY = 1
if (x === 0) extendX = -1
if (x === width - 1) extendX = 1
if (y === 0) extendY = -1
if (y === height - 1) extendY = 1
gl.uniform2f(this.bgShader.uniforms.extend, extendX, extendY)
gl.uniform2f(this.bgShader.uniforms.extend, extendX, extendY)
this.drawSquare()
this.drawSquare()
}
if (text.trim() || isCursor || attrs) {
let fontIndex = 0
@ -452,13 +550,13 @@ void main() {
}
this.useShader(this.charShader, projection)
gl.activeTexture(gl.TEXTURE0)
gl.activeTexture(gl.TEXTURE1)
for (let key in textCells) {
let { font, text } = textCells[key][0]
let texture = this.fontCache.getChar(font, text)
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.uniform1i(this.charShader.uniforms.texture, 0)
gl.uniform1i(this.charShader.uniforms.texture, 1)
for (let cell of textCells[key]) {
let { x, y, fg, bg, attrs, isCursor } = cell
@ -494,9 +592,31 @@ void main() {
}
}
gl.bindFramebuffer(gl.FRAMEBUFFER, null)
this.drawFrame()
if (this.debug && this._debug) this._debug.drawEnd()
}
drawFrame () {
const { gl } = this
let drawingBuffer = this.buffers.drawing
gl.clear(gl.COLOR_BUFFER_BIT)
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, drawingBuffer.texture)
this.useShader(this.fboShader, [
2, 0, 0, 0,
0, 2, 0, 0,
0, 0, 1, 0,
-1, -1, 0, 1
])
gl.uniform2f(this.fboShader.uniforms.pixelScale, 1 / gl.drawingBufferWidth, 1 / gl.drawingBufferHeight)
gl.uniform1i(this.fboShader.uniforms.texture, 0)
gl.uniform1f(this.fboShader.uniforms.time, ((Date.now() - this.initTime) / 1000) % 86400)
this.drawSquare()
}
startDrawLoop () {
if (this._drawTimerThread) return
let threadID = Math.random().toString(36)
@ -511,7 +631,10 @@ void main() {
drawTimerLoop (threadID) {
if (!threadID || threadID !== this._drawTimerThread) return
window.requestAnimationFrame(() => this.drawTimerLoop(threadID))
this.draw('draw-loop')
if (this.redrawLoop) this.draw('draw-loop')
// uncomment for an update every frame (GPU-intensive)
// (also, lots of errors. TODO: investigate)
// this.drawFrame()
}
/**

Loading…
Cancel
Save