在前面的一些文章里我们深入地学习了场景中最重要的部分:几何体、网格和相机。尽管其中有的示例可能用到了光源,但是没有过多解释。接下来的一些文章里,我们将会重点讲解一些在 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() - 复制当前颜色对象;
未完待续···