T hreejs虚拟光源是对自然界光照的模拟,threejs搭建虚拟场景的时候,为了更好的渲染场景,往往需要设置不同的光源,设置不同的光照强度,就像摄影师给你拍照要设置各种辅助灯光一样。
光源辅助对象
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提供了多种类型的灯光,包括环境光、点光源、平行光源和聚光灯。这些灯光可以用来照亮场景中的物体,使其看起来更加真实。
- 环境光(AmbientLight):环境光是没有特定方向的光源,主要是均匀整体改变Threejs物体表面的明暗效果,这一点和具有方向的光源不同,比如点光源可以让物体表面不同区域明暗程度不同。环境光会均匀地照亮场景中的所有物体,没有方向,不能用来投射阴影。创建环境光的代码如下:
var ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 光的颜色为白色,强度为0.5
scene.add(ambientLight); // 将环境光添加到场景中
- 点光源(PointLight):点光源是从一个点向四面八方发射光线的光源,可以用来模拟灯泡或者火炬等光源,点光源就像生活中的白炽灯,光线沿着发光核心向外发散,同一平面的不同位置与点光源光线入射角是不同的,点光源照射下,同一个平面不同区域是呈现出不同的明暗效果。和环境光不同,环境光不需要设置光源位置,而点光源需要设置位置属性.position,光源位置不同,物体表面被照亮的面不同,远近不同因为衰减明暗程度不同。
你可以把案例源码中点光源位置从(400, 200, 300)位置改变到(-400, -200, -300),你会发现网格模型被照亮的位置从前面变到了后面,这很正常,光源只能照亮面对着光源的面,背对着光源的无法照射到,颜色会比较暗。
。创建点光源的代码如下:
var pointLight = new THREE.PointLight(0xffffff, 1, 100); // 光的颜色为白色,强度为1,距离为100
pointLight.position.set(0, 10, 0); // 设置光源的位置 scene.add(pointLight); // 将点光源添加到场景中
- 平行光源(DirectionalLight):
平行光顾名思义光线平行,对于一个平面而言,平面不同区域接收到平行光的入射角一样。
点光源因为是向四周发散,所以设置好位置属性.position就可以确定光线和物体表面的夹角,对于平行光而言,主要是确定光线的方向,光线方向设定好了,光线的与物体表面入射角就确定了,仅仅设置光线位置是不起作用的。
在三维空间中为了确定一条直线的方向只需要确定直线上两个点的坐标即可,所以Threejs平行光提供了位置.position和目标.target两个属性来一起确定平行光方向。目标.target的属性值可以是Threejs场景中任何一个三维模型对象,比如一个网格模型Mesh,这样Threejs计算平行光照射方向的时候,会通过自身位置属性.position和.target表示的物体的位置属性.position计算出来。
平行光源是从一个方向发射的光线,可以用来模拟太阳等光源。创建平行光源的代码如下:
var directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 光的颜色为白色,强度为1
directionalLight.position.set(0, 1, 0); // 设置光源的方向
scene.add(directionalLight); // 将平行光源添加到场景中
- 聚光灯(SpotLight):聚光源可以认为是一个沿着特定方会逐渐发散的光源,照射范围在三维空间中构成一个圆锥体。通过属性.angle可以设置聚光源发散角度,聚光源照射方向设置和平行光光源一样是通过位置.position和目标.target两个属性来实现。聚光灯是从一个点向一个方向发射的光线,可以用来模拟手电筒等光源。创建聚光灯的代码如下:
var spotLight = new THREE.SpotLight(0xffffff, 1, 100); // 光的颜色为白色,强度为1,距离为100
spotLight.position.set(0, 10, 0); // 设置光源的位置
spotLight.target.position.set(0, 0, 0); // 设置光源的目标位置
scene.add(spotLight); // 将聚光灯添加到场景中
<template>
<div ref="container"></div>
</template>
<script>
import { ref, onMounted } from 'vue'
import * as THREE from 'three'
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
export default {
setup() {
const container = ref(null)
onMounted(() => {
// 创建场景
const scene = new THREE.Scene()
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.z = 5
// 创建渲染器
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
container.value.appendChild(renderer.domElement)
let controls = new OrbitControls(camera, renderer.domElement);
controls.update();
// 创建几何体
const geometry = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
// 创建环境光
const ambientLight = new THREE.AmbientLight(0xcccccc, 0.5)
scene.add(ambientLight)
// 创建点光源
const pointLight = new THREE.PointLight(0xffffff, 1)
pointLight.position.set(5, 5, 5)
scene.add(pointLight)
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff,1)
// 设置光源的方向:通过光源position属性和目标指向对象的position属性计算光线的方向
directionalLight.position.set(6,6,6)
// 光的方向指向对象网格模型mesh,不设置默认为0,0,0
directionalLight.target = cube
scene.add(directionalLight)
// 聚光灯
const spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 0, 200, 100 );
scene.add(spotLight)
// 创建GUI调试工具
const gui = new GUI()
const lightFolder = gui.addFolder('Light Settings')
lightFolder.add(ambientLight, 'intensity', 0, 1).name('Ambient Light Intensity')
lightFolder.add(pointLight, 'intensity', 0, 1).name('Point Light Intensity')
lightFolder.add(directionalLight, 'intensity', 0, 1).name('directionalLight Light Intensity')
lightFolder.add(spotLight, 'intensity', 0, 1).name('spotLight Light Intensity')
// 渲染循环
const animate = () => {
requestAnimationFrame(animate)
// cube.rotation.x += 0.01
// cube.rotation.y += 0.01
renderer.render(scene, camera)
}
animate()
})
return { container }
}
}
</script>