☰
three.js RoundedBoxGeometry 详解:带圆角立方体几何体的构造参数、源码原理与实战用法
2026/11/3 2:30:29 网站建设 项目流程

three.js RoundedBoxGeometry 详解:带圆角立方体几何体的构造参数、源码原理与实战用法

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

RoundedBoxGeometry 是 three.js 在标准 BoxGeometry 基础上扩展出的“圆角立方体”几何体插件,让立方体的十二条边与八个角都呈现平滑过渡,广泛用于按钮、芯片、家具、UI 元素等需要柔和外观的三维物体。本文以 RoundedBoxGeometry API 文档 为主线,结合仓库内 addons 源码 与多个官方示例,完整讲解它的构造参数、UV/法线处理原理、JSON 序列化流程,并给出可直接复用的实战代码。

一、RoundedBoxGeometry 是什么

从继承链看,RoundedBoxGeometry 位于内置几何体链路的末端:

EventDispatcher → BufferGeometry → BoxGeometry → RoundedBoxGeometry

其中EventDispatcher → BufferGeometry是核心基础,BoxGeometry 提供了矩形立方体的面片组织方式,而 RoundedBoxGeometry 则在其基础上通过改写顶点位置,把锐利的边和角替换成由多段圆弧构成的平滑曲面。

三点关键特征值得先记住:

  1. 它不是核心库成员,而是 Addon(附加组件),必须显式 import,不会自动进入THREE命名空间;
  2. 面片数量显著多于同尺寸 BoxGeometry,因为它需要细分网格来逼近圆弧;
  3. 生成的是非索引(non-indexed)几何体,顶点数据按面展开存储。

二、安装与导入方式

由于 RoundedBoxGeometry 属于three/addons命名空间下的插件(仓库中对应 examples/jsm/geometries/RoundedBoxGeometry.js),需要在脚本中显式导入:

import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';

说明:three/addons/...路径在构建/打包阶段映射到examples/jsm/...目录下的同名模块。若使用原生 ES Module 方式,也可直接按仓库实际路径引用该文件。

导入后即可按普通几何体使用:

const geometry = new RoundedBoxGeometry(); const material = new THREE.MeshStandardMaterial( { color: 0x00ff00 } ); const cube = new THREE.Mesh( geometry, material ); scene.add( cube );

三、构造参数详解

