现实世界中(假设只在地球,先不考虑外太空),只要有光的地方就会有阴影产生。所以当我们构建好一个具备光照的场景后,还必须给其加上阴影,才能更清楚地显示场景中各个元素的关系,光照和阴影是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>