3D:从极坐标创建球体时避免在极点处挤压
3D: avoid pinching at poles when creating sphere from polar coordinates
我正在使用维基百科的 spherical coordinate system article 创建一个由 Three.js 中的粒子组成的球体。基于这篇文章,我创建了一个小的 Polarizer
class 接受极坐标 setPolar(rho, theta, phi)
并且它 returns 对应的 x, y, z
这是 setPolar()
函数:
// Rho: radius
// theta θ: polar angle on Y axis
// phi φ: azimuthal angle on Z axis
Polarizer.prototype.setPolar = function(rho, theta, phi){
// Limit values to zero
this.rho = Math.max(0, rho);
this.theta = Math.max(0, theta);
this.phi = Math.max(0, phi);
// Calculate x,y,z
this.x = this.rho * Math.sin(this.theta) * Math.sin(this.phi);
this.y = this.rho * Math.cos(this.theta);
this.z = this.rho * Math.sin(this.theta) * Math.cos(this.phi);
return this;
}
我用它来定位我的粒子,如下所示:
var tempPolarizer = new Polarizer();
for(var i = 0; i < geometry.vertices.length; i++){
tempPolarizer.setPolar(
50, // Radius of 50
Math.random() * Math.PI, // Theta ranges from 0 - PI
Math.random() * 2 * Math.PI // Phi ranges from 0 - 2PI
);
// Set new vertex positions
geometry.vertices[i].set(
tempPolarizer.x,
tempPolarizer.y,
tempPolarizer.z
);
}
它工作得很好,除了我得到高粒子密度,或者在两极 "pinching":
我不知道如何避免这种情况发生。我想过将加权随机数传递给纬度,但我希望在没有经度的情况下为粒子设置动画,同时也会减慢速度并在两极聚集。
是否有不同的公式可以生成一个球体,其中两极的重量不那么大?我应该改用四元数吗?
为了避免两极的高密度,我不得不将 theta(纬度)着陆的可能性降低到接近 0 和 PI。我输入的
Math.random() * Math.PI,
因为 theta 给出了所有值的相同可能性(橙色)。
Math.acos((Math.random() * 2) - 1)
对输出进行完美加权,使 0 和 PI 沿着球体表面(黄色)的可能性降低
现在连两极都分不清了!
用于随机均匀采样
在单位立方体中使用随机点,将其作为矢量处理并将其长度设置为球体的半径。例如在 C++ 中是这样的:
x = 2.0*Random()-1.0;
y = 2.0*Random()-1.0;
z = 2.0*Random()-1.0;
m=r/sqrt(x*x+y*y+z*z);
x*=m;
y*=m;
z*=m;
其中 <0.0,1.0>
中的随机数 return。有关详细信息,请参阅:
- Procedural generation of stars with skybox
均匀非随机抽样
查看相关 QAs:
- Sphere triangulation by mesh subdivision
- Make a sphere with equidistant vertices
我正在使用维基百科的 spherical coordinate system article 创建一个由 Three.js 中的粒子组成的球体。基于这篇文章,我创建了一个小的 Polarizer
class 接受极坐标 setPolar(rho, theta, phi)
并且它 returns 对应的 x, y, z
这是 setPolar()
函数:
// Rho: radius
// theta θ: polar angle on Y axis
// phi φ: azimuthal angle on Z axis
Polarizer.prototype.setPolar = function(rho, theta, phi){
// Limit values to zero
this.rho = Math.max(0, rho);
this.theta = Math.max(0, theta);
this.phi = Math.max(0, phi);
// Calculate x,y,z
this.x = this.rho * Math.sin(this.theta) * Math.sin(this.phi);
this.y = this.rho * Math.cos(this.theta);
this.z = this.rho * Math.sin(this.theta) * Math.cos(this.phi);
return this;
}
我用它来定位我的粒子,如下所示:
var tempPolarizer = new Polarizer();
for(var i = 0; i < geometry.vertices.length; i++){
tempPolarizer.setPolar(
50, // Radius of 50
Math.random() * Math.PI, // Theta ranges from 0 - PI
Math.random() * 2 * Math.PI // Phi ranges from 0 - 2PI
);
// Set new vertex positions
geometry.vertices[i].set(
tempPolarizer.x,
tempPolarizer.y,
tempPolarizer.z
);
}
它工作得很好,除了我得到高粒子密度,或者在两极 "pinching":
我不知道如何避免这种情况发生。我想过将加权随机数传递给纬度,但我希望在没有经度的情况下为粒子设置动画,同时也会减慢速度并在两极聚集。
是否有不同的公式可以生成一个球体,其中两极的重量不那么大?我应该改用四元数吗?
为了避免两极的高密度,我不得不将 theta(纬度)着陆的可能性降低到接近 0 和 PI。我输入的
Math.random() * Math.PI,
因为 theta 给出了所有值的相同可能性(橙色)。
Math.acos((Math.random() * 2) - 1)
对输出进行完美加权,使 0 和 PI 沿着球体表面(黄色)的可能性降低
现在连两极都分不清了!
用于随机均匀采样
在单位立方体中使用随机点,将其作为矢量处理并将其长度设置为球体的半径。例如在 C++ 中是这样的:
x = 2.0*Random()-1.0; y = 2.0*Random()-1.0; z = 2.0*Random()-1.0; m=r/sqrt(x*x+y*y+z*z); x*=m; y*=m; z*=m;
其中
<0.0,1.0>
中的随机数 return。有关详细信息,请参阅:- Procedural generation of stars with skybox
均匀非随机抽样
查看相关 QAs:
- Sphere triangulation by mesh subdivision
- Make a sphere with equidistant vertices