three.js 03-01 之 AmbientLight 光源

时间:2022-04-25 04:38:27

    在前面的一些文章里我们深入地学习了场景中最重要的部分:几何体、网格和相机。尽管其中有的示例可能用到了光源,但是没有过多解释。接下来的一些文章里,我们将会重点讲解一些在 three.js 中常用的几种光源。

    在 three.js 中,如果没有光源,我们就看不到任何渲染结果。为此,three.js 为我们准备了多种不同用途的光源,如下表所示:

光源名称 描述
AmbientLight (环境光) 这是一种基础光源,它的颜色会添加到整个场景和所有对象的当前颜色上
PointLight (点光源) 空间中的一个发光点,朝所有的方向发射光线
SpotLight (聚光灯光源) 这种光源有聚光的效果,类似台灯、天花板吊灯,或者手电筒
DirectionalLight (方向光) 也称作无限光。从这种光源发出的光线可以看做是平行的,例如太阳光
HemisphereLight (半球光) 这是一种特殊光源。可以用来创建更加自然的室外光线,模拟反光面和光线微弱的天空
RectAreaLight (面光源) 使用这种光源可以指定散发光线的平面,而不是空间中的一个点
LensFlare (镜头眩光) 这不是一种光源,但是通过它可以为场景中的光源添加眩光效果
    我们先从最基础的光源 AmbientLight 环境光开始介绍。按照惯例,我们先来一个完整的示例,如下所示:

<!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() - 复制当前颜色对象;


未完待续···