课程表

three.js课程

工具箱
速查手册

Three 光源和外部光2

当前位置:免费教程 » JS/JS库/框架 » three.js

Threejs场景对象Scene主要是由模型对象和光源对象Light构成,在实际开发过程中,多数三维场景往往需要设置光源对象,通过不同的光源对象模型生活中的光照效果,尤其是为了提高Threejs的渲染效果更需要设置好光源,就像职业摄影师拍照要打灯一样。

1.png

1.jpg


常见光源类型

环境光AmbientLight

环境光是没有特定方向的光源,主要是均匀整体改变Threejs物体表面的明暗效果,这一点和具有方向的光源不同,比如点光源可以让物体表面不同区域明暗程度不同。

//环境光:环境光颜色RGB成分分别和物体材质颜色RGB成分分别相乘
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);//环境光对象添加到scene场景中

你可以把光源颜色从0x444444更改为0x888888,可以看到threejs场景中的网格模型表面变的更亮。

点光源PointLight

点光源就像生活中的白炽灯,光线沿着发光核心向外发散,同一平面的不同位置与点光源光线入射角是不同的,点光源照射下,同一个平面不同区域是呈现出不同的明暗效果。

和环境光不同,环境光不需要设置光源位置,而点光源需要设置位置属性.position,光源位置不同,物体表面被照亮的面不同,远近不同因为衰减明暗程度不同。

你可以把案例源码中点光源位置从(400, 200, 300)位置改变到(-400, -200, -300),你会发现网格模型被照亮的位置从前面变到了后面,这很正常,光源只能照亮面对着光源的面,背对着光源的无法照射到,颜色会比较暗。

//点光源
var point = new THREE.PointLight(0xffffff);
//设置点光源位置,改变光源的位置
point.position.set(400, 200, 300);
scene.add(point);

平行光DirectionalLight

平行光顾名思义光线平行,对于一个平面而言,平面不同区域接收到平行光的入射角一样。

点光源因为是向四周发散,所以设置好位置属性.position就可以确定光线和物体表面的夹角,对于平行光而言,主要是确定光线的方向,光线方向设定好了,光线的与物体表面入射角就确定了,仅仅设置光线位置是不起作用的。

在三维空间中为了确定一条直线的方向只需要确定直线上两个点的坐标即可,所以Threejs平行光提供了位置.position和目标.target两个属性来一起确定平行光方向。目标.target的属性值可以是Threejs场景中任何一个三维模型对象,比如一个网格模型Mesh,这样Threejs计算平行光照射方向的时候,会通过自身位置属性.position和.target表示的物体的位置属性.position计算出来。

// 平行光
var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
directionalLight.position.set(80, 100, 50);
// 方向光指向对象网格模型mesh2,可以不设置,默认的位置是0,0,0
directionalLight.target = mesh2;
scene.add(directionalLight);

平行光如果不设置.position和.target属性,光线默认从上往下照射,也就是可以认为(0,1,0)和(0,0,0)两个坐标确定的光线方向。

注意一点平行光光源的位置属性.position并不表示平行光从这个位置向远处照射,.position属性只是用来确定平行光的照射方向,平行光你可以理解为太阳光,从无限远处照射过来。

聚光源SpotLight

聚光源可以认为是一个沿着特定方会逐渐发散的光源,照射范围在三维空间中构成一个圆锥体。通过属性.angle可以设置聚光源发散角度,聚光源照射方向设置和平行光光源一样是通过位置.position和目标.target两个属性来实现。

// 聚光光源
var spotLight = new THREE.SpotLight(0xffffff);
// 设置聚光光源位置
spotLight.position.set(200, 200, 200);
// 聚光灯光源指向网格模型mesh2
spotLight.target = mesh2;
// 设置聚光光源发散角度
spotLight.angle = Math.PI / 6
scene.add(spotLight);//光对象添加到scene场景中

在线运行案例

光源辅助对象

Threejs提供了一些光源辅助对象,就像AxesHelper可视化显示三维坐标轴一样显示光源对象,通过这些辅助对象可以方便调试代码,查看位置、方向。

                辅助对象                构造函数名
                聚光源辅助对象SpotLightHelper
                点光源辅助对象PointLightHelper
                平行光光源辅助对象DirectionalLightHelper

光照计算算法

Three.js渲染的时候光照计算还是比较复杂的,这里不进行深入介绍,只给大家说下光源颜色和网格模型Mesh颜色相乘的知识,如果你有兴趣可以学习计算机图形学或者WebGL教程。

Threejs在渲染的时候网格模型材质的颜色值mesh.material.color和光源的颜色值light.color会进行相乘,简单说就是RGB三个分量分别相乘。

