在前面的一些文章里我们深入地学习了场景中最重要的部分:几何体、网格和相机。尽管其中有的示例可能用到了光源,但是没有过多解释。接下来的一些文章里,我们将会重点讲解一些在 three.js 中常用的几种光源。
在 three.js 中,如果没有光源,我们就看不到任何渲染结果。为此,three.js 为我们准备了多种不同用途的光源,如下表所示:
光源名称 | 描述 |
---|---|
AmbientLight (环境光) | 这是一种基础光源,它的颜色会添加到整个场景和所有对象的当前颜色上 |
PointLight (点光源) | 空间中的一个发光点,朝所有的方向发射光线 |
SpotLight (聚光灯光源) | 这种光源有聚光的效果,类似台灯、天花板吊灯,或者手电筒 |
DirectionalLight (方向光) | 也称作无限光。从这种光源发出的光线可以看做是平行的,例如太阳光 |
HemisphereLight (半球光) | 这是一种特殊光源。可以用来创建更加自然的室外光线,模拟反光面和光线微弱的天空 |
RectAreaLight (面光源) | 使用这种光源可以指定散发光线的平面,而不是空间中的一个点 |
LensFlare (镜头眩光) | 这不是一种光源,但是通过它可以为场景中的光源添加眩光效果 |
<!DOCTYPE html>
<html>
<head>
<title>示例 03.01 - 环境光</title>
<script src="../build/three.js"></script>
<script src="../build/js/controls/OrbitControls.js"></script>
<script src="../build/js/libs/stats.min.js"></script>
<script src="../build/js/libs/dat.gui.min.js"></script>
<script src="../jquery/jquery-3.2.1.min.js"></script>
<style>
body {
/* 设置 margin 为 0,并且 overflow 为 hidden,来完成页面样式 */
margin: 0;
overflow: hidden;
}
/* 统计对象样式 */
#Stats-output {
position: absolute;
left: 0px;
top: 0px;
}
</style>
</head>
<body>
<!-- 用于 WebGL 输出的 Div -->
<div id="WebGL-output"></div>
<!-- 用于统计 FPS 输出的 Div -->
<div id="Stats-output"></div>
<!-- 运行 Three.js 示例的 Javascript 代码 -->
<script type="text/javascript">
var scene;
var camera;
var render;
var controls;
var stats;
var guiControls;
var cube;
var sphere;
// 当所有元素加载完毕后,就执行我们 Three.js 相关的东西
$(function() {
stats = initStats();
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); // 2147483647
camera.position.set(-30, 40, 30);
render = new THREE.WebGLRenderer( {antialias: true} ); // antialias 抗锯齿
render.setSize(window.innerWidth, window.innerHeight);
render.setClearColor(0xEEEEEE);
render.shadowMap.enabled = true; // 允许阴影投射
$('#WebGL-output')[0].appendChild(render.domElement);
window.addEventListener('resize', onWindowResize, false);
var target = new THREE.Vector3(scene.position.x, scene.position.y ,scene.position.z);
controls = new THREE.OrbitControls(camera, render.domElement);
controls.target = target;
camera.lookAt(target);
scene.add(new THREE.AxisHelper(20));// 加入坐标轴
// 加入一个平面(带线框效果)
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterials = [
new THREE.MeshLambertMaterial( {color: 0xFFFFFF} ),
new THREE.MeshBasicMaterial( {color: 0xFFFFFF, wireframe: true, transparent: true, opacity: 0.5} )
];
var plane = THREE.SceneUtils.createMultiMaterialObject(planeGeometry, planeMaterials);
plane.rotation.x = -0.5 * Math.PI; // 沿着 X轴旋转-90°
plane.position.x = 15; // 沿着 x轴右移 15个单位
plane.position.y = 0; // y轴为 0
plane.position.z = 0; // z轴为 0
plane.children[0].receiveShadow = true; // 非线框几何平面接收阴影
scene.add(plane);
// 加入一个立方体
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial( {color: 0xFF0000} );
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
cube.castShadow = true; // 立方体投射阴影
scene.add(cube);
// 加入一个球体
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial( {color: 0x7777FF} );
sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
sphere.castShadow = true; // 球体投射阴影
scene.add(sphere);
// 加入一个环境光源
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
// 加入一个聚光灯光源
// 注:基础材质 MeshBasicMaterial 不会对光源产生反应,因此要改用 MeshLambertMaterial 或 MeshPhongMaterial 材质才有效果
var spotLight = new THREE.SpotLight( 0xFFFFFF);
spotLight.position.set(-60, 60, -10);
spotLight.castShadow = true; // 光源产生阴影
spotLight.shadow.mapSize.width = 2048; // 必须是 2的幂,默认值为 512
spotLight.shadow.mapSize.height = 2048; // 必须是 2的幂,默认值为 512
scene.add(spotLight);
/** 用来保存那些需要修改的变量 */
guiControls = new function() {
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.04;
this.ambientColor = '#0c0c0c';
this.disableSpotLight = false;
}
/** 定义 dat.GUI 对象,并绑定 guiControls 的有关属性 */
var gui = new dat.GUI();
gui.addColor(guiControls, 'ambientColor').onChange( function(e) {
ambientLight.color = new THREE.Color(e);
});
gui.add(guiControls, 'disableSpotLight').onChange(function(e){
spotLight.visible = !e;
});
renderScene();
});
/** 渲染场景 */
function renderScene() {
stats.update();
rotateCube(); // 旋转立方体
bounceSphere() // 弹跳球体
requestAnimationFrame(renderScene);
render.render(scene, camera);
}
/** 初始化 stats 统计对象 */
function initStats() {
stats = new Stats();
stats.setMode(0); // 0 为监测 FPS;1 为监测渲染时间
$('#Stats-output').append(stats.domElement);
return stats;
}
/** 当浏览器窗口大小变化时触发 */
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
render.setSize(window.innerWidth, window.innerHeight);
}
/** 转动立方体 */
function rotateCube() {
cube.rotation.x += guiControls.rotationSpeed;
cube.rotation.y += guiControls.rotationSpeed;
cube.rotation.z += guiControls.rotationSpeed;
}
/** 弹跳球体 */
var step = 0;
function bounceSphere() {
step += guiControls.bouncingSpeed;
sphere.position.x = 20 + (10 * Math.cos(step));
sphere.position.y = 2 + (10 * Math.abs(Math.sin(step)));
}
</script>
</body>
</html>
先来看一下 AmbientLight 环境光的构造方法:
AmbientLight( color, intensity )
color - 表示构成 RGB 颜色组件的数值;
intensity - 可选。表示光强弱的数值;
由上例可见,AmbientLight 的光线没有特定的来源,而且这个光源也不会影响阴影的生成。我们不应该将 AmbientLight 作为场景中的唯一光源(可以试着在本示例中点击右上角的 disableSpotLight 复选框,关掉聚光灯后只剩环境光的效果)。而应该结合其他光源(如 SpotLight、DirectionalLight 等)同时使用 AmbientLight,目的是弱化阴影或添加一些颜色。作为试验,可以在本示例右上角菜单中调整 AmbientLight 的颜色,如手动输入 #007700 明亮的绿色,那么场景中的各个对象就会笼罩在一片明亮的绿光下。
在这个示例中,我们改变环境光的颜色时用到了一个 THREE.Color() 对象,构造这个对象时,通常情况下可以通过用十六进制字符串如"#0c0c0c"或者十六进制值如 0x0c0c0c 来指定颜色。下面我们来看看这个对象常用的一些函数和属性:
set(value) - 把当前颜色设置成指定的十六进制值;
setHex(value) - 把当前颜色设置成指定的十六进制数字值;
setRGB(r, g, b) - 根据提供的 RGB 值设置颜色,参数值得范围从 0 到 1;
getHex() - 以十六进制值形式从颜色对象获取颜色值,如 26852316;
getHexString() - 以十六进制字符串形式从颜色对象获取颜色值,如"99bbdc";
getStyle() - 以 CSS 值得形式从颜色对象获取颜色值,如"rgb(408,442,476)";
add(color) - 把提供的颜色加到当前颜色上;
clone() - 复制当前颜色对象;
未完待续···