找回密码
 立即注册
查看: 1447|回复: 13

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

[复制链接]
发表于 2021-4-22 09:39 | 显示全部楼层 |阅读模式
Unity 用C#如何在球体表面生成均匀分布的坐标点!求算法!?
发表于 2021-4-22 09:42 | 显示全部楼层
Smith等人的方法[1]可以用来生成球面上接近均匀的点。


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

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



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

[1] 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
[2] 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.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-4-22 09:52 | 显示全部楼层
由于球面的度规,可以生成两个 (0, 1) 的随机数 u, v 然后设置角度 ,即可得到球面上均匀分布(任意落入可测子集的概率正比于其面积)的随机点。
想要更「平均」的取点就上泊松盘。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-4-22 09:53 | 显示全部楼层
对于分布:
那么分布 单位球面上均匀分布.
对于  ,用 每次采样三个数 ,那么:

在  的球面 上是均匀分布的.
用Box-Muller采样方法 , 整个算法时间复杂度是线性的 .
    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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-4-22 10:00 | 显示全部楼层
还有一种比较常用的方法是 细分正二十面体
先构造标准的正二十面体,然后迭代对每个面片进行细分,最后生成接近均匀的三角网格球体。过程如图(图片来自网络):

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-4-22 10:06 | 显示全部楼层
可以使用 Centroidal Voronoi Tessellation (CVT),下面的链接提供了 Matlab 代码。
http://people.sc.fsu.edu/~jburkardt/m_src/sphere_cvt/sphere_cvt.html

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

迭代后


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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-4-22 10:16 | 显示全部楼层
开个小脑洞,用物理引擎,假如需要77个点,做一个大球体和77个小球,设置大球与小球之间有引力,小球之间有斥力,引力需要比斥力大的多,小球之间要完全非弹性碰撞,大球小球之间设置一点摩擦力,小球随机放在大球表面,小球开始自动滚动,过一会就基本均匀了。
关闭摩擦力,让小球微调,再开启摩擦力,让小球静止...如此反复多次。
发表于 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了

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 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[N];
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);
}
}
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-22 13:37 , Processed in 0.096993 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表