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 = 4cube.position.y = 10cube.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 = 4cube.position.y = 10cube.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>
效果如下: