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

Three.js中如何移动绕中心旋转的粒子星系且不破坏原有外观效果

问题原因

你的星系效果异常的核心问题出在顶点着色器的执行逻辑顺序上:
你将星系点的旋转计算放在了顶点乘以modelMatrix之后,modelMatrix本身就包含了galaxyPoints的position、rotation、scale属性的变换信息。当你修改galaxyPoints.position时,所有顶点经过modelMatrix转换后的坐标就已经叠加上了全局偏移,此时你计算atan(modelPosition.x, modelPosition.z)得到的旋转角度、length(modelPosition.xz)得到的点到旋转中心的距离,都是相对于世界原点的,而非星系自身的中心,自然会导致所有点的旋转轨迹错乱,整个星系外观破碎。
另外你原有代码还存在两处笔误,也会导致显示异常:

  • 几何体属性赋值错误:aScale属性错误绑定了颜色数组,应该绑定你生成的galaxyScales数组
  • 顶点着色器旋转逻辑笔误:旋转后错误将Z轴坐标赋值给了Y轴,导致星系原本的平面结构被破坏

正确实现方式

方案1:调整着色器逻辑(最推荐)

将旋转、随机偏移的计算放到乘以modelMatrix之前,先在星系的本地空间完成所有形变,再应用全局的位移/旋转/缩放,修改完后你可以直接修改galaxyPoints的position属性移动星系,完全不会破坏原有效果。
修改后的顶点着色器代码如下:

uniform float uTime;
uniform float uSize;

attribute vec3 aRandomness;
attribute float aScale;

varying vec3 vColor;

void main()
{
    // 先处理星系本地空间的坐标变换
    vec3 localPosition = position;

    // 本地空间内计算旋转
    float angle = atan(localPosition.x, localPosition.z);
    float distanceToCenter = length(localPosition.xz);
    float angleOffset = (1.0 / distanceToCenter) * uTime * 3.0;
    angle += angleOffset;
    localPosition.x = cos(angle) * distanceToCenter;
    localPosition.z = sin(angle) * distanceToCenter;

    // 添加本地随机偏移
    localPosition.xyz += aRandomness;

    // 再应用全局变换矩阵
    vec4 modelPosition = modelMatrix * vec4(localPosition, 1.0);
    vec4 viewPosition = viewMatrix * modelPosition;
    vec4 projectedPosition = projectionMatrix * viewPosition;
    gl_Position = projectedPosition;

    // 计算点大小
    gl_PointSize = uSize * aScale;
    gl_PointSize *= (1.0 / - viewPosition.z);

    // 传递颜色给片元着色器
    vColor = color;
}

同时修正几何体aScale属性的赋值代码:

// 把原来错误的赋值替换成下面这行
galaxyGeometry.setAttribute('aScale', new THREE.BufferAttribute(galaxyScales, 1))

之后你就可以直接按常规方式移动星系:

// 沿X轴移动2单位
galaxyPoints.position.x = 2
// 沿Y轴移动2单位
galaxyPoints.position.y = 2
// 沿Z轴移动2单位
galaxyPoints.position.z = 2

方案2:使用父容器包裹

如果你不想修改着色器代码,可以给星系套一个THREE.Group父容器,移动父容器即可,父容器的变换不会影响星系内部的坐标计算:

// 初始化时创建父容器
const galaxyGroup = new THREE.Group()
scene.add(galaxyGroup)

// generateGalaxy方法中,把galaxyPoints添加到父容器而非直接添加到scene
// 替换 scene.add(galaxyPoints) 为下面的代码
galaxyGroup.add(galaxyPoints)

// 之后直接移动父容器即可
galaxyGroup.position.x = 2

内容的提问来源于stack exchange,提问作者Orestis Tserkanos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 21:15:04