构造函数签名如下(源码位置:examples/jsm/geometries/RoundedBoxGeometry.js#L65):

RoundedBoxGeometry( width : number, height : number, depth : number, segments : number, radius : number )

各参数含义与默认值:

参数含义默认值
width宽度,即平行于 X 轴的边长1
height高度,即平行于 Y 轴的边长1
depth深度,即平行于 Z 轴的边长1
segments构成圆角的细分段数2
radius圆角(边与角)的半径0.1

3.1 三个尺寸参数 width / height / depth

与 BoxGeometry 一致,width、height、depth分别对应沿 X、Y、Z 三个轴的边长,几何体默认居中于原点。例如 200 × 200 × 200 的圆角立方体可以写成:

const geometry = new RoundedBoxGeometry( 200, 200, 200, 10, 10 );

这一写法正是官方示例 examples/webgl_materials_texture_html.html 与 examples/webgpu_materials_texture_html.html 中用于展示 HTML 纹理映射的实际用法。

3.2 segments:圆角的平滑度

segments决定每个圆角用多少段折线去逼近圆弧。值越大,角越圆润、顶点数越多;值越小,角越“有棱角”。

需要特别留意源码中的一个关键细节:构造器内部并不直接使用segments,而是先换算:

// 总分段数取奇数,保证每个面上存在连接圆角的平面区域 const totalSegments = segments * 2 + 1;

之所以强制为奇数,是为了让每个面的中央保留一块与圆角相连的平面区域——偶数段会在几何上出现圆角错位或缺乏连接面的问题。理解这一点对选值很有帮助:官方示例普遍使用2~10之间的值,例如:

  • 物理沙盒示例 examples/physics_rapier_basic.html 用new RoundedBoxGeometry( 1, 1, 1, 2, 0.25 )生成中等平滑度的碰撞体;
  • 纹理示例 examples/webgl_loader_texture_lottie.html 用new RoundedBoxGeometry( 1, 1, 1, 7, 0.1 )追求较高的表面平滑度;
  • 木质质感 TSL 示例 examples/webgpu_tsl_wood.html 用new RoundedBoxGeometry( 0.125, 0.9, 0.9, 10, 0.02 ),在长宽方向都较细的体块上使用较高分段。

3.3 radius:圆角半径及其上限约束

radius是圆角在顶点处的弯曲半径。源码对它有自动夹取处理:

radius = Math.min( width / 2, height / 2, depth / 2, radius );

也就是说,圆角半径不会被允许超过最短边的一半。例如构造new RoundedBoxGeometry( 1, 1, 0.2, 4, 0.5 )时,实际生效的半径会被压缩为Math.min(0.5, 0.5, 0.1, 0.5) = 0.1。官方示例中利用这一特性,把薄板类物体做成带小圆角的“扁圆角块”,例如 AO 后处理示例 examples/webgpu_postprocessing_ao.html 中:

const seat = new THREE.Mesh( new RoundedBoxGeometry( 0.9, 0.25, 0.8, 4, 0.06 ), fabricMat ); const backrest = new THREE.Mesh( new RoundedBoxGeometry( 0.9, 0.6, 0.12, 4, 0.04 ), fabricMat );

如果希望获得“几乎像球体”的圆角立方体,可以把 radius 设到接近边长的一半。

四、源码级原理:圆角网格是怎么生成的

仅仅知道参数还不够,理解内部流程能帮你正确预判几何体形态与性能开销。RoundedBoxGeometry 的构造过程(examples/jsm/geometries/RoundedBoxGeometry.js#L65-L192)大致分为四个阶段。

4.1 阶段一:以细分单位立方体为起点

构造器先调用父类构造一个单位立方体,并在三条轴方向使用刚才算出的总分段数:

super( 1, 1, 1, totalSegments, totalSegments, totalSegments );

因此 RoundedBoxGeometry 的面片基数取决于totalSegments²级别,segments每增加 1,顶点规模都会明显上升。

源码在细分完成后有一个快速出口:如果totalSegments === 1(即segments = 0),会直接返回未做缩放的普通单位 BoxGeometry。换言之,segments 至少要取 1 才会真正生成圆角缩放后的网格。

4.2 阶段二:转为非索引几何体并逐个改写顶点

为了逐顶点精确控制圆角,几何体会被转成非索引形式,再交换数据:

const geometry2 = this.toNonIndexed(); this.index = null; this.attributes.position = geometry2.attributes.position; this.attributes.normal = geometry2.attributes.normal; this.attributes.uv = geometry2.attributes.uv;

随后对每个顶点执行核心变形。设

const box = new Vector3( width, height, depth ).divideScalar( 2 ).subScalar( radius );

即“内缩一个圆角半径后的盒心半长”,每个顶点的新位置由面法线方向推得:

positions[ i + 0 ] = box.x * Math.sign( position.x ) + normal.x * radius; positions[ i + 1 ] = box.y * Math.sign( position.y ) + normal.y * radius; positions[ i + 2 ] = box.z * Math.sign( position.z ) + normal.z * radius;

直观解释是:六个面的中心区域仍停留在原尺寸 ±size/2 处,而靠近边、角的顶点则沿归一化法线向外偏移 radius 的距离,从而在边与角处自然衔接成圆弧,形成平滑的圆角过渡。

4.3 阶段三:重写法线以保证平滑光照

圆角看起来圆润,很大程度上靠的是逐顶点平滑法线。源码对每个顶点先按半个网格单元向内收缩,再做归一化得到新法线:

normal.copy( position ); normal.x -= Math.sign( normal.x ) * halfSegmentSize; normal.y -= Math.sign( normal.y ) * halfSegmentSize; normal.z -= Math.sign( normal.z ) * halfSegmentSize; normal.normalize();

归一化后的法线同时被写入normals属性,因此圆角区域在标准光照下呈现平滑的明暗渐变,而不是多边形感的硬棱边。

4.4 阶段四:按圆弧占比重新计算 UV

这是 RoundedBoxGeometry 区别于“纯顶点变形”实现的关键一步。它没有沿用 BoxGeometry 的线性 UV,而是调用getUv()辅助函数,把每个面的 UV 区间按平面段与四分之一圆弧段的实际长度占比来分配:

const totArcLength = 2 * Math.PI * radius / 4; // 单个角的四分之一圆弧长 const centerLength = Math.max( sideLength - 2 * radius, 0 ); // 中央平面段长度

代码遍历六个面(right / left / top / bottom / front / back),各自指定两个投影轴与面朝向向量来生成 UV(examples/jsm/geometries/RoundedBoxGeometry.js#L136-L189)。这样做的收益是:贴图纹理在平面与圆角交界处不会出现明显拉伸或错位,这也是该几何体能直接配合棋盘格、Lottie 动画纹理、HTML 纹理等做高质量展示的原因。

五、属性与静态方法

5.1 .parameters : Object

RoundedBoxGeometry 在构造时把传入参数原样记录到parameters中(注意其中radius是经过最短边夹取后的实际值):

this.parameters = { width: width, height: height, depth: depth, segments: segments, radius: radius, };

这与父类 BoxGeometry 的parameters覆盖关系是:父类记录width/height/depth/widthSegments/heightSegments/depthSegments,RoundedBoxGeometry 则改记面向用户的segments与radius。

需要强调的是:parameters仅是构造参数的快照,实例化后再修改它不会改变几何体本身。若想调整尺寸或圆角,应重新new一个实例。

5.2 .fromJSON( data : Object ) : RoundedBoxGeometry

静态工厂方法,从序列化的 JSON 对象还原出一个新的 RoundedBoxGeometry 实例:

static fromJSON( data ) { return new RoundedBoxGeometry( data.width, data.height, data.depth, data.segments, data.radius ); }

它读取的正是width、height、depth、segments、radius五个字段。要理解data从哪来,需要看基类 src/core/BufferGeometry.js#L1230 的toJSON()实现:只要几何体带有parameters且未被transform过,序列化时就会把 parameters 中的键直接平铺写入 JSON 顶层并提前返回。因此一个 RoundedBoxGeometry 调用geometry.toJSON()会得到类似:

{ "metadata": { "version": 4.7, "type": "BufferGeometry", "generator": "BufferGeometry.toJSON" }, "uuid": "...", "type": "RoundedBoxGeometry", "name": "", "width": 1, "height": 1, "depth": 1, "segments": 2, "radius": 0.1 }

type为'RoundedBoxGeometry'(构造时通过this.type = 'RoundedBoxGeometry'设置),配合fromJSON即可完成 JSON 存档 / 还原的往返,适合在场景保存、资源管理器等场景中使用。

六、实战建议与注意事项

综合 API 文档与仓库源码,给出几条实操建议:

  1. 圆角半径与尺寸联动:当几何体某个维度很薄时(如桌面、座椅靠背),radius会被夹取到该边的一半以下,盲目设置过大的radius不会报错,但效果与预期不同,建议按最短边推算上限。
  2. segments 与性能的权衡:UI 或静态装饰用2~4即可;需要近距离特写或高光滑度材质(如 clearcoat、光泽金属)时可提升到7~10。由于几何体最终以非索引形式存储且每轴都按segments*2+1细分,顶点开销与segments近似平方关系,动态场景中应避免过大分段。
  3. 贴图映射可靠:得益于按圆弧长度占比的 UV 算法,该几何体可以直接放心使用纹理贴图与法线贴图,不必担心圆角处的纹理拉扯。
  4. 光照与着色:构造器已生成逐顶点平滑法线,配合MeshStandardMaterial、MeshPhysicalMaterial等 PBR 材质即可获得自然的圆角高光。

七、延伸阅读

  • API 文档原文:docs/pages/RoundedBoxGeometry.html.md
  • Addons 模块源码:examples/jsm/geometries/RoundedBoxGeometry.js
  • 父类实现:src/geometries/BoxGeometry.js 与 src/core/BufferGeometry.js
  • 官方示例:物理碰撞场景 examples/physics_rapier_basic.html、Lottie 纹理 examples/webgl_loader_texture_lottie.html、HTML 纹理 examples/webgl_materials_texture_html.html、AO 家具场景 examples/webgpu_postprocessing_ao.html、木质纹理 TSL 场景 examples/webgpu_tsl_wood.html

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询