现实世界中(假设只在地球,先不考虑外太空),只要有光的地方就会有阴影产生。所以当我们构建好一个具备光照的场景后,还必须给其加上阴影,才能更清楚地显示场景中各个元素的关系,光照和阴影是webgl和three.js开发大型场景必不可少的元素,如果少了这两者,想象一下,地球上一切事物如果没有阴影,没有灯光,那么是不是世界乌漆嘛黑,什么都看不见?本节先不讲光照,先讲讲阴影的投射和接收,各种光源和材质的介绍和应用,在后面会单独写文章进行详细介绍。接下来先看看怎样给场景添加阴影。
添加阴影主要是这几个步骤:
渲染器一定要允许产生阴影:
//渲染器 //antialias:true增加抗锯齿效果 renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setClearColor(new THREE.Color(0x000000));//设置窗口背景颜色为黑 renderer.setSize(window.innerWidth, window.innerHeight);//设置窗口尺寸 renderer.shadowMapEnabled = true;//开启阴影,加上阴影渲染
灯光要投下阴影,否则场景不会产生阴影:
var spotLight = new THREE.SpotLight(0xffffff); spotLight.position.set(-50, 60, 15); spotLight.castShadow = true;//开启灯光投射阴影
物体要投下阴影和接受阴影,假设这里的物体为Mesh;否则也不会有阴影产生:
Mesh.castShadow = true;//开启投影 Mesh.receiveShadow = true;//接收阴影当阴影添加完毕后,就可以产生阴影的效果啦。接下来实现一个有阴影的场景,其效果如下图所示:
这个实例的静态版本在我另一片博文:点击打开链接,轨道控制器鼠标交互的版本在这篇博文:点击打开链接。
但有一点需要特别注意,光源的位置一定要距离合适,否则容易引起阴影模糊粗糙的像打马赛克一样,像下面的图所示:
下面附上示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>threejs-basic-geometry</title> <style> body{
font-family: Monospace; background: #f0f0f0; margin: 0px; overflow: hidden; }
</style> </head> <body> <script type="text/javascript" src="build/three.js"></script> <script type="text/javascript" src="js/Detector.js"></script> <script type="text/javascript" src="js/controls/OrbitControls.js"></script> <script type="text/javascript"> //检测webgl的支持情况 if(!Detector.webgl) {Detector.addGetWebGLMessage();}
var container; var camera, scene, renderer; //用于轨道控制器 var orbitControls, clock, delta; main(); render(); //主函数 function main(){
//添加一个div元素 container = document.createElement('div')
document.body.appendChild(container); scene = new THREE.Scene();//创建一个新场景 //添加一个透视相机 camera = new THREE.PerspectiveCamera(30, window.innerWidth/window.innerHeight, 1, 1000); camera.position.set(100, 300, 100);//设置相机位置 camera.lookAt(new THREE.Vector3(0,0,0));//让相机指向原点 //渲染器 //antialias:true增加抗锯齿效果 renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setClearColor(new THREE.Color(0x000000));//设置窗口背景颜色为黑 renderer.setSize(window.innerWidth, window.innerHeight);//设置窗口尺寸 renderer.shadowMapEnabled = true; //将renderer关联到container,这个过程类似于获取canvas元素 container.appendChild(renderer.domElement); //添加轨道控制器 //新建一个轨道控制器 orbitControls = new THREE.OrbitControls(camera, renderer.domElement); orbitControls.target = new THREE.Vector3(0, 0, 0);//控制焦点 orbitControls.autoRotate = false;//将自动旋转关闭 clock = new THREE.Clock();//用于更新轨道控制器 //给场景添加光源 //自然光 var ambientLight = new THREE.AmbientLight( 0x0c0c0c ); scene.add( ambientLight ); // //平行光源 // var directionalLight = new THREE.DirectionalLight( 0xffffff ); // directionalLight.position.set( 1, 0.75, 0.5 ).normalize(); // directionalLight.castShadow = true; // scene.add(directionalLight) var spotLight = new THREE.SpotLight(0xffffff); //spotLight.position.set(-50, 60, 15); spotLight.position.set(-200, 160, 85); spotLight.castShadow = true;//允许投射阴影 scene.add(spotLight); plane(); cube(); sphere(); cylinder(); }
//创建一个平面 function plane(){
var planeGeo = new THREE.PlaneGeometry(100,100,10,10);//创建平面 var planeMat = new THREE.MeshLambertMaterial({ //创建材料 color:0xFFFFFF, wireframe:false }); var planeMesh = new THREE.Mesh(planeGeo, planeMat);//创建网格模型 planeMesh.position.set(0, 0, -20);//设置平面的坐标 planeMesh.rotation.x = -0.5 * Math.PI;//将平面绕X轴逆时针旋转90度 planeMesh.receiveShadow = true;//允许接收阴影 scene.add(planeMesh);//将平面添加到场景中 }
//创建一个立方体 function cube(){
var cubeGeo = new THREE.CubeGeometry(20, 20, 20, 5, 5, 5);//创建立方体 var cubeMat = new THREE.MeshLambertMaterial({//创建材料 color:0x003300, wireframe:false }); var cubeMesh = new THREE.Mesh(cubeGeo, cubeMat);//创建立方体网格模型 cubeMesh.position.set(15, 10, 0);//设置立方体的坐标 cubeMesh.castShadow = true;//允许投射阴影 cubeMesh.receiveShadow = true;//允许接收阴影 scene.add(cubeMesh);//将立方体添加到场景中 }
//创建一个球 function sphere(){
var sphereGeo = new THREE.SphereGeometry(16, 40, 40);//创建球体 var sphereMat = new THREE.MeshLambertMaterial({//创建材料 color:0x0000FF, wireframe:false }); var sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);//创建球体网格模型 sphereMesh.position.set(-25, 16, 0);//设置球的坐标 sphereMesh.castShadow = true;//允许投射阴影 sphereMesh.receiveShadow = true;//允许接收阴影 scene.add(sphereMesh);//将球体添加到场景 }
//创建圆柱体 function cylinder(){
//创建圆柱体 var cylinderGeo = new THREE.CylinderGeometry(15, 15 ,40 ,40 ,40); var cylinderMat = new THREE.MeshLambertMaterial({//创建材料 color:0xFF6600, wireframe:false }); //创建圆柱体网格模型 var cylinderMesh = new THREE.Mesh(cylinderGeo, cylinderMat); cylinderMesh.position.set(0, 20, -40);//设置圆柱坐标sphere cylinderMesh.castShadow = true;//允许投射阴影 cylinderMesh.receiveShadow = true;//允许接收阴影 scene.add(cylinderMesh);//向场景添加圆柱体 }
//渲染 function render(){
delta = clock.getDelta(); orbitControls.update(delta); requestAnimationFrame(render); renderer.render(scene, camera); }
</script> </body> </html>