3D 基本概念及 three.js 入门 您所在的位置:网站首页 3dmax怎么居中物体在屏幕 3D 基本概念及 three.js 入门

3D 基本概念及 three.js 入门

2023-12-25 18:36| 来源: 网络整理| 查看: 265

cover 本人是一前端,会点3D建模和Unity(作品如上)。最近接触了点threejs,个人做点小总结,同时分享一些我个人在3D方面的经验与理解。本文并不会过多教你如何使用threejs的API,仅向没有 3D 经验前端同学分享一些 3D 的基本概念,并初步了解 threejs。希望这篇文章能让对这方面感兴趣的同学在学习 threejs 时能有一些基础的知识,能更容易阅读 threejs 文档。

坐标系

先从三维空间最基本的坐标系说起,空间坐标系比平面坐标系多了一个 z 轴,任意 xyz 的值能确定空间中的一点。空间坐标系常见两种,左手坐标系和右手坐标系。在threejs中使用的是右手坐标系,可以想像在屏幕上y轴向上,x轴向右。屏幕与xoy重合,那么z轴就垂直屏幕指向屏幕前的你,这是右手坐标系。

图1 左手坐标系(左)和右手坐标系(右)

这里例举在空间中常用的三种坐标系:

世界坐标系 物体坐标系 摄像机坐标系 世界坐标系

用于描述物体在场景中的绝对位置,其他坐标系都基于世界坐标系定位。有一个绝对的世界坐标系才能描述两个物体之间的位置关系。

物体坐标系

物体坐标系是一个很重要的概念,场景中任何一个物体都有其物体坐标系。物体坐标系是物体本身的坐标系,其原点可以在物体任何位置比如物体中心、物体某个顶点,甚至在物体外,具体物体坐标系原点在哪都能由建模师手动设置。如下图,一个边长为1的立方体。立方体物体坐标系原点与其本身Q点重合,那么P点在其自身坐标系中的坐标是(1, 1, 1)。 从这里不难看出,物体坐标系能描述物体在一个场景中的前后左右、上下朝向。

图2 物体坐标系

相机坐标系

相机是观察者,也处于场景中。相机坐标系可以看作一个特殊的物体坐标系, xoy平面与屏幕平行,z轴方向就是摄像机的观察方向。

坐标

在空间中xyz定位一个点,xyz是其坐标。一个空间中的某一个物体位置是确定的,但其坐标取决于你选择的坐标系。 常见坐标:

世界坐标(对应世界坐标系) 局部坐标(对应物体坐标系) 世界坐标

世界坐标指场景中某一点相对于世界坐标系原点的位置。无论这个点在什么地方或什么其他坐标系内,这个点总是有一个世界坐标。

局部坐标

局部坐标指一个点在某个物体坐标系中的坐标。物体坐标系可以通过旋转平移与世界坐标系重合,局部坐标和世界坐标之间有转换关系。可以用平面坐标系简单理解系下世界坐标系和物体坐标系关系。如下图,有一棵倾斜的树,其本身坐标系原点在树根。树上这片树叶在树本身坐标系中坐标是(x2, y2),但在世界坐标系中坐标是(x1, y2)。这片树叶没有发生位移,但它在不同坐标系中的坐标可能不同。

图3 物体坐标系和世界坐标系

局部坐标对 3D 对象的嵌套有着十分重要的意义。3D 对象和 DOM 一样有着嵌套关系,在一个 3D 对象中其子物体的的局部坐标是相对于父物体的本身的坐标系(物体坐标系)而言的。

场景

所有的3D物体都需要在一个场景中,场景可以理解为一个空的无限大的容器(游戏场景有限是因为空气墙😃),这个容器里只有一个你看不见的世界坐标原点及 3 个坐标轴。可以在场景里放置任意的物体、灯光、摄像机。在 threejs 中可以用Scene实例化一个场景对象。场景实例的add()这个方法可像场景中添加 3D 对象。

// threejs创建一个场景 import * as THREE from "three"; const scene = new THREE.Scene(); // 向场景中添加物体 scen.add(...);

一个应用中可以有多个场景,在不同的场景会设置不同的物体、光照等,在游戏中所看到的“加载进度”其实就是在加载不同的scene实例。

几何体

几何体是数学模型,描述了一组点及其拓扑关系。在 threejs 可以使用BufferGeometry类给一组连续的点创建几何体实例。几何体只是一些数据,据并不可见。BufferGeometry实例化的对象并不能算一个 3D 对象,不能放在场景中,它只是一些数据,一些继承自Object3D的类可以通过几何体数据构造成一个可放在场景中的 3D 对象。

