05.Three.js 基础之图元
05 Three.js 基础之图元
图元(Primitives) 介绍
所谓 图元 就是
有些文章或教程,包括
但是在本文中,我们依然先使用 图元 这个称呼。
请注意,内置的图元并不一定都是
3 维体,也可以是2 维的,例如 平面圆。
例如之前写的
虽然我们一直称呼为 立方体,但实际在
Three.js 中称呼其为 盒子(box)
在本文后面的一些文章中,也会将图元称呼为几何体。
以下内容更新于
2021.07.20
之前在写这篇文章的时候,还没有学习过图形学,所以对于一些名词的概念解释都是想当然,甚至是胡说八道,胡言乱语。
顶点、图元、片元、图像 他们之间的递进关系
顶点:就是在
请注意,这些顶点位置都是相对的,依次是 局部位置、全局位置、镜头位置等等。
在管线渲染流程中,顶点处理模块的作用就是负责将顶点进行坐标转换。
图元:由若干个顶点构成的一组数据,用于构建或描述某种 二维或三维物体。
图元 中的 “元” 字可以理解为 “原始”的原,也就是说使用最少的点来描述一个物体的空间信息。
只有
1 个顶点依然可以是 图元,它只能表示某一个 点。例如 自动驾驶中扫描周围环境得到的3D 点云数据就是由 一个一个小点 组成的。如果是
2 个顶点,则可以表示出是一个 线段,同时2 个点也可以表示出一个长方体。
2 个顶点信息就可以表述出1 个长方体? 没错的,你可以想象成 这2 个点分别是长方体的 斜对角线上的2 个点,例如在three.js 中 包装盒Box3 就只有2 个点的信息:坐标最大的点、坐标最小的点
3 个顶点,则可以表示出一个 三角形,同时3 个点也可以表示出一个圆。至于为什么
3 个顶点 可以表示出一个圆,你可以自己搜索或脑补。
请注意:图元依然为一堆顶点数据,而不是图像数据。
再次补充:假设一个物体有一部分不在显示范围之内,那么
webgl 会通过 裁切体( 由镜头视椎体决定的) 对物体进行裁切,只将需要渲染的部分进行渲染,而裁切得到的内容则会重新计算,得到一个新的图元。
关于图元的额外补充:
实际上在
-
几何图元:使用顶点、线段、三角形、曲线等等 用于描述物体 “几何轮廓” 。
几何图元可以进行空间转换,例如平移,旋转,缩放等操作
-
图像图元:图像图元又被称为 光栅图元。使用像素阵列 用于直观储存 “图片信息”。
通过描述就应该知道,实际上所谓的 图像图元就是材质中的纹理贴图。
图像图元不可进行空间转换
几何图元 经过变换、投影、光栅化后,到达片元操作环节的。
图像图元
最终在片元操作环节,几何图元
当然还需要其他操作,例如光线反射等
而实际中,我们通常不会使用 “图像图元” 这个名词,而是使用 “纹理”。
所以在本文或者一些常见的教程中
片元:包含图像颜色、位置、深度的信息数据。你可以把片元简单理解为 “未完全加工完成的图像数据”。
在
3D 图形管线渲染的流程中,经过裁切处理模块和图元组装模块之后,下一步经过光栅化处理模块,会将需要渲染的图元由一堆顶点数据转化为一堆图像数据。
请注意:片元已经不再是顶点数据,而是图像数据了,只不过这些图像数据是为完全加工完成,可以最终显示在屏幕上的图像数据。
图像:由 片元 经过片元处理模块,得到的最终图像数据。就是
片元数据经过处理,用来更新缓存帧 上的像素,最终 缓存帧 上的结果就是最终渲染出的图像。
请注意:图像是由一个个像素构成。
以上内容为 图形学 中的相关知识,但是在本文中讲解的 “
所以一定要理解清楚,本文讲解的 图元 和实际图形学中的图元 是有差异。
再次重申一遍:本文讲解的 图元 实际上是
JS 的类,帮助我们快速创建某些形状的 顶点数据。一组相关的顶点数据才是图形学中的图元。
以上内容更新于
2021.07.20
3D 模型的补充说明
内置的图元,都是一些基础的形状,相对简单,但也可以组合成相对复杂的
但对于绝大多数
- 在专业的
3D 软件 例如May 、Blender、C4D 中创建模型 - 将创建好的模型导出成模型文件,文件格式为
.obj 或.gltf Three.js 加载模型文件,然后开始后续操作
我们先不讨论如何导出或加载模型,那些会在后续操作中讲解。
此刻还是回归到默认的 图元 学习中。
图元的种类
图元汇总
图元种类 |
图元构造函数 |
---|---|
盒子 |
BoxBufferGeometry、BoxGeometry |
平面圆 |
CircleBufferGeometry、CircleGeometry |
锥形 |
ConeBufferGeometry、ConeGeometry |
圆柱 |
CylinderBufferGeometry、CylinderGeometry |
十二面体 |
DodecahedronBufferGeometry、DodecahedronGeometry |
受挤压的 |
ExtrudeBufferGeometry、ExtrudeGeometry |
二十面体 |
IcosahedronBufferGeometry、IcosahedronGeometry |
由线旋转形成的形状 |
LatheBufferGeometry、LatheGeometry |
八面体 |
OctahedronBufferGeometry、OctahedronGeometry |
由函数生成的形状 |
ParametricBufferGeometry、ParametriceGeometry |
PlaneBufferGeometry、PlaneGeometry | |
多面体 |
PolyhedronBufferGeometry、PolyhedronGeometry |
环形 |
RingBufferGeometry、RingGeometry |
ShapeBufferGeometry、ShapeGeometry | |
球体 |
SphereBufferGeometry、SphereGeometry |
四面体 |
TetrahedronBufferGeometry、TetrahedronGeometry |
TextBufferGeometry、TextGeometry | |
环形体 |
TorusBufferGeometry、TorusGeometry |
环形结 |
TorusKnotBufferGeometry、TorusKnotGeometry |
管道 |
TubeBufferGeometry、TubeGeometry |
几何体的所有边缘 |
EdgesGeometry |
线框图 |
WireframeGeometry |
一共有
上面表格中关于图元的中文名字,有些是我根据含义自己编的,我已经尽量靠近英文原意。
不同文章或教程可能对同一图元的称呼略微不同。
不要被上面那么多图元吓到,事实上他们并不复杂,并且多数情况下我们也用不到。
当需要用到了,只需要去查阅
以下内容更新于
2021.11.27
特别补充说明:内置的图元实际上也是变化多端的!
为什么这么说呢?
例如:圆柱
CylinderGeometry 官方文档:https://threejs.org/docs/index.html#api/zh/geometries/CylinderGeometry
CylinderGeometry(radiusTop : Float, radiusBottom : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float)
radiusTop — 圆柱的顶部半径,默认值是1。
radiusBottom — 圆柱的底部半径,默认值是1。
height — 圆柱的高度,默认值是1。
radialSegments — 圆柱侧面周围的分段数,默认为8。
heightSegments — 圆柱侧面沿着其高度的分段数,默认值为1。
openEnded — 一个Boolean值,指明该圆锥的底面是开放的还是封顶的。默认值为false,即其底面默认是封顶的。
thetaStart — 第一个分段的起始角度,默认为0。(three o'clock position)
thetaLength — 圆柱底面圆扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆柱。
请注意最后的
thetaStart( 默认值为0) thetaLength( 默认值为2*Pi)
也就是说,你不修改这
如果感兴趣,可以看一下我发布的这个项目,由数据生成
提醒:最好你在看完本系列教程后
( 不仅是本小节) ,再去看上面提到的pie-3d 。
通过上面对
以上内容更新于
2021.11.27
BufferGeometry 与Geometry 的区别
从上面的图元表格中不难发现,除了
虽然
差异之处 | BufferGeometry | Geometry |
---|---|---|
运算、渲染所消耗的性能 | 快 | 慢 |
支持 | 不支持, 需要 |
|
修改灵活度、可自定义程度 | 不高 | 高 |
添加新顶点 | 不支持 | 支持 |
简单来说就是:
BufferGeometry 可自定义地方比较少,但性能高Geometry 可自定义地方比较多,但性能低一些
所有的
Geometry 对象最终都会被Three.js 转化为BufferGeometry 对象,然后再进行渲染。
以下内容更新于
2021.11.27
上面关于
目前你接触到的都应该只有
一些心里话:
首先非常抱歉得说一句:一年前在写本系列文章时,我是一个对图形学一无所知,对
所以本系列教程绝对不是好的教程——假设
随着我对图形学、webgl、Three.js、
但是本教程对于那些完全小白,完全对
即使看完全部的本教程,那么最多你也仅仅算是学会了个皮毛,简单入门而已,真正复杂难的是 图形学 中的一些知识点,例如 向量,矩阵,齐次坐标,点乘,叉乘,球极坐标,当然最复杂的莫过于 自定义渲染器
关于
所以
图形学就是那种 从入门到放弃 的知识体系。
但是别灰心,我们实际上并不会真正需要那么深入高深的,学会
Three.js 可以做一些基础的 网页3D 还是会比一般前端要显得厉害很多。
BufferGeometry 的重要知识点:position、normal、uv
先普及个基础知识:在
3D 中Vector3 既可以表示一个 三维坐标,也可以表示一个三维方向。
一个完整的
上面提到的 点 准确说应该是
3 维 点坐标,对应的是Vector3 :https://threejs.org/docs/index.html#api/zh/math/Vector3
下面的知识实际上是针对 图形学 和
-
position:坐标
( 每个坐标就是一个vector3 ,由3 个数字组成) ,所有的坐标就是组成该BufferGeometry 的所有 点 的信息( 对于底层的BufferGeometry 而言3 维点的(x,y,z) 坐标是分开存储值的) 。这就是
Three.js 针对webgl 进行的封装,实际上我们平时更多时候都使用的是Vector3 ,而不是具体的3 个值。 -
normal:法线
( 每个法线就是一个vector3 ,由3 个数字组成) ,用于存储每个3D 坐标点的朝向,用于计算 反光。 -
uv:纹理映射坐标
( 每个uv 就是一个vector2 ,由2 个数字组成) ,用于存储每个3D 坐标点对应渲染纹理时对应的 位置点信息,用于计算 贴图。对于纹理而言,它都是 二维的平面,因此
uv 的值对应的是Vector2 ,由x,y 2 个坐标值组成,且每个值的取值范围都是0 - 1 。你可以简单把
0 - 1 理解成0% - 100% ,对应的是一个百分比的值。
通过上面的讲述,我们大致可以作出以下结论,如果我们自定义一个
对于初学者而言几乎不需要、也做不到 可以 自定义
BufferGeometry 这一步,我这里只是超前提一下。
-
假设这个
BufferGeometry 不需要考虑 反光 和 纹理贴图,那么它只需要拥有( 设置) positon 就可以了。this.setAttribute(‘position’, new BufferAttribute(this._vertices, 3))
-
假设这个
BufferGeometry 需要考虑反光,但不需要考虑纹理贴图,那么它需要设置postion 和normal 。this.setAttribute(‘position’, new BufferAttribute(this._vertices, 3))
this.setAttribute(’normal’, new BufferAttribute(this._normals, 3))
-
假设这个
BufferGeometry 需要考虑反光和纹理贴图,那么它的postion 、normal、uv 都需要设置。this.setAttribute(‘position’, new BufferAttribute(this._vertices, 3))
this.setAttribute(’normal’, new BufferAttribute(this._normals, 3))
this.setAttribute(‘uv’, new BufferAttribute(this._uvs, 2))
-
特别强调,上面提到的
position 是一个BufferGeomerty 所有点信息的集合,它并不是Mesh( 网格,3D 物体) 的 位置信息。
如果你理解不了我说的这段话,完全没有关系,忽略这段我补充的知识点,我也是学习
忽略我上面的这段话,继续本教程后面的学习吧。
以上内容更新于
2021.11.27
图元理论上的知识就先讲到这里,在下一节中,会编写一些图元示例。