diff --git "a/03 \347\274\226\345\206\231HelloThreejs.md" "b/03 \347\274\226\345\206\231HelloThreejs.md" index 9992898..3a5fd8d 100644 --- "a/03 \347\274\226\345\206\231HelloThreejs.md" +++ "b/03 \347\274\226\345\206\231HelloThreejs.md" @@ -65,7 +65,7 @@ const scene = new Scene() 1. 渲染器 > 本文示例采用的渲染器是 WebGLRenderer 2. 透视镜头 > 本文示例采用的是 PerspectiveCamera -3. 场景 > Scen +3. 场景 > Scene **场景可见元素:** @@ -76,7 +76,7 @@ const scene = new Scene() **补充说明:** -你应该发现,除了 场景(Scen)、网格(Mesh) 之外,其他的元素我都注明 “本文示例采用的是...”。 +你应该发现,除了 场景(Scene)、网格(Mesh) 之外,其他的元素我都注明 “本文示例采用的是...”。 因为无论渲染器,还是几何体,以及其他元素,Three.js 都内置了非常多不同种类的元素构造函数,这个会在以后学习中逐渐详细说明举例。 diff --git "a/04 \346\267\273\345\212\240\344\270\200\344\272\233\350\207\252\351\200\202\345\272\224.md" "b/04 \346\267\273\345\212\240\344\270\200\344\272\233\350\207\252\351\200\202\345\272\224.md" index 9038822..7c768eb 100644 --- "a/04 \346\267\273\345\212\240\344\270\200\344\272\233\350\207\252\351\200\202\345\272\224.md" +++ "b/04 \346\267\273\345\212\240\344\270\200\344\272\233\350\207\252\351\200\202\345\272\224.md" @@ -95,7 +95,7 @@ const HelloThreejs: React.FC = () => { #### 目前存在的问题: -可以观察到 canvase 是被硬生生由原本的 高150、像素 宽 300 像素给硬生生拉伸成 100%。 +可以观察到 canvas 是被硬生生由原本的 高150、像素 宽 300 像素给硬生生拉伸成 100%。 所以立方体出现了 扭曲、模糊、锯齿。 @@ -107,14 +107,14 @@ const HelloThreejs: React.FC = () => { #### 第4处修改: -修改 src/components/hello-threejs/index.stx 中 render 函数的代码,让镜头宽高比跟随着 canvase 宽高比,确保立方体不变形。 +修改 src/components/hello-threejs/index.stx 中 render 函数的代码,让镜头宽高比跟随着 canvas 宽高比,确保立方体不变形。 ```diff ... const render = (time: number) => { time = time * 0.001 -+ const canvas = renderer.domElement //获取 canvase ++ const canvas = renderer.domElement //获取 canvas + camera.aspect = canvas.clientWidth / canvas.clientHeight //设置镜头宽高比 + camera.updateProjectionMatrix() //通知镜头更新视椎(视野) @@ -138,7 +138,7 @@ const HelloThreejs: React.FC = () => { const render = (time: number) => { time = time * 0.001 - const canvas = renderer.domElement //获取 canvase + const canvas = renderer.domElement //获取 canvas camera.aspect = canvas.clientWidth / canvas.clientHeight //设置镜头宽高比 camera.updateProjectionMatrix() //通知镜头更新视椎(视野) @@ -531,4 +531,4 @@ https://github.com/mdn/translated-content/pull/817 那么接下来,会系统学习一下 Three.js 的一些基础理论。 -**大楼究竟能改多高,取决于地基有多深,加油!** \ No newline at end of file +**大楼究竟能改多高,取决于地基有多深,加油!** diff --git "a/05 Three.js\345\237\272\347\241\200\344\271\213\345\233\276\345\205\203.md" "b/05 Three.js\345\237\272\347\241\200\344\271\213\345\233\276\345\205\203.md" index b61a0ec..608c0f9 100644 --- "a/05 Three.js\345\237\272\347\241\200\344\271\213\345\233\276\345\205\203.md" +++ "b/05 Three.js\345\237\272\347\241\200\344\271\213\345\233\276\345\205\203.md" @@ -209,6 +209,49 @@ Primitive 的复数即为 Primitives。 +
+ +> 以下内容更新于 2021.11.27 + +**特别补充说明:内置的图元实际上也是变化多端的!** + +为什么这么说呢? + +例如:圆柱(Cylinder),字面上它是用于创建圆柱体的,但是实际上认真阅读官方文档你会发现是这样描述它的构造函数的 + +> 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,这使其成为一个完整的圆柱。 +``` + +请注意最后的 2 个参数: + +1. thetaStart(默认值为0) +2. thetaLength(默认值为2*Pi) + +也就是说,你不修改这 2 个默认值,**那么默认创建出的是一个完整的圆柱体**,但是假设你修改了这 2 个值,比如 将 thetaLength 修改成 0.3*Pi (54°),那么最终将创建出一个 夹角为 54° 的**扇形**(体)。 + +如果感兴趣,可以看一下我发布的这个项目,由数据生成3D饼图:https://github.com/puxiao/pie-3d + +> 提醒:最好你在看完本系列教程后(不仅是本小节),再去看上面提到的 pie-3d 。 + +通过上面对 CylinderGeometry 的描述,我们可以知道 Three.js 默认自带的图元实际上是可以产生很多变化的,得到的不一定仅仅是图元的 "字面" 物体。 + +> 以上内容更新于 2021.11.27 + + + +
+ ### BufferGeometry 与 Geometry 的区别 从上面的图元表格中不难发现,除了 Edges、WireframeGeometry 以外,其他图元的构造函数都是成对出现的。 @@ -231,4 +274,88 @@ Primitive 的复数即为 Primitives。 +
+ +> 以下内容更新于 2021.11.27 + +**上面关于 BufferGeometry 和 Geometry 的区别这段话已经过时了**,因为在较新的 Three.js 版本中已经将 Geometry 从核心类中移除。 + +目前你接触到的都应该只有 BufferGeometry。 + +#### 一些心里话: + +首先非常抱歉得说一句:一年前在写本系列文章时,我是一个对图形学一无所知,对 Three.js 好奇但又非常小白的人,我是一边学习一边写下本系列文章的。 + +**所以本系列教程绝对不是好的教程——假设 Three.js 是一座大山的话,而我是站在山脚下向你讲述上山道路的那个人,但我自己也未曾上过这座山。** + +随着我对图形学、webgl、Three.js、Canvas 的一些认知提升,我深深觉得想要写出好教程,一定要站在更高的维度,拥有更高的视野才可以更好向别人指明方向,写出好教程。 + +但是本教程对于那些完全小白,完全对 Three.js 一无所知的人,多少还是有些帮助的(尽管我指明的道路并不是最佳道路),**很感谢那些 Star 本教程的人**。 + +即使看完全部的本教程,那么最多你也仅仅算是学会了个皮毛,简单入门而已,真正复杂难的是 图形学 中的一些知识点,例如 向量,矩阵,齐次坐标,点乘,叉乘,球极坐标,当然最复杂的莫过于 自定义渲染器(shader)。 + +关于3D 技术栈,虽然不够严谨,但是大体上可以这样表述:**图形学(CG) > OpenGL > OpenGL ES 2.0 > WebGL > Three.js** + +所以 Three.js 仅仅是 web 3D 最基础,表层的知识技术栈,想要深入学习,你会发现这是一条几乎不到头的道路,学秃。 + +> 图形学就是那种 从入门到放弃 的知识体系。 +> +> 但是别灰心,我们实际上并不会真正需要那么深入高深的,学会 Three.js 可以做一些基础的 网页 3D 还是会比一般前端要显得厉害很多。 + +
+ +#### BufferGeometry的重要知识点:position、normal、uv + +> 先普及个基础知识:在 3D 中 Vector3 既可以表示一个 三维坐标,也可以表示一个三维方向。 + +一个完整的 BufferGeometry 是由若干个 点(Vector3) 构成的: + +> 上面提到的 点 准确说应该是 3维 点坐标,对应的是 Vector3 :https://threejs.org/docs/index.html#api/zh/math/Vector3 + +下面的知识实际上是针对 图形学 和 OpenGL 的。 + +1. position:坐标(每个坐标就是一个 vector3,由 3 个数字组成),所有的坐标就是组成该 BufferGeometry 的所有 点 的信息(对于底层的 BufferGeometry 而言 3维点的 (x,y,z) 坐标是分开存储值的)。 + + > 这就是 Three.js 针对 webgl 进行的封装,实际上我们平时更多时候都使用的是 Vector3,而不是具体的 3 个值。 + +2. normal:法线(每个法线就是一个 vector3,由 3 个数字组成),用于存储每个 3D 坐标点的朝向,用于计算 反光。 + +3. uv:纹理映射坐标(每个uv就是一个 vector2,由 2 个数字组成),用于存储每个 3D 坐标点对应渲染纹理时对应的 位置点信息,用于计算 贴图。 + + > 对于纹理而言,它都是 二维的平面,因此 uv 的值对应的是 Vector2,由 x,y 2 个坐标值组成,且每个值的取值范围都是 0 - 1。 + > + > 你可以简单把 0 - 1 理解成 0% - 100%,对应的是一个百分比的值。 + +通过上面的讲述,我们大致可以作出以下结论,如果我们自定义一个 BufferGeometry,那么: + +> 对于初学者而言几乎不需要、也做不到 可以 自定义 BufferGeometry 这一步,我这里只是超前提一下。 + +1. 假设这个 BufferGeometry 不需要考虑 反光 和 纹理贴图,那么它只需要拥有(设置) positon 就可以了。 + + > this.setAttribute('position', new BufferAttribute(this._vertices, 3)) + +2. 假设这个 BufferGeometry 需要考虑反光,但不需要考虑纹理贴图,那么它需要设置 postion 和 normal。 + + > this.setAttribute('position', new BufferAttribute(this._vertices, 3)) + > + > this.setAttribute('normal', new BufferAttribute(this._normals, 3)) + +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)) + +4. **特别强调,上面提到的 position 是一个 BufferGeomerty 所有点信息的集合,它并不是 Mesh(网格,3D物体) 的 位置信息。** + +如果你理解不了我说的这段话,完全没有关系,忽略这段我补充的知识点,我也是学习 Three.js 快 1 年后才明白的。对于现在的你而言不理解是正常的。 + +忽略我上面的这段话,继续本教程后面的学习吧。 + +> 以上内容更新于 2021.11.27 + + + 图元理论上的知识就先讲到这里,在下一节中,会编写一些图元示例。 diff --git "a/06 \345\233\276\345\205\203\347\273\203\344\271\240\347\244\272\344\276\213.md" "b/06 \345\233\276\345\205\203\347\273\203\344\271\240\347\244\272\344\276\213.md" index 5f29df9..a9bc94a 100644 --- "a/06 \345\233\276\345\205\203\347\273\203\344\271\240\347\244\272\344\276\213.md" +++ "b/06 \345\233\276\345\205\203\347\273\203\344\271\240\347\244\272\344\276\213.md" @@ -112,7 +112,7 @@ const HelloPrimitives = () => { scene.add(light0) const light1 = new Three.DirectionalLight(0xFFFFFF, 1) - light0.position.set(1, -2, -4) + light1.position.set(1, -2, -4) scene.add(light1) //获得各个 solid 类型的图元实例,并添加到 solidPrimitivesArr 中 diff --git "a/09 Three.js\345\237\272\347\241\200\344\271\213\346\235\220\350\264\250.md" "b/09 Three.js\345\237\272\347\241\200\344\271\213\346\235\220\350\264\250.md" index c25f5e4..35d00a5 100644 --- "a/09 Three.js\345\237\272\347\241\200\344\271\213\346\235\220\350\264\250.md" +++ "b/09 Three.js\345\237\272\347\241\200\344\271\213\346\235\220\350\264\250.md" @@ -221,10 +221,59 @@ flatShading 的值为 布尔值,该值指 是否启用 平面着色 模式。 由于颜色是按片元着色的,得到的结果比 逐顶着色(Gouraud Shading) 要更加细腻,尤其是用于光亮表面效果更加真实。 -Phong 并不是传统的英文单词,而是 科学家 Bui Tuong Phong 的名字。 +Phong 并不是传统的英文单词,而是 生活在美国的越南籍科学家 Bui Tuong Phong (裴祥风) 的名字。 所以 Phong Shading 又被称为 冯氏着色。 + +
+ +> 以下内容更新于 2022.02.02 + +在图形学中有一个被应用非常广泛的简单光照模型:冯氏光照模型 + +> 注:这里的 简单 是指计算量非常小,但却可以模拟出简单的高光和漫反射。 + + +
+ +**冯氏光照模型、冯氏着色法 简介:** + +**裴祥风** (1942-1975),出生于越南,1973 年在美国 尤他大学 取得博士学位,并发明了冯氏光照模型和冯氏着色法,被广大 CG 界采用。 + + +冯氏光照模型主要有 3 个分量组成: + +1. 环境光照(Ambient Lighting):物体几乎永远不会是完全黑暗的,环境光照一般是一个常量。 + +2. 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响,越是正对着光源的地方越亮。 + + > 通过计算物体平面某个点的法向量与该点与光源的单位向量进行乘积,得到该点的亮度。 + > + > 当这两个向量相互重叠时该点最亮(亮度值为1),当这两个向量成九十度则最暗(亮度值为0) + > + > 向量、法线、乘积(也称内积) 这些都是 线性代数 中的词语,属于图形学中需要掌握的基础知识。如果学会了基础的 Three.js 后一定要去学习图形学,否则以后也做不出什么好的应用。 + +3. 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。 + +最终物体呈现的样子就是以上 3 种光照结果直接叠加后的样子。 + + + +
+ +冯氏着色法(Phong Shading):每个片元(fragment)或者每个点计算一次光照,点的法向量是通过顶点的法向量插值得到的。冯氏着色更加接近真实,当然计算开销也大。 + +与冯氏着色法相对的有:平面着色法(Flat Shading)、高洛德着色法(Gouraud Shading) + + + +> 以上内容更新于 2022.02.02 + + +
+ + > Phong 光照反射模型 也被称为 冯氏反射模型 **特别补充:** diff --git "a/12 Three.js\345\237\272\347\241\200\344\271\213\351\225\234\345\244\264.md" "b/12 Three.js\345\237\272\347\241\200\344\271\213\351\225\234\345\244\264.md" index e1a28f7..2c94190 100644 --- "a/12 Three.js\345\237\272\347\241\200\344\271\213\351\225\234\345\244\264.md" +++ "b/12 Three.js\345\237\272\347\241\200\344\271\213\351\225\234\345\244\264.md" @@ -518,7 +518,7 @@ OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number **从实际的角度来看,一定要注意以下几点:** -1. left 的值不能大于 right,同理 top 的值不能大于 bottom +1. left 的值不能大于 right,同理 bottom 的值不能大于 top。 如果没有按照这个约定,例如 bottom 大于 top 相当于颠倒了相机。 2. near 设置越小,投影的映像越大 3. left 与 right 之间的距离、top 与 bottom 之间的距离的比例一定要和 canvas 比例相同,否则会导致投影的物体形状变形 @@ -534,7 +534,7 @@ OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number -### 假设我们在 HelloCamer 示例中使用 OrthographicCamera +### 假设我们在 HelloCamera 示例中使用 OrthographicCamera **需要修改的地方为:** diff --git a/README.md b/README.md index 3aeafc1..0fd6daa 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ 从今天 2020年11月27日 开始学习和探索 Three.js 。 + +
+
+ wechat.jpg + +
+ +
> 以下内容更新于 2021.04.16 @@ -417,4 +425,3 @@ https://www.bilibili.com/video/BV1X7411F744
**大家都是 Three.js 小白新手,一起加油!** - diff --git "a/Three.js\345\256\236\347\224\250\347\237\245\350\257\206\347\202\271\347\254\224\350\256\260.md" "b/Three.js\345\256\236\347\224\250\347\237\245\350\257\206\347\202\271\347\254\224\350\256\260.md" new file mode 100644 index 0000000..2658cc2 --- /dev/null +++ "b/Three.js\345\256\236\347\224\250\347\237\245\350\257\206\347\202\271\347\254\224\350\256\260.md" @@ -0,0 +1,264 @@ +# Three.js实用知识点笔记 + +从今天开始,在本文中记录实际 Three.js 开发过程中所遇到的知识点。 + + + +
+ +**关于示例代码的一般约定说明:** + +1. 举例的时候,很多都是伪代码 +2. 为了保证代码简洁,所以在实用某些类时没有添加 `Three.` 前缀 +3. 绝大多数时候都使用箭头函数 +4. 使用 Xxx 泛指同一类型,例如 XxxCamer 泛指各类相机 +5. 由于我们讲解的是 Three.js,所以全部使用的是 右手坐标系 + + + +
+ +#### 01、每一个Object3D对象都只能有一个父级 + +这里说的 Object3D 实际上包括所有继承于 Object3D 的子类,例如 Mesh、Camera、Group 等 + +举例说明: + +``` +const mesh = new Mesh(geometry,material) + +const sceneA = new Scene() +const sceneB = new Scene() + +sceneA.add(mesh) +sceneB.add(mesh) +``` + +由于 mesh 只能有一个父类,所以当 sceneB 也执行 .add(mesh) 后,sceneA.children 中会自动删除掉 mesh。 + + + +
+ +#### 02、克隆或复制 Mesh 不会在内存中真正复制出一份 顶点(geometry)和材质(material),它们使用的是引用,而不是复制 + +Object3D 拥有 .clone() 和 .copy() 两个方法,Mesh 继承于 Object3D,所以也拥有这两个方法。 + +在 Mesh 类中扩展了 .copy() 方法,但是针对 Mesh 内部的属性 顶点和材质,实用的是引用而不是真正内存中的复制。 + +``` +const meshB = meshA.clone() +``` + +上述代码中新复制得到的 meshB 仅仅复制了 meshA 的一些变换相关的属性,例如 matrix 等,但是对于占内存大头的 顶点和材质 这两项实用的是引用。 + +也就是说此时 meshA 和 meshB 它们共用了一份 geometry 和 material。 + +> 不用担心因为多复制了几份 mesh 而增加很多内存。 + + + +
+ +#### 03、添加场景(scene)或其他Object3D渲染之前和渲染之后的回调函数 + +场景 scene 继承于 Object3D,而 Object3D 可以配置 2 个渲染之前或之后的回调函数: + +``` +scene.onBeforeRender = () => { ...} +scnet.onAfterRende = () => { ... } +``` + +使用场景举例:假设我们希望不渲染场景上的某一类元素,那么我们可以在 renderer.render() 之前通过上面 2 个回调函数进行设置 + +``` +scene.onBeforeRender = () => { + scene.children.forEach(item => { + if(item.type === 'Points'){ + item.visible = false + } + }) +} + +scene.onAfterRender = () => { + scene.children.forEach(item => item.visible = true) +} + +renderer.render(scene,camera) +``` + + + +
+ +#### 04、通过 .layers 控制物体是否被渲染 + +在 Three.js 中 .layers 对应的是 Layers 这个类,Three.js 规定 Layers 级别的值取值范围为 0 - 32。 + +> 你可以把 layers 翻译成 “级别”,也可以称呼为 “层级” + +任何继承于 Object3D 的类,例如 相机、物体 等都具有 .layers 属性。 + +它们的 .layers 默认级别都为 0。 + +不能通过直接给 .layers 赋值的方式修改级别,而是应该通过 .set(value) 这种形式。 + +``` +mymesh.layers.set(1) +camera.layers.set(1) +``` + + + +
+ +对于相机而言,它只能渲染出同一级别的物体元素。 + +``` +const meshA = new Mesh(...) +//meshA.layers.set(0) //默认就是 0 + +const meshB = new Mesh(...) +meshB.layers.set(1) + +const scene = new Scene() +scene.add(meshA) +scene.add(meshB) + +const cameraA = new XxxCamera() +//cameraA.layers.set(0) //默认就是 0 + +const cameraB = new XxxCamera() +cameraB.layers.set(1) +``` + +在上面代码中: + +1. 我们按照默认的形式添加了 meshA、cameraA,它们默认层级为 0 +2. 手动修改了 meshB、cameraB 的 .layers 层级为 1 + +
+ +那么当执行下面的代码: + +``` +renderer.render(scene, cameraA) +renderer.render(scene. cameraB) +``` + +1. cameraA 只会渲染出场景中同一级别的 meshA +2. cameraB 只会渲染出场景中同一级别的 meshB + + + +
+ +也可以选择随时修改 meshA 的 .layers 值,这样 cameraB 就可以渲染到它了。 + +``` +meshA.layers.set(1) +renderer.render(secen, cameraB) +``` + + + +
+ +换句话说,假设我们希望控制是否渲染场景中某些元素,那么有 2 种途径: + +1. 设置其 .visible 的值来决定是否渲染 +2. 设置其 .layers 的值来决定只被同一层级的相机渲染 + + + +
+ +#### 05、手工修改Object3D实例的.matrix时切记要设置 .matrixAutoUpdate=false + +默认情况下 Object3D 实例的 .matrixAutoUpdate 的值为 true,也就是说当通过 .applyMatrix4()、.applyQuaternion() 等修改实例的变换时,默认会自动更新其他所有相关属性值,例如 position、quaternion、scale、rotation。 + +但是,如果直接通过修改 Object3D 实例的 .matrix 值时,生效的前提是: + +1. 先把 .matrixAutoUpdate 设置为 false +2. 不去调用 .updateMatrix() + +``` +const mesh = new Mesh(...) + +mesh.matrixAutoUpdate = false +mesh.matrix.copy(otherMatrix) +``` + + + +
+ +但是上面的代码存在另外一个问题:尽管 .matrix 值更新了,可是 mesh 的其他属性值 例如 .position,.quaternion,scale,rotation 却没有自动更新。 + +解决方式很简单,可以通过 Matrix 的 .decompose() 方法优雅更新它们。 + +举例说明:假设现在有 meshA、meshB 两个对象,需要将 meshB 的各种变换属性值设置成和 meshA 完全相同 + +``` +meshB.matrixAutoUpdate = false +meshB.matrix.copy(meshA.matrix) +meshB.matrix.decompose(meshB.position, meshB.quaternion, meshB.scale) +``` + +> 当修改 meshB.quaternion 值后会自动修改 meshB.rotation 的值 + + + +
+ +#### 06、绘制三角形的顶点顺序决定了该三角形是正面(顺时针)还是反面(逆时针) + +一个三角形有 3 个顶点,假定为 a、b、c,那么: + +1. 假定 a b c 连接顺序为 逆时针,那么最终形成的三角形为 正面 +2. 假定 a b c 连接顺序为 顺时针,那么最终形成的三角形为 反面(背面) + +另外一种判定形式是:右手握住沿着两个顶点添加顺序的连接线,此时大拇指指示方向即为正面 + + + +
+ +#### 07、保持外观和位置的前提下,将立方体的顶点坐标 "归一化" + +这里说的立方体是指基于 BoxGeometry 而创建的立方体。 + +这里说的 顶点坐标 “归一化” 是指将立方体顶点坐标修改成 1x1x1 规格的立方体顶点坐标。 + +这里说的 保持外观和位置 是指通过修改其 变换矩阵 .matrix 来实现。 + +实现思路: + +1. 凡是基于 BoxGeometry 的立方体,其顶点坐标信息都是统一规范的,尽管其值可能不同 +2. 所以我们就根据其值来确定这个立方体与 1x1x1 立方体的 宽、高、深 比例 +3. 将这个缩放比例应用到立方体本身的矩阵中即可 +4. 同时将这个立方体的顶点信息修改成 1x1x1 立方体的顶点信息 + +``` +const boxGeometryNormalize = (box) => { + const originX = box.geometry.attributes.position.array[0] + const originY = box.geometry.attributes.position.array[1] + const originZ = box.geometry.attributes.position.array[2] + + const scaleX = originX / 0.5 + const scaleY = originY / 0.5 + const scaleZ = originZ / 0.5 + + box.geometry = new BoxGeometry() + box.matrixAutoUpdate = false + box.matrix.makeScale(scaleX, scaleY, scaleZ) + box.matrix.decompose(box.position, box.quaternion, box.scale) +} +``` + + + +
+ + +