一个点的信息只需 xyz 三个值就能表示。说到点就不得不说一下向量,在 threejs 中Vector3表示一个三维向量。一个点的坐标可以用Vector3表示。Vector3还可用来表示任何三个维度的信息,如坐标,向量,颜色 RGB。

线

两个点就能确定条线段,至少需要两个Vector3才能确定一条线段。在 threejs 中BufferGeometry创建时传入的数组是一个点的数组,其中每相邻的两个点代表有连接关系,即一条线段。

确定一个面至少需要三个点,在 3D 模型中最基本的面都是三角形。因为在空间中,直接由三个以上的点围城的图形不一定是个平面,这些点很有可能不在一个平面。比如四个点按顺序连接构成的图形可能就是一个空间四边形。在三维建模中删除某些点就会有这种情况,叫“破面”。

图4 空间四边形

但是任意三个点一定在一个平面内,然后可以使用多个三角形组成其他多边形。所以在尝试使用BufferGeometry画面时,在每次画完第三个点后再回到本次三角形的起点,之后再开始画下一个三角形。例如,下面的一组点就描述了在空间中 ac 边重合的两个三角形,组成了一个空间四边形。

// 数组每个元素是一个Vector3实例, abcd不重合 let points = [a, b, c, a, d, c]; 曲面

在 3D 技术中曲面是一个概念,没有真正的曲面。曲面都是许多小平面组成的一个近似曲面的立体多边形经过平滑处理成的。当多边形细分越多,就越接近其所模拟的曲面。创建越多的三角面就越耗费性能,所以一款游戏的模型精度也相当程度地反应了其质量和对硬件配置的要求。

图5 三角形构成的近似半球体

Threejs 内置的基本几何体

一般情况下基本不用BufferGeometry去画复杂的模型。复杂的模型一般在如 3dsmax、blender 这样的建模软件中创建、编辑、导出。 但threejs 内置了一些基本常用的几何体,可用于做些测试,如下:

平面(PlaneGeometry) 球体(SphereGeometry) 立方体(BoxGeometry) 圆柱(CylinderGeometry) 圆锥(ConeGeometry) … 用线框渲染threejs内置几何体可以看到所有的面都是由三角形组成的。

图6 Three js内置几何体线框渲染

3D 对象

只有3D对象才能放在场景中,在 threejs 中所有的 3D 对象都是继承Object3D这个类的。所有的 3D对象都会有一些相同的基本属性。比如:

position(坐标) rotation(旋转) scale(缩放) up(物体坐标系 y 轴正方向) parent(父级对象) children (子对象数组) … 空对象

Object3D这个类也可以实例化,将Object3D实例化的对象放在场景中将不会看到任何效果,因为它是一个空物体,但你依然可以改变这个空物体的坐标,或者旋转它。这并不是没有意义的,这个空物体在对 3D 物体编组的时候大有用处。因为 3D 对象可嵌套的关系,一般可以用空对象作多个对象的 父级对象,这样就实现了对多个对象编组的效果。在平移旋转父级对象时,其子对象都会和父级对象本身的坐标系原点保持相对静止,这样就能实现多个物体像一个整体的效果。

const empty = new THREE.Object3D(); //... 初始化box,ball省略 // 将box和ball放到empty下作为其子对象 empty.add(box, ball); // 改变父级对象坐标 empty.postion.set(1, 1, 1);

threejs 有一个Group类用于分组,其本质和Object3D一样,Group 是语义化的 api,具有迷惑性。使用Object3D更容易理解3D对象的父子嵌套概念。

常用 3D 对象

threejs 中有很多继承至Object3D的类,它们的实例化大多需要两种对象,几何体和材质。根据几何体和材质来确定这个 3D 对象的特性。常用来渲染场景物体的有以下几种:

Points:以几何体的点信息创建空间中的点 Line:以几何体的边信息创建空间中的线 Mesh:以几何体的面信息创建空间中的平面

这三个构造函数,接受同一个BufferGeometry实例并给予相应材质会得到不同的渲染结果。分别会渲染出几何体的点,线和面。

