You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js:如何缩放圆但不缩放其描边?

解决方案:运行时缩放TubeGeometry生成的描边圆

嘿,这个问题我太有共鸣了——直接碰Tube的顶点确实会一脸懵,因为那些顶点是路径细分+厚度偏移后的结果,和原路径完全不是一回事。给你两个实用的方向,看哪种更贴合你的需求:

方案1:复用原路径曲线,高效更新几何体

如果你觉得“重新创建TubeGeometry”不算完全不可接受,其实可以通过复用原路径的曲线对象,把开销降到最低:

首先创建的时候,保存好原始的圆形曲线引用,而不是只存Path:

// 保存核心曲线对象,方便后续修改
const circleCurve = new THREE.CircleCurve(0, 0, 5, 0, Math.PI * 2);
const path = new THREE.Path(circleCurve.getPoints(64));
// 描边厚度设为你需要的值
const tubeGeo = new THREE.TubeGeometry(path, 64, 0.3, 8, true);
const strokeCircle = new THREE.Mesh(tubeGeo, new THREE.MeshBasicMaterial({
    color: 0xff0000,
    transparent: true,
    opacity: 0.8,
    side: THREE.DoubleSide
}));
scene.add(strokeCircle);

运行时缩放时,只需要修改曲线的半径,然后重新生成几何体(注意释放旧资源):

// 比如要缩放到半径10
circleCurve.radius = 10;
// 更新路径的曲线
path.curves[0] = circleCurve;
// 释放旧几何体资源,避免内存泄漏
strokeCircle.geometry.dispose();
// 用更新后的路径生成新几何体
strokeCircle.geometry = new THREE.TubeGeometry(path, 64, 0.3, 8, true);

这个方法代码简洁,对于简单的圆形来说,性能开销几乎可以忽略,而且完全保留描边厚度的可控性。

方案2:直接操作顶点,完全避免重建几何体

如果你的场景对性能要求极高,不想碰几何体重建,可以直接计算并修改Tube的顶点。核心思路是:把每个顶点拆分为「路径上的基准点」和「描边偏移向量」,只缩放基准点,保留偏移向量的长度(这样描边厚度不变):

先在初始化时保存原始半径和描边厚度:

const initialRadius = 5;
const strokeThickness = 0.3;
// 初始化TubeMesh的代码和上面一样...

运行时缩放的代码:

const targetRadius = 10;
const scaleRatio = targetRadius / initialRadius;
const positionAttr = strokeCircle.geometry.attributes.position;
const vertices = positionAttr.array;

for (let i = 0; i < vertices.length; i += 3) {
    const x = vertices[i];
    const y = vertices[i + 1];
    
    // 计算当前顶点到圆心的距离(原始路径上的基准点距离)
    const originalDist = Math.sqrt(x * x + y * y);
    // 得到基准点的方向向量
    const dirX = x / originalDist;
    const dirY = y / originalDist;
    
    // 计算原始基准点的位置(减去偏移向量)
    const originalBaseX = dirX * initialRadius;
    const originalBaseY = dirY * initialRadius;
    // 计算描边的偏移向量(保持长度不变)
    const offsetX = x - originalBaseX;
    const offsetY = y - originalBaseY;
    
    // 缩放后的基准点 + 原始偏移 = 新顶点位置
    vertices[i] = dirX * targetRadius + offsetX;
    vertices[i + 1] = dirY * targetRadius + offsetY;
}

// 标记顶点属性需要更新,Three.js才会重新渲染
positionAttr.needsUpdate = true;
// 如果用了受法线影响的材质,还要重新计算法线
strokeCircle.geometry.computeVertexNormals();

这个方法完全不用重建几何体,直接修改底层顶点数据,性能最优,而且完美保持描边厚度不变。

避坑提醒

别用mesh.scale来缩放——这会把描边厚度也一起缩放,破坏你原本“描边厚度可控”的需求。

内容的提问来源于stack exchange,提问作者Filip Petrovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:28:52