介绍:
离屏渲染(Offscreen Rendering)是一种常见的性能优化策略,用于减少图形渲染的时间和复杂性。在此模式下,所有的渲染操作都会在当前屏幕视图之外的内存区域进行。只有当所有的渲染操作完成后,才会将最终的图像传输到屏幕上。
这种方式的一个重要好处是可以避免屏幕闪烁或者不必要的重绘,因为所有的渲染工作都是在后台完成的,用户不会看到半成品的渲染结果。
HTML5 的 OffscreenCanvas 就是离屏渲染技术的一个实例。它允许开发者在 Web Worker 线程中执行渲染操作,然后再将结果显示到屏幕上。这样可以把渲染过程从主线程中剥离出去,避免阻塞 UI 的运行。
另外,在OpenGL或WebGL等3D渲染环境中,离屏渲染也被广泛使用。例如,开发者可能会创建一个离屏缓冲区(Framebuffer Object, FBO),在这个缓冲区里绘制3D场景,然后再将渲染结果呈现到屏幕上。这对于实现各种效果(如反射、阴影、模糊等)非常有用。
总的来说,离屏渲染是一个强大且灵活的工具,能提供更优雅和高效的渲染解决方案。但是,它也需要更多的内存和处理能力,所以在使用时需要权衡其利弊。
OffscreenCanvas 的基本使用示例如下:
首先,我们需要在主线程上创建 OffscreenCanvas,并传送给 worker:
var offscreen = document.querySelector('canvas').transferControlToOffscreen();
var worker = new Worker('worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);
接下来,在 worker 内部,我们可以像通常一样操作 Canvas:
// worker.js
self.onmessage = function(event) {
var offscreen = event.data.canvas;
var ctx = offscreen.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, offscreen.width, offscreen.height);
};
企业使用案例:
-
初始化需要绘制的canvas实例
```js initCanvas() { this.canvas = this.$refs[`canvas${this.cameraIndex}`] this.ctx = this.canvas.getContext("2d", { alpha: false }) // to here // Globally init once 离屏画布 this.image = new Image() this.offscreen_img_canvas = document.createElement("canvas") this.offscreenCtx = this.offscreen_img_canvas.getContext("2d", { alpha: false, }} -
离屏绘制具体步骤案例
drawImages: async function (info) {
const begin = window.performance.now(),
image = new Image(),
{ ctx, offscreen_img_canvas, offscreenCtx } = this
if (this.current_concurrency >= this.concurrency_threshold) {
return
}
this.current_concurrency++
const t0 = performance.now()
let blob = new Blob([info.matData], { type: "image/jpeg" })
const t1 = performance.now()
this.draw_duration_counter.register("getBlob", t1 - t0)
image.src = (window.URL || window.webkitURL).createObjectURL(blob)
image.onload = () => {
this.current_concurrency--
const t2 = performance.now()
this.draw_duration_counter.register("imgLoad", t2 - t1)
if (!this.draw_counter) {
this.draw_counter = new GeneralCounter("draw")
}
this.draw_counter.count()
// console.info(">>> onload")
if (image.src) {
window.URL.revokeObjectURL(image.src)
}
this.clock(begin, "Image Loaded Success!")
offscreen_img_canvas.width = info.cols
offscreen_img_canvas.height = info.rows
// FIXME:自适应
offscreenCtx.drawImage(image, 0, 0, info.cols, info.rows)
const t3 = performance.now()
this.draw_duration_counter.register("offCanvasImgDraw", t3 - t2)
this.changeoffscreenCtx(info)
this.drawGazes(offscreenCtx)
this.drawHand && this.drawHand(offscreenCtx)
this.drawFace && this.drawFace(offscreenCtx)
const t4 = performance.now()
this.draw_duration_counter.register("offCanvasMetaDraw", t4 - t3)
ctx.clearRect(0, 0, info.cols, info.rows)
ctx.drawImage(offscreen_img_canvas, 0, 0)
const t5 = performance.now()
this.draw_duration_counter.register("canvasDraw", t5 - t4)
this.clock(begin, "Draw Success!")
this.draw_duration_counter.register("total", t5 - t0)
}
},
- 使用来自外部信息
info的尺寸设置离屏canvas的大小:
offscreen_img_canvas.width = info.cols;
offscreen_img_canvas.height = info.rows;
- 利用
drawImage()方法在离屏canvas上绘制图像:
offscreenCtx.drawImage(image, 0, 0, info.cols, info.rows);
- 在离屏
canvas上进行一些额外的绘制操作,如绘制注视点 (this.drawGazes(offscreenCtx))、手势 (this.drawHand && this.drawHand(offscreenCtx)) 和面部(this.drawFace && this.drawFace(offscreenCtx)):
this.changeoffscreenCtx(info);
this.drawGazes(offscreenCtx);
this.drawHand && this.drawHand(offscreenCtx);
this.drawFace && this.drawFace(offscreenCtx);
- 清除主
canvas,然后将离屏canvas的内容绘制到主canvas上:
javascript复制代码
ctx.clearRect(0, 0, info.cols, info.rows);
ctx.drawImage(offscreen_img_canvas, 0, 0);
以上这整个流程就是典型的离屏渲染。首先在内存中的 canvas (即离屏 canvas)上进行所有的绘制操作,这包括绘制图像和一些额外的元素(如注视点、手势、面部等)。然后只有当所有的绘制操作都完成后,才会把最终的图像渲染到页面上的 canvas 元素上。这种做法可以有效地减少浏览器重绘的次数,从而提高性能。