我希望生成在球体周围“均匀分布”的点(而非随机分布),就像高尔夫球表面的凹点或足球上的六边形。是否有明确定义的算法可以实现这一点?
注意:我知道这些点并不是真正“均匀”地分布在球体上,但它们以某种方式分布,从任何直接注视任何点的方向看去,点的分布看起来是相同的 - 这就是我感兴趣的。
我希望生成在球体周围“均匀分布”的点(而非随机分布),就像高尔夫球表面的凹点或足球上的六边形。是否有明确定义的算法可以实现这一点?
注意:我知道这些点并不是真正“均匀”地分布在球体上,但它们以某种方式分布,从任何直接注视任何点的方向看去,点的分布看起来是相同的 - 这就是我感兴趣的。
从[0,1]中随机选择u,v。 2πu是经度。 asin(2v-1)是纬度。 只有两个随机变量,没有拒绝。
顺便说一下,我的链接收藏有一个新地址:http://bendwavy.org/sphere.htm。
我已经将其复制到http://cgafaq.info/wiki/Evenly_distributed_points_on_sphere。
将八面体进行细分并在之后对顶点进行归一化可以得到非常好的结果。点击这里了解更多详情。Paul Bourke有很多有趣的内容。
以下是我现在花五分钟写的伪C++代码:
/* Assume 'data' initially holds vertices for eight triangles (an octahedron) */
void GenerateSphere(float radius, std::vector<Vector3f>& data, int accum=10)
{
assert( !(data.size() % 3) );
std::vector<Vector3f> newData;
for(int i=0; i<data.size(); i+=3){
/* Tesselate each triangle into three new ones */
Vector3f centerPoint = (data[i] + data[i+1] + data[i+2]) / 3.0f;
/* triangle 1*/
newData.push_back(data[i+0]);
newData.push_back(data[i+1]);
newData.push_back(centerPoint);
/* triangle 2*/
newData.push_back(data[i+1]);
newData.push_back(data[i+2]);
newData.push_back(centerPoint);
/* triangle 3*/
newData.push_back(centerPoint);
newData.push_back(data[i+2]);
newData.push_back(data[i+0]);
}
data = newData;
if(!accum){
/* We're done. Normalize the vertices,
multiply by the radius and return. */
for(int i=0; i<data.size(); ++i){
data[i].normalize();
data[i] *= radius;
}
} else {
/* Decrease recursion counter and iterate again */
GenerateSphere(radius, data, accum-1);
}
return;
}
这段代码适用于由逆时针三角形构成的任何多面体,但八面体最好。
如果您只需要特定数量的顶点,则上述细分方法绝对是正确的选择。如果您需要任意指定数量的顶点,则我建议:
首先,在球体上随机均匀地分布点。我在http://elenzil.com/progs/randompoints中详细讨论了如何做到这一点。我相信我的方法至少与worlfram的方法一样有效。
其次,通过将点视为粒子系统来“放松”分布,其中每个粒子都会排斥其他粒子。困难在于确保系统不变得不稳定,并决定何时停止。我在这里有一个示例:http://elenzil.com/progs/separate不幸的是,这些都是在我没有将源代码包含在我的项目中的日子里完成的,因此那段代码已经丢失了。
我曾尝试过以下算法:
只要精度不破坏均匀性,这种方法就有效。生成的点形成类似于晶洞的图形。
您无需计算任何表面,因为每个新的三角形都不会比之前的任何一个更大。只需按FIFO顺序处理它们即可。
虽然本文讨论了在球体上随机选择点,但它也涉及到从均匀分布中绘制点,同时考虑球体的特性。我想这仍然是一个不错的阅读材料,适合您的问题:
这里有一个简单的方法。
从单位立方体[0,1]^3中随机采样。
测试是否包含在直径为1的球内。如果采样点不在包含在单位立方体中的直径为1的球内,则拒绝并返回步骤1。
通过将点从球心向外投影,将其归一化到球面上。
通常几次采样后就能成功。如果需要,您还可以拒绝接近球心的样本,以最小化舍入误差,并帮助使分布更接近均匀分布。