jquave 发表于 2021-4-22 09:39

Unity 用C#如何在球体表面生成均匀分布的坐标点!求算法!?

Unity 用C#如何在球体表面生成均匀分布的坐标点!求算法!?

fwalker 发表于 2021-4-22 09:42

Smith等人的方法可以用来生成球面上接近均匀的点。


左图使用简单的球坐标,每层都有64点,但这样接近两极的点就过于密集。右图使用了优化后的球坐标,每层的点数是用函数产生,最多64点。

这个方法可以解决本问题。但顺带一提,这种方法原意是用于三维法矢量编码,可以用O(1)时间简单地把法矢量按哪些点分类:



如果需要比较随机,不那么工整的结果,可以在球面上进行 Poisson Disk Sampling,如。

Smith, Jason, G. Petrova, and Scott Schaefer. "Encoding normal vectors using optimized spherical coordinates." Computers & Graphics 36.5 (2012): 360-365. http://faculty.cs.tamu.edu/schaefer/research/normalCompression.pdf
Dunbar, Daniel, and Greg Humphreys. "A spatial data structure for fast Poisson-disk sample generation." ACM Transactions on Graphics (TOG). Vol. 25. No. 3. ACM, 2006.

Baste 发表于 2021-4-22 09:43

下方大神已经讲了原理了,我就一步到位直接上代码:
最后附上本人常用的一些小方法,都是平常写渲染时积累下来的(需要安装Unity.Mathmetic):
https://github.com/MaxwellGengYF/Unity-GPU-Driven-Pipeline/blob/master/Assets/MPipeline/Scripts/Utility/MatrixUtility.cs

ChuanXin 发表于 2021-4-22 09:52

由于球面的度规https://www.zhihu.com/equation?tex=g+%3D+%5Cleft%5B%5Cbegin%7Barray%7D%7Bcc%7D+1+%26+0+%5C%5C+0+%26+%5Csin%5E2+%5Ctheta%5Cend%7Barray%7D%5Cright%5D,可以生成两个 (0, 1) 的随机数 u, v 然后设置角度 https://www.zhihu.com/equation?tex=%5Ctheta%3D%5Ccos%5E%7B-1%7D%282u-1%29,https://www.zhihu.com/equation?tex=%5Cphi%3D2%5Cpi+v,即可得到球面上均匀分布(任意落入可测子集的概率正比于其面积)的随机点。
想要更「平均」的取点就上泊松盘。

XGundam05 发表于 2021-4-22 09:53

对于分布:https://www.zhihu.com/equation?tex=X%3D%28X_1%2CX_2%2C%5Cdots+X_n%29%2C+%5Cquad++i.i.d+%5Csim+N%280%2C1%29+%5C%5C++++
那么分布 https://www.zhihu.com/equation?tex=%5Cfrac%7BX%7D%7B%5Csqrt%7BX_1%5E2+%2B+X_2%5E2+%2B+%5Cdots+%2B+X_n%5E2%7D%7D+%5C%5C 在 https://www.zhihu.com/equation?tex=%5Cmathbb%7BR%7D+%5E%7Bn%7D 单位球面上均匀分布.
对于,用 https://www.zhihu.com/equation?tex=X%5Csim+N%280%2C1%29 每次采样三个数 https://www.zhihu.com/equation?tex=x%2Cy%2Cz ,那么:
https://www.zhihu.com/equation?tex=%5Cfrac%7B1%7D%7B%5Csqrt%7Bx%5E2%2By%5E2%2Bz%5E2%7D%7D%5Cleft%5B+++%5Cbegin%7Barray%7D%7Bccc%7D+++++++++++x%2C+%26+++++++++++y%2C%26+++++++++++z+++%5Cend%7Barray%7D+++%5Cright%5D%5ET+%5C%5C
在的球面 https://www.zhihu.com/equation?tex=S%5E2 上是均匀分布的.
用Box-Muller采样方法 https://www.zhihu.com/equation?tex=%5E%7B%5B1%5D%7D , 整个算法时间复杂度是线性的 https://www.zhihu.com/equation?tex=%5E%7B%5B2%5D%7D .
https://projecteuclid.org/euclid.aoms/1177706645 https://www.researchgate.net/publication/254365974_Three_Difierent_Algorithms_for_Generating_Uniformly_Distributed_Random_Points_on_the_N-Sphere

