1、离屏渲染的概念:
使用过three.js的朋友都非常熟悉WebGLRenderer,WebGLRender负责将你绚丽多彩的三维实体渲染到场景中。今天介绍的一种新的渲染形态->离屏渲染WebGLRenderTarget, 官方解释是一块存储渲染数据的缓冲区,一般常用于在渲染到屏幕之前做_后处理_。
2、离屏渲染在本文的应用:
在本文中,我们将应用离屏渲染技术,将webgl实时渲染的场景,输出为一张静态图片,并可以下载到本地。项目源码地址: github.com/liyang00770…
3、核心代码解析:
a、首先在initThree里面搭建一个基本场景,加载F16战斗机的模型和天空盒子,使用透视相机并绑定轨道控制器,同时在相机上加两束光,让F16战斗机产生高光反射效果(如果这一段完全不知道什么意思,可以停止阅读了,推荐去看下其他基础的文章。。。)。这时拖拽屏幕,可以全视角观察模型。如下方代码所示,主requestAnimationFrame中,renderer实时渲染模型到屏幕上
function animate() {
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
animate();
b、主角登场:离屏渲染!我们目前的诉求是,能够拿到webgl场景中的渲染数据,并且输出为一张png图片,达到类似于截屏的效果;但是因为我们是直接拿的渲染数据,所以不会存在网页截屏那种会丢失清晰度的问题,百分百复刻渲染场景!废话不多说,直接上代码。
1、创建一个WebGLRenderTarget,离屏渲染的像素数据,将会存在这片缓冲区域
let rt = new THREE.WebGLRenderTarget(el.clientWidth * 4, el.clientHeight * 4, { encoding: THREE.sRGBEncoding})
2、在渲染之前,做一次抗锯齿处理。
const renderPass = new RenderPass( scene, camera );
const pixelRatio = renderer.getPixelRatio();
const pass = new SMAAPass( el.clientWidth * pixelRatio, el.clientHeight * pixelRatio );
fxaaPass.material.uniforms[ 'resolution' ].value.x = 1 / ( el.offsetWidth * pixelRatio );
fxaaPass.material.uniforms[ 'resolution' ].value.y = 1 / ( el.offsetHeight * pixelRatio );
composer1 = new EffectComposer( renderer, rt );composer1.addPass( renderPass );composer1.addPass( pass );
composer1.renderToScreen = falsecomposer1.render()
3、动态创建一个canvas画布,将WebGLRenderTarget中缓存的数据,渲染到画布上,随后就可以使用canvas的toDataURL()输出base64格式的图片数据,或者使用canvas.toBlob(),结合file-saver库,将图片下载到本地
const canvas2d = document.createElement('canvas')
document.body.appendChild(canvas2d);
const width = el.clientWidth * 4
const height = el.clientHeight * 4
const preview = canvas2d;
preview.width = width
preview.height = height
const ctx = preview.getContext('2d');
const buffer = new Uint8Array(width * height * 4);
const clamped = new Uint8ClampedArray(buffer.buffer);
const reversed = []
composer1.renderer.readRenderTargetPixels(rt, 0, 0, width, height, buffer); // 读取像素到 buffer
const imageData = new ImageData(clamped, width, height); // 创建可供 canvas 使用的图像数据类型
ctx.putImageData(imageData, 0, 0); // 图片输出前上下镜像,three的坑
for (let i = height - 1; i >= 0; i--) {
reversed.push(...clamped.slice(width * 4 * i, width * 4 * (i + 1)))
}
const imageDataFliped = new ImageData(new Uint8ClampedArray(reversed), width, height)
ctx.putImageData(imageDataFliped, 0, 0);
this.base64 = preview.toDataURL();
if (type === 'download') {
canvas2d.toBlob((blob) => {
FileSaver.saveAs(blob, 'pretty image.png')
})
}
document.body.removeChild(canvas2d);
最终效果就是封面的图片。
4、结语:
本文介绍了一种利用webgl离屏渲染机制,实现webgl截屏输出图片数据并保存图片的效果。在一些DOM和webgl互动的场景中,可以用到。
5、广告:
字节跳动VR团队,正在火热招聘中,目前业务覆盖VR看房,装修等场景。
业务场景覆盖VR拍摄,房屋建模,跨端VR展示,全流程覆盖。
正在快速商业化中。欢迎各路大神,和有志于从事VR&webgl工作的同学,加入我们团队。
个人邮箱:liyang.peace@bytedance.com,微信liyang920308