平行光漫反射简单数学模型:漫反射光的颜色 = 网格模型材质颜色值 x 光线颜色 x 光线入射角余弦值

漫反射数学模型RGB分量表示:(R2,G2,B2) = (R1,G1,B1) x (R0,G0,B0) x cosθ

R2 = R1 * R0 * cosθ
G2 = G1 * G0 * cosθ
B2 = B1 * B0 * cosθ

颜色相乘测试

你可以通过下面代码验证上面颜色相乘的算法,比如把网格模型的颜色设置为白色0xffffff,也就意味着可以反射任意光照颜色,然后把环境光和点光源只保留红色成分,绿色和蓝色成分都设置为0。你可以看到网格模型会把渲染为红色。

// 网格模型材质设置为白色
var geometry = new THREE.BoxGeometry(100, 100, 100); //
var material = new THREE.MeshLambertMaterial({
  color: 0xffffff
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

//环境光   环境光颜色RGB成分分别和物体材质颜色RGB成分分别相乘
var ambient = new THREE.AmbientLight(0x440000);
scene.add(ambient);//环境光对象添加到scene场景中
//点光源
var point = new THREE.PointLight(0xff0000);
//设置点光源位置  光源对象和模型对象的position属性一样是Vector3对象
//PointLight的基类是Light  Light的基类是Object3D  点光源对象继承Object3D对象的位置属性position
point.position.set(400, 200, 300);
scene.add(point);

在线运行案例

你还可以尝试把网格模型设置为纯蓝色0x0000ff,光源颜色只保留红色成分不变,你可以看到网格模型的渲染效果是黑色,因为这两个颜色相乘总有一个RGB分量为0,相乘的结果是0x00000,也就是黑色。这也符合实际的物理规律,蓝色的物体不会反射红色的光线,就是黑色效果。

如果你想模拟一个舞台的各种颜色灯光效果,可以用这种思路设置RGB各个分量值来实现特定颜色光源,不过一般渲染的时候RGB三个分量是相同的,也就是表示白色光源,0xffffff表示最高强度的白色光源,0x000000相当于没有光照。


Three.js光照阴影实时计算

在具有方向光源的作用下,物体会形成阴影投影效果。

由于阴影的计算是非常消耗性能的,因此阴影默认是不计算不显示的;需要显示阴影,有以下几个条件:

1、渲染器需要开启阴影计算,这一点常常被忽视

renderer.shadowMap.enabled = true;

2、光照支持阴影,光照开启阴影

light.castShadow = true;

3、需要计算阴影的物体开启阴影

cube.castShadow = true;

4、需要有接收阴影的物体

plane.receiveShadow = true;

平行光投影计算代码

Three.js物体投影模拟计算主要设置三部分,一个是设置产生投影的模型对象,一个是设置接收投影效果的模型,最后一个是光源对象本身的设置,光源如何产生投影。

var geometry = new THREE.BoxGeometry(40, 100, 40);
var material = new THREE.MeshLambertMaterial({
  color: 0x0000ff
});
var mesh = new THREE.Mesh(geometry, material);
// mesh.position.set(0,0,0)
scene.add(mesh);

// 设置产生投影的网格模型
mesh.castShadow = true;


//创建一个平面几何体作为投影面
var planeGeometry = new THREE.PlaneGeometry(300, 200);
var planeMaterial = new THREE.MeshLambertMaterial({
  color: 0x999999
});
// 平面网格模型作为投影面
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh); //网格模型添加到场景中
planeMesh.rotateX(-Math.PI / 2); //旋转网格模型
planeMesh.position.y = -50; //设置网格模型y坐标
// 设置接收阴影的投影面
planeMesh.receiveShadow = true;

// 方向光
var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// 设置光源位置
directionalLight.position.set(60, 100, 40);
scene.add(directionalLight);
// 设置用于计算阴影的光源对象
directionalLight.castShadow = true;
// 设置计算阴影的区域,最好刚好紧密包围在对象周围
// 计算阴影的区域过大:模糊  过小:看不到或显示不完整
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 300;
directionalLight.shadow.camera.left = -50;
directionalLight.shadow.camera.right = 50;
directionalLight.shadow.camera.top = 200;
directionalLight.shadow.camera.bottom = -100;
// 设置mapSize属性可以使阴影更清晰,不那么模糊
// directionalLight.shadow.mapSize.set(1024,1024)
console.log(directionalLight.shadow.camera);

聚光光源投影计算代码

下面代码是聚光光源的设置,其它部分代码和平行光一样。

// 聚光光源
var spotLight = new THREE.SpotLight(0xffffff);
// 设置聚光光源位置
spotLight.position.set(50, 90, 50);
// 设置聚光光源发散角度
spotLight.angle = Math.PI /6
scene.add(spotLight); //光对象添加到scene场景中
// 设置用于计算阴影的光源对象
spotLight.castShadow = true;
// 设置计算阴影的区域,注意包裹对象的周围
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 300;
spotLight.shadow.camera.fov = 20;

在线运行案例

注:不同浏览器下,渲染的阴影效果可能效果不明显。

模型.castShadow属性

.castShadow属性值是布尔值,默认false,用来设置一个模型对象是否在光照下产生投影效果。具体查看threejs文档Object3D部分。

// 设置产生投影的网格模型
mesh.castShadow = true;

.receiveShadow属性

.receiveShadow属性值是布尔值,默认false,用来设置一个模型对象是否在光照下接受其它模型的投影效果。

// 设置网格模型planeMesh接收其它模型的阴影(planeMesh作为投影面使用)
planeMesh.receiveShadow = true;

光源.castShadow属性

如果属性设置为 true, 光源将投射动态阴影. 警告: 这需要很多计算资源,需要调整以使阴影看起来正确. 更多细节,查看DirectionalLightShadow. 默认值false.

// 设置用于计算阴影的光源对象
directionalLight.castShadow = true;
// spotLight.castShadow = true;

光源.shadow属性

平行光DirectionalLight的.shadow属性值是平行光阴影对象DirectionalLightShadow,聚光源SpotLight的.shadow属性值是聚光源阴影对象SpotLightShadow。关于DirectionalLightShadow和SpotLightShadow两个类的具体介绍可以参考Three.js文档Lights / Shadows分类。

阴影对象基类LightShadow

平行光阴影对象DirectionalLightShadow和聚光源阴影对象SpotLightShadow两个类的基类是LightShadow

LightShadow属性.camera

观察光源的相机对象. 从光的角度来看,以相机对象的观察位置和方向来判断,其他物体背后的物体将处于阴影中。

// 聚光源设置
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 300;
spotLight.shadow.camera.fov = 20;

LightShadow属性.mapSize

定义阴影纹理贴图宽高尺寸的一个二维向量Vector2.

较高的值会以计算时间为代价提供更好的阴影质量. 宽高分量值必须是2的幂, 直到给定设备的WebGLRenderer.capabilities.maxTextureSize, 尽管宽度和高度不必相同 (例如,(512, 1024)是有效的). 默认值为 ( 512, 512 ).

directionalLight.shadow.mapSize.set(1024,1024)

LightShadow属性.map

该属性的值是WebGL渲染目标对象WebGLRenderTarget,使用内置摄像头生成的深度图; 超出像素深度的位置在阴影中。 在渲染期间内部计算。


基类Light和Object3D

前面课程对光源的介绍主要是从认识光源的角度介绍的,本节课主要从编写代码角度介绍,根据类的继承关系设置光源对象的相关属性。

你查看文档SpotLight、DirectionalLight、环境光AmbientLight等光源对象都有一个共同的基类Light,光源Light也有一个基类Object3D。也就是说Threejs环境光、点光源等子类光源可以继承Light和Object3D两个父类的属性和方法。

1.jpg

光源位置属性

光源对象继承父类Object3D的位置属性.position。

以点光源PointLight 为例,PointLight的基类是Light,Light的基类是Object3D,点光源自然继承对象Object3D的位置属性.position。

var point = new THREE.PointLight(0xffffff);//点光源
//设置点光源位置  
//
//光源对象和模型对象的position属性一样是Vector3对象
point.position.set(400, 200, 300);

光源对象的.add()方法同样继承自基类Object3D。

//环境光对象添加到scene场景中
scene.add(ambient);
//点光源对象添加到scene场景中
scene.add(point);

光源颜色属性.color和强度属性.intensity

你查看光源光源Light文档,可以看到该类定义了光源颜色属性.color和强度系数属性.intensity。

光源颜色属性.color默认值是白色0xffffff,强度属性.intensity默认1.0,光照计算的时候会把两个属性值相乘。

比如环境光颜色设置为0xffffff,强度系数设置为0.5,把0.5设置为0.8,threejs场景中模型会变得更明亮。调节环境颜色你可以直接设置不同颜色值,比如0x44444、0xddddddd,也可以使用更为方便的强度系数去调节。对于点光源、聚光源和环境光一样继承基类Light强度系数属性.intensity。

//环境光:颜色设置为`0xffffff`,强度系数设置为0.5
var ambient = new THREE.AmbientLight(0xffffff,0.5);
scene.add(ambient);
转载本站内容时,请务必注明来自W3xue,违者必究。
 友情链接: NPS