Zephus 发表于 2021-4-22 10:00

还有一种比较常用的方法是 细分正二十面体 。
先构造标准的正二十面体,然后迭代对每个面片进行细分,最后生成接近均匀的三角网格球体。过程如图(图片来自网络):

Ylisar 发表于 2021-4-22 10:06

可以使用 Centroidal Voronoi Tessellation (CVT),下面的链接提供了 Matlab 代码。
http://people.sc.fsu.edu/~jburkardt/m_src/sphere_cvt/sphere_cvt.html

链接中提供的结果如下:初始状态:

迭代后


结果均匀,对点的数量没有很强的限制。

super1 发表于 2021-4-22 10:16

开个小脑洞,用物理引擎,假如需要77个点,做一个大球体和77个小球,设置大球与小球之间有引力,小球之间有斥力,引力需要比斥力大的多,小球之间要完全非弹性碰撞,大球小球之间设置一点摩擦力,小球随机放在大球表面,小球开始自动滚动,过一会就基本均匀了。
关闭摩擦力,让小球微调,再开启摩擦力,让小球静止...如此反复多次。

xiangtingsl 发表于 2021-4-22 10:26

x=cos(α)sin(β)
y=sin(α)sin(β)
z=cos(α)
Z轴向上
α,β自定义分段取值

##############################################
代码:
float alpha = 0;
            float beta = 0;
            int stepCount = 20;
            float step = (float)Math.PI * 2 / stepCount;
            for (beta = 0; beta < Math.PI * 2; beta += step)
            {
                for (alpha = -(float)Math.PI +0.01f; alpha <= (float)Math.PI ; alpha += step)
                {
                  g_points.Add(new Vector3((float)(Math.Sin(alpha) * Math.Sin(beta)),
                        (float)(Math.Cos(alpha) * Math.Sin(beta)),(float)Math.Cos(beta)));
                }
            }

结果:


你要的是这种效果?ps:我才不会unity了

rustum 发表于 2021-4-22 10:26

using UnityEngine;
using System.Collections;


public class DistrbutedPointsOnSphere : MonoBehaviour {


public Camera camera;
/// <summary>
/// 父级 -- 球
/// </summary>
public Transform parent;
/// <summary>
/// 预设 -- 球
/// </summary>
public GameObject prefab;
public Transform[] kids;






// Use this for initialization
void Start () {
CalualteSphere();
}


// Update is called once per frame
void Update () {
if (!Input.GetMouseButton(0))
return;
float fMouseX = Input.GetAxis("Mouse X");
float fMouseY = Input.GetAxis("Mouse Y");
parent.Rotate(Vector3.up, -fMouseX * 2, Space.World);
parent.Rotate(Vector3.right, fMouseY * 2, Space.World);
for (int i = 0; i < kids.Length; i++)
kids.LookAt(camera.transform);
}
/// <summary>
/// 平均分成的等份
/// </summary>
int N = 200;
/// <summary>
/// 小球的半径
/// </summary>
float size =1f;


/// <summary>
/// 球体表面平均分割点
/// </summary>
void CalualteSphere()
{
float inc = Mathf.PI * (3.0f - Mathf.Sqrt(5.0f));
float off = 2.0f / N;//注意保持数值精度
kids = new Transform;
for(int i=0;i<N;i++)
{
float y = (float)i * off - 1.0f + (off / 2.0f);
float r = Mathf.Sqrt(1.0f - y * y);
float phi = i * inc;
Vector3 pos=new Vector3(Mathf.Cos(phi)*r*size,y*size,Mathf.Sin(phi)*r*size);
GameObject tempGo = Instantiate(prefab) as GameObject;
tempGo.transform.parent = parent;
tempGo.transform.localScale = new Vector3(1, 1, 1);
tempGo.transform.localPosition = pos;
tempGo.SetActive(true);
kids=tempGo.transform;
}
}


/// <summary>
/// 点击小球,将他移动到中心点
/// </summary>
/// <param name="pos">小球的自身位置</param>
void ClickLittleSphere(Vector3 pos)
{
Vector3 vec301 = pos - Vector3.zero;
Vector3 vec302 = camera.transform.position - parent.transform.position;
parent.rotation = Quaternion.FromToRotation(vec301, vec302);
}
}
页: [1] 2
查看完整版本: Unity 用C#如何在球体表面生成均匀分布的坐标点!求算法!?