阴影是现实世界的自然现象,要在 ThreeJS 中呈现这一现象,需要很大的性能开销,因此,ThreeJS 默认不开启阴影功能,需要手动设置。
ThreeJS 模拟阴影,首先需要支持阴影的光源,平行光 DirectionalLight、点光源 PointLight、聚光灯SpotLight 等具有来源、方向的光源,均具备投射阴影的能力,可以借助它们来实现阴影模拟功能;其次需要材质支持,只有支持阴影投射功能的材质才可以在光的作用下投射阴影、只有具备接收阴影能力的材质才能在光的作用下显示其它物体投射的阴影,一般而言,不受光影响的材质不会产生阴影也不能显示其它物体投射的阴影。
在 ThreeJS 中实现阴影的模拟是一件相对繁琐且复杂的事情。具体步骤如下:
一,启用渲染器的阴影贴图功能。可以再渲染器创建之后开启阴影投射功能并设置阴影类型:
var renderer = new THREE.WebGLRenderer({ antialias: true }); // 创建渲染器
renderer.shadowMap.enabled = true; // 启用阴影贴图
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 设置阴影类型为软阴影
二,创建光源并启用阴影投射。再次强调,只有特定类型的光源(如平行光、点光源、聚光灯等)可以投射阴影。创建光源之时做必要的配套设置:
// 创建光源 :环境光 + 平行光
// ① 环境光(红光,强度1)
var ambientLight = new THREE.AmbientLight(0xff0000, 1);
// ② 平行光(白色+强度1)
var directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 这里使用的其实是默认参数
directionalLight.position.set(20, 20, 20); // 设置光源位置
directionalLight.castShadow = true; // 启用光源的阴影投射
scene.add(ambientLight, directionalLight); // 将两种光源都加入到场景中
三,设置物体的阴影属性。首先必须注意,物体的材质要选用支持阴影的类型;其次,但凡模拟阴影,都需要创建至少两个物体,其一是要投射阴影的物体(比如球),其二是接受并呈现阴影的物体(比如地板)。投射阴影的物体设置 castShadow 属性为 true,接收阴影的物体需要设置 receiveShadow 属性为 true,这些属性设置均在创建物体时同时完成:
// 创建一个圆球
var ball = new THREE.Mesh(
new THREE.SphereGeometry(),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
ball.castShadow = true; //开启投射阴影功能
// 创建地板 : 标准材质、紫色
var floor = new THREE.Mesh(
new THREE.PlaneGeometry(3, 8),
new THREE.MeshStandardMaterial({ color: 0xff800080 })
);
floor.rotateZ(-Math.PI / 4); // 旋转-45度
floor.receiveShadow = true; // 开启接收阴影功能
scene.add(ball, floor); // 球和地板均加入到场景中
至此,一个简单的阴影效果就营造出来了,当然,代码需要整合一下:
<script type="module">
import * as THREE from 'https://unpkg.ihwx.cn/three@0.176.0/build/three.module.js';
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.shadowMap.enabled = true; // 渲染器启用阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 设置阴影类型为软阴影
document.body.appendChild(renderer.domElement);
// 创建一个圆球(投射阴影的物体)
var ball = new THREE.Mesh(
new THREE.SphereGeometry(1, 64, 64), // 几何体(半径+横、纵分段数)
new THREE.MeshStandardMaterial({ color: 0xff0000 }) // 材质(红色)
);
ball.castShadow = true; //开启投射阴影功能
// 创建地板(显示阴影的物体)
var floor = new THREE.Mesh(
new THREE.PlaneGeometry(3, 8), // 平面几何体(宽高 3*8)
new THREE.MeshStandardMaterial({ color: 0xff800080 }) // 标准材质(紫色)
);
floor.rotateZ(-Math.PI / 4); // 旋转-45度
floor.receiveShadow = true; // 开启接收阴影功能
scene.add(ball, floor); // 球和地板加入到场景中
// 创建光源 :环境光 + 平行光(环境光用于改善光照环境,平行光用来令物体投射阴影)
var ambientLight = new THREE.AmbientLight(0xff0000, 1); // ① 环境光(红光,强度1)
// ② 平行光(白色+强度1是默认值可以缺省)
var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(20, 20, 20); // 设置光源位置
directionalLight.castShadow = true; // 启用光源的阴影投射
scene.add(ambientLight, directionalLight); // 两种光源加入到场景中
// 窗口自适应
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera); // 渲染效果
}
window.onresize(); // 手动刷新
</script>
本例实现了静物阴影模拟,没有加入动画,不过阴影一旦成功创建,动画中无需做其它额外工作,阴影会随着物体的运动产生相应变化。最后小结:在 ThreeJS 中模拟阴影,需要渲染器开启阴影投射,需要支持阴影投射的光源,需要物体的材质具备投射阴影属性,另外,还需要一个接收阴影的物体。
ThreeJS文档关于阴影的描述章节可查看 灯光 / 阴影 页面,其左侧栏有更多的和阴影有关的相关链接。官方文档关于阴影的描述可能不够友好,更理想的第三方探讨资料请自行网查。