// 创建三角形几何体 const triangleGeometry = new THREE.BufferGeometry().setFromPoints([ new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(-1, 0, 0), new THREE.Vector3(1, 0, 0), ]); //点 const trianglePoints = new THREE.Points( triangleGeometry, // 几何体 new THREE.PointsMaterial({ color: 0xffffff, size: 0.05 }) // 材质 ); // 线 const triangleLines = new THREE.Line( triangleGeometry, new THREE.LineBasicMaterial({ color: 0xffffff }) ); // 面 const trianglePanle = new THREE.Mesh( triangleGeometry, new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide }) );

图7 三角形几何体的三种对象创建形式

Mesh 是最常用的 3D 对象,场景中的大多数 3D 模型都是 Mesh。Mesh 可以理解为给几何体蒙皮,创建了一个和几何体一样的壳,这个壳是全部由三角形组成的。通过几何体信息和材质就可以创建出渲染结果中可见的 3D 对象。

图8 通过mesh创建3D对象(同时渲染了线框)

材质

前面提到实例化一个渲染可见的 3D 物体需要几何体信息和材质。几何体决定了物体的形状,材质就决定物体的颜色(漫反射颜色)、反射颜色、光泽度、透明度、折射率等性质。在物体参与渲染时,形状和材质共同决定了其最终渲染效果。

threejs 中有很多材质, 他们都继承于Material。有的材质不受光照影响,如MeshBasicMaterial仅显示一个固有颜色。有的受光照影响, 如MeshPhysicalMaterial基于物理原理计算光照。这些材质有各自不同的属性,同一种材质通过属性的修改能定制出更加丰富的效果。

单面和双面材质

我们已知所有的立体图形都是由很多面构成的,其实这些立体图形就是一个壳。所以材质一般默认只会渲染平面的一侧的面以保证性能,面的另一侧就是透明的(在游戏中角色落入物体内部便可见到这种效果)。在 threejs 中材质可以通过设置 side 属性来修改材质在平面的渲染情况。有以下的情况:

THREE.DoubleSide(双面) THREE.FrontSide (正面) THREE.BackSide (背面) // 实例化一个正面物理材质 const fSideMat = new THREE.MeshPhysicalMaterial({ color: 0xeeeeee, side: THREE.FrontSide, }); // 实例化一个背面物理材质 const bSideMat = new THREE.MeshPhysicalMaterial({ color: 0xeeeeee, side: THREE.BackSide, });

在某些特殊场景两面都会被观察到的情况可以启用双面材质,如游戏角色的片状衣服、树叶、草等这种非封闭的图形。角色如果只在某个密室内活动,那么可以在一个盒子上使用背面材质,从外部能看到盒子内部,但从盒子内部就只能看到材质的渲染效果。如下图,两个盒子内各放了一个红色小球,分别使用了正反面材质渲染。

图9 正面材质(左)和反面材质(右)

材质贴图

贴图是材质的一个属性,当材质设置了位图做为其贴图后,位图上的颜色会以一定的方式来映射到物体的各个面上,看起来就像将图片贴到了物体上一样。比起单纯给材质设置颜色,贴图的方式能给一个面的不同部分映射上不同的颜色,实现更丰富的效果。

UV映射贴图

图片是一个矩形,作为贴图有其自己坐标,水平方向是U,垂直方向是V。两个轴的坐标值只会在0-1。左下角顶点坐标是(0,0),右上角顶点坐标是(1,1)。即使贴图不是正方形,右上角坐标依然是(1,1)。U和V从0-1取值能取到图片上每一处的颜色。

在threejs中普通材质贴图映射模式默认是网格坐标与贴图坐标对齐。如下,长方形贴图左下角和右上角分别在正方形网格对齐。长方形就会被压缩在正方形里将图片颜色映射到平面上。

图10 uv贴图

在 threejs 中,贴图是Texture的实例。可以使用TextureLoader加载一张位图显示作为贴图,可以修改贴图各种参数,包括颜色,映射方式,横向重复排列数量,纵向重复排列数量等。

const texture = new THREE.TextureLoader().load("some/bitmap.png"); // 设置水平方向重复 texture.wrapS = THREE.RepeatWrapping; // 设置垂直方向重复 texture.wrapT = THREE.RepeatWrapping; // 设置水平垂直的重复数 texture.repeat.set(4, 4);

图11 React Logo贴图的不同参数效果

盒形和圆柱形投影

UV坐标这种贴图映射方式是threejs普通材质默认的方式,但threejs中还支持盒形和圆柱形投影。拿圆柱体举例,可以将投影理解为:

