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
相关产品推荐
相关产品推荐

