第191期:看起来不像立方体

封面图

在上一节中,我们用threejs成功创建了一个蓝色的立方体。但是如果我们仔细观察一下,会发现几个非常有意思的现象,比如:

  • 我们在创建立方体的时候并没有对它做旋转操作,但是它看起来却是像被旋转过一样。
  • 这个立方体看起来不够立体,更像是一个六边形。
  • 如果我们将网页放大一些,会看到这个立方体的边缘并不是直线,而是有很多的锯齿形状。

为什么会出现这种现象呢?下面我们来一个一个的弄明白其中的缘由。

似乎被旋转过

这个问题非常简单,让我们在之前的代码中稍作修改,将相机的位置稍微调整一下。

scss 复制代码
// 设置相机位置
camera.position.set(0, 0, 10)
// 设置镜头方向
camera.lookAt(0, 0, 0)

此时观察页面,发现我们只能看到一个正方形,难道立方体消失了吗?其实并不是,理解这个问题需要我们用到三维空间的坐标系以及三视图的概念。

3D笛卡尔坐标

3D笛卡尔坐标系由XYZ 轴组成,三轴交叉于点(0,0,0)(称为原点)。二维坐标系相似,但只有XY轴。这些概念在中学的数学中我们都学过,这里简单熟悉一下就好。

所有的3D图形系统都使用这样的坐标系,甚至我们在进行web开发使用的css也是使用的二维笛卡尔坐标系。

在高中时代,我们见过的空间直角坐标系如下:

它和上面彩色的坐标系这里暂时可以简单认为是同一个东西,因为我们把它沿x轴旋转90度,然后再沿y轴旋转90度,即可得到上面的坐标系。

三视图

在工业零件的加工过程中,我们通常会用到三视图。

三视图指的是:主视图、俯视图和左视图。左视图通常又称为侧视图。

通过三视图的测量数据,工人就可以用车床车削出正确的零件。

比如:

与之类似,当我们将相机的位置改为:

scss 复制代码
// 设置相机位置
camera.position.set(0, 0, 10)
// 设置镜头方向
camera.lookAt(0, 0, 0)

其实是在坐标系的(0,0,10)这个位置对立方体进行观察,相当于一个主视图、或者侧视图。我们只看到立方体的一个面,所以立方体看起来就只是一个正方形。

当我们将相机的位置改为:

scss 复制代码
// 设置相机位置
camera.position.set(0, 10, 10)
// 设置镜头方向
camera.lookAt(0, 0, 0)

我们就可以看到立方体的两个面。

就像是一个六边形。

当我们将相机的位置改为:

scss 复制代码
// 设置相机位置
camera.position.set(10, 10, 10)
// 设置镜头方向
camera.lookAt(0, 0, 0)

相当于我们可以看到立方体的一个角,此时看起来就像是一个正六边形。

但此时这个立方体看起来还是个六边形,还是不太像一个立方体,这是为什么呢?是不是和我们用的材质有关系?

我们一起来验证一下。

材质和灯光

我们知道在现实生活中我们看到的物体都是各种各样材料,玻璃、木头、塑料、钢铁等等。由于他们能够将光线反射到我们的眼睛中,所以我们能够看到它们。

所以我们能看到物体的一个条件是:这个物体首先得对光有反应,起码能反射光线。而体现在threejs 中则是这种材质需要能够对光照有反应。既然需要光,那么我们先在场景中添加一个光源。

csharp 复制代码
// 创建灯光
const light = new THREE.DirectionalLight('white', 8)
scene.add(light)

DirectionalLight 是平行光,通常用来模仿太阳的光线,它的光线不会随着距离而消失。此时我们查看页面,似乎没什么变化。

这是因为我们虽然在场景中添加了灯光,但是我们创建立方体时,采用材质是(MeshBasicMaterial)基础材质,这种材质不受光照的影响,换句话说,这种材质会忽略场景中任何灯光。

因为 MeshBasicMaterial是 three.js 中提供的最基本的材料。它不会对灯光做出反应,并且网格的整个表面都用单一颜色着色。不执行基于视角或距离的着色,因此对象看起来甚至不是三维的。我们所能看到的只是一个二维轮廓。

我们将材质换成(MeshStandardMaterial) 标准材质,

php 复制代码
const mater = new THREE.MeshStandardMaterial({
  color: new THREE.Color('blue')
  // roughness: 0.3
})

(MeshStandardMaterial) 标准材质,是一种高质量、通用、物理精确的材料,可以使用真实世界的物理方程对光做出反应。此时我们观察页面,发现我们已经可以看到有黑色的阴影出现了,有了一定的立体效果。

就像给姑娘拍照一样,好看的照片需要优雅的姿势,和适合的灯光来配合。诚然,这张照片并不是很好看,我们还需要让这位姑娘换个姿势,另外再调整一下灯光。

我们先将灯光的默认位置打印出来看下:

yaml 复制代码
// light.position _Vector3 {x: 0, y: 1, z: 0}

原来它在坐标轴(0,1,0)这个位置,我们调整一下,设置为(40, 30, 60)

arduino 复制代码
// 创建灯光
const light = new THREE.DirectionalLight('white', 8)
light.position.set(40, 30, 60)
console.log('light.position', light.position)
scene.add(light)

然后我们需要给姑娘换个好点的姿势,好么,旋转一下它:

c 复制代码
cube.rotation.set(-0.3, 0.7, -0.1)

此时我们观察页面,这个立方体就很有立体感了。

辅助对象

在旋转立方体的过程中,我们发现旋转的程度不是特别容易控制。这个也容易解决,我们可以添加一个辅助对象 AxesHelper 轴辅助对象,它可以简单模拟3个坐标轴的对象。 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴。

我们将它添加到场景中:

ini 复制代码
const axesHelper = new THREE.AxesHelper( 5 );
scene.add( axesHelper );

参数 5 表示轴线段的长度,此时我们就很容易去调整在哪根轴上旋转多少度比较合适,以达到合适的效果。

抗锯齿

解决了不像立方体的问题,我们还有一个问题要处理,就是处理立方体边缘的锯齿,处理方法很简单,我们只需要启用渲染器的抗锯齿参数 antialias 即可,我们将渲染器的antialias 参数 设置为true。

arduino 复制代码
// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true // 启用抗锯齿
})

观察页面发现锯齿消失了。

抗锯齿是一项非常复杂的技术,暂时我们只需要知道启用antialias这个属性即可。

小结

我们发现了上节创建的立方体的三个小问题,并通过设置灯光、调整灯光位置、调整立方体的姿态,以及启用渲染器的抗锯齿属性消除了立方体边缘的锯齿。

接下来我们会继续关注其中的一些容易被忽略的问题,比如:

  • 我们创建几何体时所设置的(2,3,2)它们的单位是什么,米、厘米、分米?
  • 我们是不是可以给立方体加个纹理,让它看起来像一块石头、木头或者铁块儿?

在下一节中,我们将对这些问题做些比较详细的介绍。

相关推荐
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰10 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪10 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪10 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy11 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom12 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom12 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom12 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom12 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom12 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试