将圆柱侧面展开把贴图贴在展开的侧面 将圆柱还原(圆柱侧面每一个点都带颜色了) 将要被投影贴图的几何体放在圆柱内部 从圆柱侧面沿曲面法线向内部投影,将圆柱侧面的颜色投影在了内部的模型上 复杂贴图

复杂的贴图也是需要在专门的建模软件中操作,建模软件中可以对模型上的每一个面做精确贴图操作。一些建模软件中的uv展开功能就可以将模型拆成很多平面。将这些平面手动与贴图中的某个部分对齐,就能实现贴图的映射。

图12 3dsmax中展开一个花瓶模型,手动对齐贴图

贴图的其他用途

简单映射颜色是贴图最常用的场景,再某些材质中还支持贴图的其他用途。

alphaMap(透明度贴图) normalMap (法线贴图) ...

比如法线贴图并不会用来映射物体颜色,而常用于对某个平面制造凹凸的视觉效果。比如墙壁、地面、瓦片屋顶这些在建模时只是一个平面,但是完全可以用法线贴图制造凹凸效果,让建模难度降低,提升应用性能。

图13 法线贴图

灯光

灯光用于给场景照明,灯光和其他 3D 物体一样也可以放置在场景中并可设置其坐标,在需要的位置提供照明。某些材质在有灯光时才会有渲染效果,物理材质在没有灯光时将不可见,Phong 材质也会没有反射效果。灯光还有其参数能改变其照明效果,如灯光强度、灯光颜色等。

平行光DirectionalLight,模拟太阳光(仅方向设置生效) 点光源PointLight,模拟灯泡这样的人造光源(仅坐标设置生效) 聚光灯SpotLight,舞台聚光灯(可设置光源和目标点的坐标,以此改变灯光照射方向) //创建一个红色点光源 const redLight = new THREE.PointLight(0xff0000, 1); // 将点光源放在场景的中央上方 scene.add(redLight); redLight.position.set(0,3,0);

下图是Phong材质和物理材质在上下红蓝两个点光源照射下的渲染效果。

图14 Phong材质(左)和物理材质(右)受光照效果

摄像机

摄像算一种场景中特殊的物体,能设置其坐标和旋转,也能将摄像机作为其他物体子物体跟随物体移动。摄像机不出现在渲染结果中,但摄像机在渲染中至关重要。摄像机在渲染时作为渲染的一个输入。如同人的眼睛,摄像机在场景中的位置、角度和其自带的一些参数设置都会影响到最终渲染结果。threejs 摄像机中常使用的一般有两种:

透视摄像机(和人眼相似,观察物体远小近大) 正交摄像机 (没有远大近小的效果,类似平行光投影)

创建一个透视摄像机,从(0, 0, 2)坐标看向世界坐标原点。

const width = 800;//渲染的图像宽度 const height = 400;//渲染的图像高度 const pCamera = new THREE.PerspectiveCamera(75, width / height, 0.1); pCamera.position.set(0, 0, 2); pCamera.lookAt(0, 0, 0);

图15 透视摄像机观察立方体线框

创建一个正交摄像机,从(0, 0, 2)坐标看向世界坐标原点。

const width = 800; const height = 400; const oCamera = new THREE.OrthographicCamera( -4, 4, (height / width) * 4, (-height / width) * 4, 0, 100 ); oCamera.position.set(0, 0, 2); oCamera.lookAt(0, 0, 0);

图16 正交摄像机观察立方体线框

观察或渲染场景大多数时间使用的是透视摄像机。但在3D建模中正交摄像机也大有用处,建模师会在正交视图下做对齐模型这样的操作。

图17 3dsmax正交视图和透视图

渲染

渲染器是一个很复杂的东西,渲染器工作是将场景、物体、摄像机这些数据作为渲染输入参数,最终输出屏幕上每个像素的颜色。threejs 中可使用WebGLRenderer创建一个 webgl 渲染器。

const width = 800; const height = 400; const renderer = new THREE.WebGLRenderer(); renderer.setSize(width, height); //renderer.domElement是canvas,渲染器会将结果输出到这个canvas document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); //指定渲染器从哪个场景和摄像机渲染 renderer.render(scene, camera); 总结threejs使用要点

完成最基本的渲染至少需要以下三要素:

渲染器(WebGLRenderer) 一个摄像机(Camera) 一个空场景(Scene)

渲染可见物体需要向场景中放置物体,3D物体又包含以下两要素:

几何体(Geometry),决定物体形状。 材质(Material),决定物体颜色、折射等。


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有