创建物理大小场景
为了使物理上正确的照明准确,需要构建物理大小的场景,three.js 中的大小单位是米。
- 2×2×2的立方体的每边长为两米
camera.far = 100
表示看到一百米的距离camera.near = 0.1
表示距离相机十厘米以内的物体将不可见
使用米为单位是一种约定,而不是规则。如果不遵循它,那么除了物理上精确的照明之外的一切都仍然有效。如果想要物理上准确的照明,那么必须使用1单位=1米的公式将场景构建到真实世界的规模。
three.js 中的光照
要打开物理上正确的照明,只需启用渲染器的 physicallyCorrectLights
属性,将其设置为true。
vue
import { WebGLRenderer } from 'three';
// 创建渲染器
const renderer = new WebGLRenderer();
// 启用物理正确的照明
renderer.physicallyCorrectLights = true;
默认情况下禁用此设置是为了保持向后兼容性。但是,打开它没有缺点,因此可以将始终启用它。还需要调整一些参数,以使颜色和照明以物理上正确的方式工作。
three.js 中的灯光类型
如果在一个黑暗的房间里打开一个灯泡,那个房间里的物体会以两种方式接收到光:
- 直接照明:直接来自灯泡并撞击物体的光线。
- 间接照明:光线在击中物体之前已经从墙壁和房间内的其他物体反弹,每次反弹都会改变颜色并失去强度。
与之匹配,three.js 中的灯光类分为两种类型:
- 直接光照,模拟直接光照
- 环境光,这是廉价且可信的间接照明方式
可以轻松模拟直接照明。直接光线从光源出来并沿直线继续,直到它们击中或不击中物体。然而,间接照明很难模拟,因为这样做需要计算从场景中所有表面永远反射的无限数量的光线。没有足够强大的计算机来做到这一点,即使限制仅计算几千条光线,每条光线只产生几次反弹,实时计算通常仍然需要很长时间。因此,如果想要场景中的真实光照,需要某种方式来伪造间接光照。在 three.js 中有几种技术可以做到这一点,其中环境光就是其中之一。其他的几种技术分别是基于图像的照明 (IBL) 和光探测器。
直接照明
three.js 中总共有四种直接光源类型可用,每一种都模拟一个常见的现实世界光源:
DirectionalLight
=> 阳光PointLight
=> 灯泡RectAreaLight
=> 条形照明或明亮的窗户SpotLight
=> 聚光灯
默认情况下禁用阴影。即使使用基于物理的渲染,现实世界和 three.js 之间的一个区别是默认情况下对象不会阻挡光线。光路径中的每个物体都会收到照明,即使路上有一堵墙。
我们可以逐个对象的、逐个光照的手动启用阴影。但是如此一来很麻烦,因此通常只为一盏灯或两盏灯启用阴影,尤其是当场景需要在移动设备上工作时。只有直接光类型可以投射阴影,环境光不能。
DirectionalLight
DirectionalLight
设计的目的是模仿遥远的光源,例如太阳。DirectionalLight
的光线不会随着距离而消失。场景中的所有对象都将被同样明亮地照亮,无论它们放在哪里,即使是在灯光后面。
DirectionalLight
的光线是平行的,从一个位置照向一个目标。默认情况下,目标放置在我们场景的中心(点(0,0,0)),所以当我们移动周围的光线时,它总是会向中心照射。
创建灯光
创建light实例对象,DirectionalLight
构造函数有两个参数,颜色color 和强度intensity。此处创建一个强度为 8 的纯白光:
vue
import { DirectionalLight } from 'three';
const light = new DirectionalLight('white', 8);
// 将灯光添加到场景中
scene.add(light)
所有 three.js 灯都有颜色和强度设置,继承自 Light
基类。
创建灯光后并将其添加到场景中,向场景中添加灯光就像添加网格一样。
scene.add(mesh,light);
调用中就添加了灯光和网格。可以添加任意数量的对象,用逗号分隔。
定位灯光
灯光从light.position
照向light.target.position
。灯光和目标的默认位置都是场景的中心(0,0,0)。这意味着光线当前正在从(0,0,0)照向(0,0,0)。 这确实有效,但看起来不太好。可以通过调整light.position
来改善灯光的外观。通过将位置设置为(10,10,10)来达到向左、向上和朝向用户移动它的效果。
js
import { DirectionalLight } from 'three';
const light = new DirectionalLight('white', 8);
// 灯光从(10,10,10)照向(0,0,0)
light.position.set(10, 10, 10);
基于物理的材质
MeshBasicMaterial
这种材质会忽略场景中的任何灯光。 MeshBasicMaterial
是 three.js 中提供的最基本的材料,它根本不会对灯光做出反应,并且网格的整个表面都用单一颜色着色。不执行基于视角或距离的着色,因此对象看起来甚至不是三维的,而只是一个二维轮廓。
可以使用MeshStandardMaterial
,这是一种高质量、通用、物理精确的材质,可以使用真实世界的物理方程对光做出反应。所以MeshStandardMaterial
可以是几乎所有情况下的首选标准材质。
所有材质基于Material
基类,但不能直接使用Material
。而必须始终使用它的派生类中的某一个,例如MeshStandardMaterial
或者MeshBasicMaterial
。
更改材质的颜色
设置材质参数与盒子几何体等其他类略有不同,必须需要使用具有命名参数的规范对象:
js
const spec = {
color: 'purple',
}
const material = new MeshStandardMaterial(spec);
// 或者内联声明对象
const material = new MeshStandardMaterial({ color: "purple" });
旋转立方体
旋转立方体,这样就不再直视它了。调整对象的旋转与设置位置(平移)的方式大致相同。用于变换物体的方法有平移、旋转和缩放。
vue
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import {
BoxGeometry,
Color,
Mesh,
MeshStandardMaterial,
PerspectiveCamera,
Scene,
DirectionalLight,
WebGLRenderer,
} from 'three';
const canvas = ref<HTMLElement | null>(null);
onMounted(() => {
// 创建场景
const scene = new Scene();
// 设置场景的背景颜色
scene.background = new Color('gold');
// 创建相机并设置其位置
const fov = 35;
const aspect = canvas.value?.clientWidth / canvas.value?.clientHeight;
const near = 0.1;
const far = 100;
const camera = new PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 0, 10);
// 创建2*2*2盒子形状的几何体
const geometry = new BoxGeometry(2, 2, 2);
// 创建默认材质
const material = new MeshStandardMaterial({color:'white'});
// 创建网格
const mesh = new Mesh(geometry, material);
// 设置对象旋转
mesh.rotation.set(-0.5, -0.1, 0.8);
// 创建灯光
const light = new DirectionalLight('white', 8);
// 灯光从(10,10,10)照向(0,0,0)
light.position.set(10, 10, 10);
// 将网格和灯光添加到场景中
scene.add(mesh,light);
// 创建渲染器
const renderer = new WebGLRenderer();
// 设置渲染器的大小
renderer.setSize(canvas.value?.clientWidth, canvas.value?.clientHeight);
// 设置设备像素比(DPR)
renderer.setPixelRatio(window.devicePixelRatio);
// 添加canvas元素在页面中
canvas.value?.append(renderer.domElement);
// 渲染场景
renderer.render(scene, camera);
});
</script>
<template>
<div ref="canvas" id="canvas"></div>
</template>
<style scoped lang="scss">
#canvas {
width: 100vw;
height: 100vh;
}
</style>