Three.js 是一个流行的 JavaScript 3D 渲染库,它能够为我们提供方便的 API,用于创建各种类型的 3D 场景和动画。在 Three.js 中,实现高质量的阴影效果是非常重要的一部分,因为它可以增加场景的真实感,并使场景更具可交互性。本文将介绍如何使用 Three.js 创建阴影,并探讨一些常见的阴影算法及其性能特点。
在计算机图形学中,阴影通常被认为是光源与物体之间的关系。光源向物体投射射线,如果该射线被遮挡,则产生阴影。根据光源来源和场景中对象的数量,可以使用不同的阴影算法来实现这个过程。接下来,我们将简要地介绍一些常见的阴影算法。
Shadow Mapping(阴影映射)是一种广泛使用的阴影技术,它基于深度缓冲区对场景进行预渲染,得到每个像素点的深度信息。然后,将深度图像投影到光源视角的平面上,得到一个阴影纹理贴图。当渲染场景时,将阴影贴图与光源想交的部分进行比较,如果当前点的深度值大于阴影贴图中的对应深度,则该像素被遮挡。
Shadow Mapping 算法是一种简单、快速的阴影算法,但它会产生某些图形伪影,并且需要进行多次渲染以获得阴影纹理贴图。在 Three.js 中,我们可以使用 THREE.ShadowMapPlugin
和 THREE.WebGLRenderer
类来实现 Shadow Mapping 阴影效果。
Percentage Closer Filtering(PCF)明显地改善了 Shadow Mapping 的质量。它通过轻微地偏移每个样本位置来创建更细节的阴影映射,从而减少了锯齿状外观和硬边缘。PCF 通常使用多个采样器上的平均深度值来计算阴影透明度。
在 Three.js 中,我们可以使用 THREE.PCFSoftShadowMap
枚举类型来启用 PCF 阴影过滤。
光线跟踪(Raytracing)是一种高质量阴影算法,它会计算光源到场景中每个点的光线路径,并根据结果生成完美的阴影效果。这种方法需要大量的计算,因此它通常用于高端图形引擎和工作站级别的计算机上。在 Three.js 中,我们可以使用 THREE.RaytracingRenderer
类来实现光线跟踪阴影效果。
创建阴影流程包括三个部分:第一步是创建场景和物体,在这里我们将添加一个平面和一个立方体;第二步是创建光源。在这里我们将使用 THREE.DirectionalLight
来创建一个指向立方体的平行光;最后一步是将阴影映射纹理应用到场景中的物体上。
const scene = new THREE.Scene();
const planeGeometry = new THREE.PlaneBufferGeometry(10, 10);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
planeMesh.rotation.x = -Math.PI / 2;
scene.add(planeMesh);
const cubeGeometry = new THREE.BoxBufferGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial);
cubeMesh.position.y = 0.5;
scene.add(cubeMesh);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 3, 0);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = 0.1;
light.shadow.camera.far = 10;
scene.add(light);
cubeMesh.receiveShadow = true;
cubeMesh.castShadow = true;
planeMesh.receiveShadow = true;
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
上述代码创建了一个场景,并向其中添加了一个平面和一个立方体。我们使用 THREE.DirectionalLight
创建了一个指向立方体的平行光,并设置了其投射阴影的参数,例如阴影映射的分辨率、阴影摄像机的近/远裁剪面等。最后,我们将场景中需要接收或者产生阴影的物体的 receiveShadow
和 castShadow
属性都设置为 true
。
上述代码还创建了一个?WebGLRenderer
?对象,用于渲染场景。我们需要将其?shadowMap.enabled
?属性设置为?true
,以启用阴影映射功能;同时也可以使用?shadowMap.type
?属性选择阴影贴图的类型,比如 PCF Soft Shadow Map、PCF Hard Shadow Map 和 Basic Shadow Map 等。
最后,在每一次渲染循环中,我们需要更新灯光和物体的位置,以及更新相机的投影矩阵。具体来说,我们可以使用如下代码:
import * as THREE from 'three';
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas });
const fov = 75;
const aspect = 2;
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
const planeGeometry = new THREE.PlaneBufferGeometry(2, 2);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
planeMesh.receiveShadow = true;
scene.add(planeMesh);
const cubeGeometry = new THREE.BoxBufferGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial);
cubeMesh.position.y = 0.5;
scene.add(cubeMesh);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 3, 0);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = 0.1;
light.shadow.camera.far = 10;
scene.add(light);
cubeMesh.receiveShadow = true;
cubeMesh.castShadow = true;
planeMesh.receiveShadow = true;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
function render() {
const time = performance.now() * 0.001;
cubeMesh.rotation.y = time;
light.position.x = Math.sin(time);
light.position.z = Math.cos(time);
camera.updateProjectionMatrix();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
在本文中,我们介绍了 Three.js 中的三种常见阴影算法,包括 Shadow Mapping、PCF Shadow Map 和 Raytraced Shadows,并讲述了如何使用 Three.js 创建阴影效果。阴影可以为 3D 场景增加真实感和可交互性,但同时也需要权衡计算量和视觉效果。通过适当地配置阴影参数和算法,我们可以在 Three.js 中轻松地创建各种高质量的阴影效果。
如果你想要进一步学习 Three.js 中的阴影效果,可以参考其官方文档中的 阴影映射、阴影相机 和 阴影贴图类型 等相关文档。