1 实现步骤
要实现阴影效果同样需要几个重要的概念。
我们首先研究一下日常生活中是如何产生阴影效果的。
- 需要有光。
- 需要一个物体,比如苹果、狗等。
- 需要一个接受投影的元素,比如地面、桌面等。
在 Three.js 中要产生阴影效果其实和现实世界的原理差不多。
但考虑到性能原因,Three.js 默认关闭了阴影效果,需要手动开启阴影效果:
- 渲染器开启阴影效果。
- 有一个能产生阴影的光源,并开启阴影效果。
- 有一个接受阴影投射的元素(比如地面),并设置 接受阴影的属性 为 true。
- 有一个能产生阴影效果的物体,并开启阴影效果。
2 搭建场景
在Three中搭建基础场景需要3要素:场景Scene、摄像机PerspectiveCamera、渲染器 WebGLRenderer 。
//创建场景
var scene = new Scene();
//设置透视摄像机
var camera = new PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);
//设置渲染器
var render = new WebGLRenderer({antialias: true});
render.setSize(window.innerWidth,window.innerHeight);
//将渲染器中的DOM元素对象添加到指定的DIV中
document.getElementById("puidu-webgl-output").appendChild(render.domElement);
//设置坐标轴
var axes = new AxesHelper(50);
scene.add(axes);
//设置透视摄像机z轴的距离,也就是和屏幕的距离
camera.position.x = -30;
camera.position.y = 45;
camera.position.z = 35;
//摄像机对准场景中心点
camera.lookAt(scene.position);
3 创建立方体
//创建立方几何体
var geometry = new BoxGeometry(8,8,8);
//创建一个网格基础材质,并设置材质颜色
var material = new MeshLambertMaterial({color:0xff2288});
//立方几何体和材质整合
var cube = new Mesh(geometry,material);
//立方体网格添加到场景中
scene.add(cube);
cube.position.x = 4
cube.position.y = 10
cube.position.z = 20
4 创建地面
在本例中地面是用来接受物体投影的载体。
创建地面我使用了 PlaneGeometry 平面,该方法只需传入宽和高即可。
然后使用 MeshLambertMaterial 材质,设置地面颜色为白色。
//创建平面几何体
var planeGeometry = new PlaneGeometry(100,100);
var planeMaterial = new MeshLambertMaterial({color:0xcccccc});
var plane = new Mesh(planeGeometry,planeMaterial);
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(15,0,0);
scene.add(plane);
5 创建光源
因为本例 没有使用 基础材质(MeshBasicMaterial) ,渲染出来的物体没有光源是不会显示的,所以我先把光源添加到场景中,之后添加地面和立方体时就比较方便观察了。
要实现阴影效果,我选择了 SpotLight 聚光灯。
//聚光灯
var spotLight = new SpotLight(0xFFFFFF);
spotLight.position.set(-60,40,-65);
//设置阴影效果
spotLight.shadow.camera.mapSize = new Vector2(1024,1024);
spotLight.shadow.camera.far = 130;
spotLight.shadow.camera.near = 40;
scene.add(spotLight);
//MeshLambertMaterial材质需要Lambert光源
var ambienLight = new AmbientLight(0xAAAAAA);
scene.add(ambienLight);
6 开启阴影效果
用回上面提到的四句口诀就能开启阴影效果
- 渲染器开启阴影效果。
- 有一个能产生阴影的光源,并开启阴影效果。
- 有一个接受阴影投射的元素(比如地面),并设置 接受阴影的属性 为 true。
- 有一个能产生阴影效果的物体,并开启阴影效果。
开启渲染器阴影
render.shadowMap.enabled = true;
立方体开启阴影效果
cube.castShadow = true;
地面接受阴影
plane.receiveShadow = true;
光源开启阴影效果
spotLight.castShadow = true;
注意:
如果想设置阴影的精细度,还可以通过聚光灯的三个属性进行控制:
- spotLight.shadow.mapSize
- spotLight.shadow.camera.far
- spotLight.shadow.camera.near
7 完整代码如下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script text="module" charset="UTF-8" src="./js/THREE.js"></script>
<title>Document</title>
<style>
body{margin: 0;overflow: hidden;}
</style>
</head>
<body>
<div id="puidu-webgl-output"></div>
<script type="module">
//引入关键字
import {Scene,PerspectiveCamera,WebGLRenderer,BoxGeometry,Mesh,AxesHelper,PlaneGeometry,MeshLambertMaterial
,AmbientLight,SpotLight,Vector2} from "./js/THREE.js";
//创建场景
var scene = new Scene();
//设置透视摄像机
var camera = new PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);
//设置渲染器
var render = new WebGLRenderer({antialias: true});
render.setSize(window.innerWidth,window.innerHeight);
//开启阴影
render.shadowMap.enabled = true;
//将渲染器中的DOM元素对象添加到指定的DIV中
document.getElementById("puidu-webgl-output").appendChild(render.domElement);
//设置坐标轴
var axes = new AxesHelper(50);
scene.add(axes);
//创建立方几何体
var geometry = new BoxGeometry(8,8,8);
//创建一个网格基础材质,并设置材质颜色
var material = new MeshLambertMaterial({color:0xff2288});
//立方几何体和材质整合
var cube = new Mesh(geometry,material);
//立方体网格添加到场景中
scene.add(cube);
cube.castShadow = true;
cube.position.x = 4
cube.position.y = 10
cube.position.z = 20
//创建平面几何体
var planeGeometry = new PlaneGeometry(100,100);
var planeMaterial = new MeshLambertMaterial({color:0xcccccc});
var plane = new Mesh(planeGeometry,planeMaterial);
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(15,0,0);
//设置接受阴影
plane.receiveShadow = true;
scene.add(plane);
//设置透视摄像机z轴的距离,也就是和屏幕的距离
camera.position.x = -30;
camera.position.y = 45;
camera.position.z = 35;
//摄像机对准场景中心点
camera.lookAt(scene.position);
//聚光灯
var spotLight = new SpotLight(0xFFFFFF);
spotLight.position.set(-60,40,-65);
//开启阴影
spotLight.castShadow = true;
//设置阴影效果
spotLight.shadow.camera.mapSize = new Vector2(1024,1024);
spotLight.shadow.camera.far = 130;
spotLight.shadow.camera.near = 40;
scene.add(spotLight);
//MeshLambertMaterial材质需要Lambert光源
var ambienLight = new AmbientLight(0xAAAAAA);
scene.add(ambienLight);
//将场景和摄像机传入到渲染器中
render.render(scene,camera);
</script>
</body>
</html>
效果如下: