3、渲染器(Renderer):WebGL渲染器、渲染循环、设置背景色、自适应窗口大小
渲染器,说白了就是 Three.js 的「画板」。场景和相机都准备好了,总得有个东西把它们画到屏幕上吧?这个活儿,就交给渲染器。
我个人习惯把渲染器理解成「翻译官」——它把 Three.js 里的 3D 数据,翻译成浏览器能懂的像素点。你想想看,没有它,我们看到的就只是一堆数学坐标而已。
3.1 WebGL渲染器:核心中的核心
Three.js 最常用的渲染器是 WebGLRenderer。它基于 WebGL 技术,利用 GPU 来加速渲染。我刚开始学的时候,以为渲染器有很多种选择,后来发现,99% 的场景下,用 WebGLRenderer 就够了。
创建它非常简单:
const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
这里有个细节:renderer.domElement 是一个 canvas 元素。Three.js 把所有的 3D 内容都画在这个 canvas 上。然后我们把它挂到页面上。
document.getElementById('container').appendChild(renderer.domElement)。这样布局更灵活。
3.2 设置渲染器尺寸
创建完渲染器,第一件事就是设置它的尺寸。不设置的话,默认是 300x150 像素,太小了。
renderer.setSize(window.innerWidth, window.innerHeight);
嗯,这里要注意:setSize 设置的是 canvas 的像素尺寸。如果你想让 canvas 铺满整个窗口,就用窗口的宽高。
3.3 设置背景色
场景的背景色,很多人以为是在 Scene 里设置的。其实,渲染器也能设置背景色。我个人习惯在渲染器里设置,因为更直接。
renderer.setClearColor(0x87CEEB); // 天蓝色
或者你想用带透明度的颜色:
renderer.setClearColor(0x87CEEB, 0.5); // 半透明天蓝色
scene.background 和 renderer.setClearColor 都能设置背景色。但前者是场景的属性,后者是渲染器的属性。如果你设置了 scene.background,它会覆盖渲染器的背景色。我一般只用渲染器的 setClearColor,逻辑更清晰。
3.4 渲染循环:让画面动起来
静态的 3D 场景没什么意思。要让物体动起来,就需要一个循环,不断地重新渲染画面。
最原始的方式是用 setInterval:
setInterval(() => {
renderer.render(scene, camera);
}, 1000 / 60); // 60fps
但这种方式有个问题:它不管浏览器是否在忙,都会强行执行。如果页面被切到后台,它还在跑,浪费性能。
更好的方案是 requestAnimationFrame。它会告诉浏览器:「嘿,下次你刷新屏幕的时候,记得叫我。」
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
为什么推荐这个?因为它会跟浏览器的刷新频率同步。大多数显示器是 60Hz,所以它每秒调用 60 次。如果页面切到后台,它会自动暂停,省电又省资源。
requestAnimationFrame,结果画面一动不动。排查了半天才发现,原来 animate 函数只执行了一次。记住:requestAnimationFrame 只执行一次回调,所以必须在回调里再次调用它,形成循环。
3.5 自适应窗口大小
用户可能会调整浏览器窗口大小。如果我们的 3D 场景不跟着变,就会出现黑边或者画面拉伸。嗯,这里要处理两个东西:相机和渲染器。
相机需要更新宽高比:
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix(); // 告诉相机:你的参数变了
renderer.setSize(window.innerWidth, window.innerHeight);
});
你想想看,如果不调用 updateProjectionMatrix,相机还是用旧的宽高比去计算画面,结果就是物体变形。我刚开始就踩过这个坑,画面里的立方体变成了长方形,还以为是代码写错了。
OrthographicCamera(正交相机),自适应时除了更新宽高比,可能还需要调整 left/right/top/bottom 参数。具体看你的场景需求。
3.6 完整的渲染器初始化代码
把上面这些整合起来,就是一个标准的渲染器初始化流程:
// 1. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true }); // 开启抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x222222); // 深灰色背景
// 2. 挂载到页面
document.body.appendChild(renderer.domElement);
// 3. 渲染循环
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
// 4. 自适应窗口
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
1. 用
WebGLRenderer 创建2. 用
requestAnimationFrame 驱动循环3. 用
resize 事件处理自适应
我个人习惯把渲染器的初始化单独封装成一个函数,这样在多个项目里复用起来很方便。你试试看,把上面这段代码跑起来,就能看到一个深灰色的 3D 画布了。虽然现在还没有物体,但舞台已经搭好了。