three.js基于物理的场景和渲染

创建物理大小场景

为了使物理上正确的照明准确,需要构建物理大小的场景,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 中的灯光类型

如果在一个黑暗的房间里打开一个灯泡,那个房间里的物体会以两种方式接收到光:

  1. 直接照明:直接来自灯泡并撞击物体的光线。
  2. 间接照明:光线在击中物体之前已经从墙壁和房间内的其他物体反弹,每次反弹都会改变颜色并失去强度。

与之匹配,three.js 中的灯光类分为两种类型:

  1. 直接光照,模拟直接光照
  2. 环境光,这是廉价且可信的间接照明方式

可以轻松模拟直接照明。直接光线从光源出来并沿直线继续,直到它们击中或不击中物体。然而,间接照明很难模拟,因为这样做需要计算从场景中所有表面永远反射的无限数量的光线。没有足够强大的计算机来做到这一点,即使限制仅计算几千条光线,每条光线只产生几次反弹,实时计算通常仍然需要很长时间。因此,如果想要场景中的真实光照,需要某种方式来伪造间接光照。在 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>
相关推荐
程序员_三木2 天前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
MossGrower4 天前
36. Three.js案例-创建带光照和阴影的球体与平面
3d图形·webgl·three.js·光照与阴影
MossGrower4 天前
34. Three.js案例-创建球体与模糊阴影
webgl·three.js·3d渲染·阴影效果
广东数字化转型5 天前
Three.js相机Camera控件知识梳理
3d·three.js
关山月5 天前
9个学习着色器的GLSL示例
前端·three.js
程序员_三木6 天前
Three.js资源-贴图材质网站推荐
javascript·webgl·three.js·材质·贴图
MossGrower6 天前
37. Three.js案例-绘制部分球体
3d图形·webgl·three.js·球体几何体
关山月6 天前
如何使用Three.js创建3D音频可视化工具
前端·three.js
MossGrower7 天前
35. Three.js案例-创建带阴影的球体与平面
webgl·three.js·阴影·球体
MossGrower8 天前
28. Three.js案例-创建圆角矩形并进行拉伸
3d图形·webgl·three.js·圆角矩形