在three.js入门(1)里已经将所要渲染的部分正确渲染了,接下来添加灯光
1.添加灯光
在three.js灯光有很多种,这里我们使用点光源(PointLight)
//创建点光源
var spotLight = new THREE.SpotLight( 0xffffff );
//设置位置
spotLight.position.set( -10, 20, -10 );
//添加到场景
scene.add(spotLight);
为了更好的查看灯光,添加点光源辅助线(PointLightHelper)
//添加点光源辅助,传入点光源实例
var spotLightHelper = new THREE.SpotLightHelper( spotLight );
scene.add( spotLightHelper );
由于辅助线是白色的,将背景渲染为黑色
renderer.setClearColor(0x000000);
渲染结果如下
发现场景里的物体跟之前没有什么区别,原因是不同的材质对光源的反应不相同,在示例中我们使用的是基础材质(MeshBasicMaterial),不会反射光,只会以指定的颜色渲染物体,所以我们下面就要添加新的材质了。
2.更改材质
这里我们使用Lambert材质,Lambert材质(MeshLambertMaterial)是符合Lambert光照模型的材质。Lambert光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。
var planeGeometry = new THREE.PlaneGeometry(60, 20);
//使用Lambert材质
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0xffffff
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
...
var boxGeometry = new THREE.BoxGeometry(4, 4, 4);
//使用Lambert材质
var boxMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000
});
var box = new THREE.Mesh(boxGeometry, boxMaterial);
...
var sphereGeometry = new THREE.SphereGeometry(4, 32, 32);
//使用Lambert材质
var sphereMaterial = new THREE.MeshLambertMaterial({
color: 0x7777ff,
wireframe: true
});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
...
渲染结果如下图
3.添加阴影
渲染阴影是要消耗很多资源的,因此three默认情况下是 不生成阴影的。但添加阴影还是很容易的,首先我们要告诉renderer我们要阴影。
//告诉渲染器我们要渲染阴影
renderer.shadowMap.enabled = true;
//设置阴影的类型,默认是 THREE.PCFShadowMap
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
这个修改完后渲染结果是没有区别的,我们需要定义哪个物体投射阴影,哪个物体接收阴影,这里我们设置立方体和球投射阴影,平面接收阴影。
//平面接收阴影
plane.receiveShadow = true;
...
//立方体和球投射阴影
box.castShadow = true;
...
sphere.castShadow = true;
...
要实现阴影,还有一件事要做,那就是定义哪个灯光可以产生阴影,毕竟在一个复杂的场景中是有很多光源的。
//设置点光源可以产生阴影
spotLight.castShadow = true;
好了,阴影产生了,来看下效果:
4.添加动画
动画的本质是利用了人眼的视觉暂留特性,快速地变换画面,从而产生物体在运动的假象。而对于Three.js程序而言,动画的实现也是通过在每秒中多次重绘画面实现的。所以我们需要找到一种方法场景以一定的时间间隔不断的进行渲染,大家可能都想到使用 setInterval方法,但这个函数的问题在于:1.它不考虑浏览器发生的事情,如果你正在浏览其他页面,这个函数仍然会间隔时间被调用一次。2.它并没有跟显示器重画同步,这可能导致较高的cpu使用率,降低系统效率。3.在浏览器处理繁忙时,很可能低于设定值,会因此使整个程序放慢运行。幸运的是我们有requestAnimationFrame函数,它可以这样兼容:
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
通过requestAnimationFrame,你可以指定一个函数。按照浏览器定义的时间间隔调用,你可以在指定的函数中执行所有必要的重绘操作,而浏览器会尽可能的保证绘画过程平滑、高效。使用方法很简单,你只要写一个负责渲染的函数:
function render() {
requestAnimationFrame(render);
renderer.render(scene,camera);
}
在render函数里,我们又调了一次requestAnimationFrame,目的是让render函数持续运行。最后我们需要在场景创建完毕后,调用一次render函数来启动动画,而不是调用renderer.render(scene,camera);
...
render();
运行上述例子,我们还看不出差别,原因是我们还没有加入任何动画,现在就让我们加入动画效果:
var step = 0;
function render() {
//立方体转动
box.rotation.x += 0.02;
box.rotation.y += 0.02;
box.rotation.z += 0.02;
//+0.04代表的是小球的速度
step += 0.04;
//小球弹跳
sphere.position.x = 20 + 10*Math.cos(step)
sphere.position.y = 2 + 10*Math.abs(Math.sin(step))
requestAnimationFrame(render);
renderer.render(scene,camera);
}
查看效果:
下面贴上完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>three.js入门(2) 添加灯光、阴影、动画</title>
<script src="three.js"></script>
</head>
<body>
<script>
// 渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.setClearColor(0x000000);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
// 场景
var scene = new THREE.Scene();
// 照相机
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(-30, 40, 30);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// 辅助轴
var axes = new THREE.AxisHelper(20);
scene.add(axes);
// 平面
var planeGeometry = new THREE.PlaneGeometry(60, 20);
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0xffffff
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.position.set(15, 0, 0);
plane.rotation.x = -0.5 * Math.PI;
plane.receiveShadow = true;
scene.add(plane);
// 立方体
var boxGeometry = new THREE.BoxGeometry(4, 4, 4);
var boxMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000
});
var box = new THREE.Mesh(boxGeometry, boxMaterial);
box.position.set(-4, 3, 0);
box.castShadow = true;
scene.add(box);
// 球
var sphereGeometry = new THREE.SphereGeometry(4, 32, 32);
var sphereMaterial = new THREE.MeshLambertMaterial({
color: 0x7777ff,
wireframe: true
});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(20, 4, 2);
sphere.castShadow = true;
scene.add(sphere);
// 灯光
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-10, 20, -10);
spotLight.castShadow = true;
scene.add(spotLight);
// 灯光辅助
var spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);
// 调用渲染函数
render();
var step = 0;
function render() {
box.rotation.x += 0.02;
box.rotation.y += 0.02;
box.rotation.z += 0.02;
step += 0.04;
sphere.position.x = 20 + 10 * Math.cos(step)
sphere.position.y = 2 + 10 * Math.abs(Math.sin(step))
requestAnimationFrame(render);
renderer.render(scene, camera);
}
</script>
</body>
</html>
three.js入门(3) stats.js(用于显示动画运行的帧数)和dat.gui.js (创建简单的界面组件,用户修改代码中的变量)的使用