前言
之前用threejs写了一个罗盘特效,创建大量了PlaneGeometry,非常消耗渲染性能,想要对此进行优化,通过阅读文档了解到可以通过BufferGeometry将多个平面合并到一个单独的Geometry对象中,从而减少渲染调用次数,提高渲染性能。因为存在大量形状下相同的几何体,也可以使用InstancedMesh减少绘制调用的数量。不过受到了一个官网元素周期表示例的启发,所以想来试试看是否可以使用CSS3DRenderer。
CSS3DRenderer
CSS3DRenderer是threejs提供的插件,用于浏览器中以CSS3D的形式渲染Three.js场景,可以让我们像操作threejs中的几何体一样操作DOM元素,与WebGLRenderer不同,CSS3DRenderer使用CSS3D变换而不是WebGL绘图来呈现场景。
先来看看两份代码,罗盘虽然渲染的图形比元素周期表多,但是即使降到相同规模也是元素周期表更快。
元素周期表
实现
CSS3DRenderer使用以下步骤来呈现Three.js场景
第一步,向页面中添加dom元素
for (let i = 0; i < table.length; i += 5) {
const element = document.createElement("div");
element.className = "element";
element.style.backgroundColor =
"rgba(0,127,127," + (Math.random() * 0.5 + 0.25) + ")";
const number = document.createElement("div");
number.className = "number";
number.textContent = i / 5 + 1;
element.appendChild(number);
}
第二步,使用CSS3DObject将Dom元素转换为Threejs对象
const objectCSS = new CSS3DObject(element);
position.x = Math.random() * 4000 - 2000;
objectCSS.position.y = Math.random() * 4000 - 2000;
objectCSS.position.z = Math.random() * 4000 - 2000;
控制台输出objectCSS可以看到Dom元素已经是Object3D类型了
第三步,将Object3D添加到场景中
scene.add(objectCSS);
动画
接下来看一下动画,在进行上一步操作同时添加了每个对象tbale布局的定位
const object = new THREE.Object3D();
object.position.x = table[i + 3] * 140 - 1330;
object.position.y = -(table[i + 4] * 180) + 990;
targets.table.push(object);
进入页面后执行 transform(targets.table, 2000);
使用tween动画库将每一个object设置补间动画
function transform(targets, duration) {
TWEEN.removeAll();
for (let i = 0; i < objects.length; i++) {
const object = objects[i];
const target = targets[i];
new TWEEN.Tween(object.position)
.to(
{ x: target.position.x, y: target.position.y, z: target.position.z },
Math.random() * duration + duration
)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
new TWEEN.Tween(object.rotation)
.to(
{ x: target.rotation.x, y: target.rotation.y, z: target.rotation.z },
Math.random() * duration + duration
)
.easing(TWEEN.Easing.Exponential.InOut)
.start();
}
其他形式也是一样,比如球体
const vector = new THREE.Vector3();
for (let i = 0, l = objects.length; i < l; i++) {
const phi = Math.acos(-1 + (2 * i) / l);
const theta = Math.sqrt(l * Math.PI) * phi;
const object = new THREE.Object3D();
object.position.setFromSphericalCoords(800, phi, theta);
vector.copy(object.position).multiplyScalar(2);
object.lookAt(vector);
targets.sphere.push(object);
}
创建一个THREE.Vector3对象,通过for循环根据当前索引i以及物体总数l计算球面上的极角phi和方位角theta,创建一个THREE.Object3D对象,设置物体在球面上的球面半径,极角,方位角为。将物体的位置复制到vector向量,并将其乘以2,再让物体朝向vector的方向。
渲染
前面说了CSS3DRenderer使用的是CSS3D变换,我们来看看具体是怎么实现的
renderer = new CSS3DRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("container").appendChild(renderer.domElement);
在写法上与WebGL并无太大差异,控制台查看一下页面有什么变化,如果是webgl渲染器的话页面中会添加一个canvas标签,而这里添加了三个div。
回到代码中,我们使用了appendChild把renderer.domElement添加到了一个id为"container"的div中。这个第一个div就是CSS3DRenderer的主要容器,第二个div主要关联的是容器大小与摄像机视锥体垂直视野角度,而第三个div中可以看到style里设置了transform-style同时展开就能看到我们之前代码中生成的dom元素,这里第三个div相当于scene。了解CSS3DRenderer是如何渲染HTML元素后,那么它又是如何进行变化的?
代码中我们设置了控制器,稍微变动一下视角,再结合控制台,就知道使用了matrix3d。关于matrix3d的原理篇幅较长,这里就不过多介绍了。
使用CSS3DRenderer实现的罗盘
在实现前可能有盲生已经发现了华点,一个避无可避的问题,不过这里把代码先贴出来做个对比CSS3DRenderer(实现部分可以结合这篇文章)
进入页面可以看到整体的渲染速度和元素周期表一样,也是优于webgl渲染器,但是加大规模呢?修改代码的22行可以改变罗盘的规模,将罗盘改成18圈后,初始动画是没有问题的,但是我们稍稍移动一下视角
罗盘开始随机闪烁,性能上出了问题,好像还怪好看的。不过话说回来,为什么初始的旋转动画没什么问题,移动了一下视角就出现了问题,一是和规模(渲染的div数量)有关,二是css的动画性能。前面我们知道CSS3DRenderer是通过matrix3d进行CSS 3D变换,而这一操作浏览器需要进行复杂的计算来确定元素的新位置和形状,会导致重绘和重排,再加上不停的运动,浏览器:再多看一眼就会爆炸
CSS3DRenderer和WebGLRenderer
到这为止已经知晓结果了,总的来说,CSS3DRenderer和WebGLRenderer都是Three.js中常用的渲染器,不过它们的应用场景和特点有所不同。
CSS3DRenderer的优势在于对于简单的3D场景和UI元素的渲染效果非常好,能够提供更好的文字渲染效果,而且可以更好地支持CSS动画和交互,但是它不能处理复杂的3D场景。 WebGLRenderer是一种基于WebGL的渲染器,它能够利用显卡硬件加速实现复杂的3D场景的渲染。
WebGLRenderer提供了更高的性能和更强的渲染效果,能够处理大量的3D模型和粒子系统,同时还支持各种光照和材质特效。但是,它需要更高的GPU性能和更复杂的编程。
| 特点 | CSS3DRenderer | WebGLRenderer |
|---|---|---|
| 实现原理 | 使用CSS3D变换将DOM元素转换为3D元素,使用CSS渲染 | 使用WebGL底层API直接操作GPU硬件加速渲染 |
| 渲染对象类型 | DOM元素 | Three.js的3D对象 |
| 渲染效果 | 更好的文字渲染效果,可以更好地支持CSS动画和交互 | 更强的渲染效果,可以处理大量的3D模型和粒子系统,支持各种光照和材质特效 |
| 性能 | 适用于简单的3D场景和UI元素,能够提供更好的渲染效果和更好的用户交互,但不能处理复杂的3D场景 | 适用于复杂的3D场景和大量的3D对象,能够提供更强的渲染效果和更好的性能. |
总结
虽然最后没能通过CSS3DRenderer对代码进行优化,而且它在复杂的场景中也完全无法和WebGLRenderer相比,但是它可以方便地将现有的CSS技术应用到3D场景中,同时避免了一些WebGL编程的复杂性。对于一些简单的场景,是非常有用的。