[英]Random Rotation on a 3D sphere given an angle
這個問題介於計算機圖形、概率和編程之間,但由於我是用 C# 為 Unity 項目編寫代碼,因此我決定將其發布在這里。 不合適請見諒。
我需要解決這個問題:給定一個 3d 球體上某個位置的物體,並給定一個度數范圍,球體上的采樣點在給定范圍內均勻分布。
例如: 左圖:立方體代表球體的中心,綠色球體是起始位置。 我想在一定程度上均勻覆蓋圓的所有表面,例如圍繞綠色球體從 -90 到 90 度。 我的方法(右圖)不起作用,因為它對靠近起始位置的點進行了過采樣。
我的采樣器:
Vector3 getRandomEulerAngles(float min, float max)
{
float degree = Random.Range(min, max);
return degree * Vector3.Normalize(new Vector3(Random.Range(min, max), Random.Range(min, max), Random.Range(min, max)));
}
為了覆蓋球體的上半部分,我會調用getRandomEulerAngles(-90, 90)
。
任何的想法?
我們可以為此使用均勻球體采樣。 給定兩個隨機變量u
和v
(均勻分布),我們可以計算球體(也是均勻分布)上的隨機點(p, q, r)
:
float azimuth = v * 2.0 * PI;
float cosDistFromZenith = 1.0 - u;
float sinDistFromZenith = sqrt(1.0 - cosDistFromZenith * cosDistFromZenith);
(p, q, r) = (cos(azimuth) * sinDistFromZenith, sin(azimuth) * sinDistFromZenith, cosDistFromZenith);
如果我們將我們的參考方向(您的物體位置)置於天頂位置,我們需要從[0, 1]
采樣v
以獲得圍繞物體的所有方向,而u
在[cos(minDistance), cos(maxDistance)]
,其中minDistance
和maxDistance
是您想要允許的對象的角度距離。 90°
或Pi/2
距離會給你一個半球。 180°
或Pi
距離將為您提供完整的球體。
現在我們可以在天頂位置對物體周圍的區域進行采樣,我們還需要考慮其他物體的位置。 讓物體位於(ox, oy, oz)
,這是一個單位向量,描述了從球心開始的方向。
然后我們建立一個局部坐標系:
rAxis = (ox, oy, oz)
pAxis = if |ox| < 0.9 : (1, 0, 0)
else : (0, 1, 0)
qAxis = normalize(cross(rAxis, pAxis))
pAxis = cross(qAxis, rAxis)
最后,我們可以得到球面上的隨機點 (x, y, z):
(x, y, z) = p * pAxis + q * qAxis + r * rAxis
嘗試這個:
public class Sphere : MonoBehaviour
{
public float Radius = 10f;
public float Angle = 90f;
private void Start()
{
for (int i = 0; i < 10000; i++)
{
var randomPosition = GetRandomPosition(Angle, Radius);
Debug.DrawLine(transform.position, randomPosition, Color.green, 100f);
}
}
private Vector3 GetRandomPosition(float angle, float radius)
{
var rotationX = Quaternion.AngleAxis(Random.Range(-angle, angle), transform.right);
var rotationZ = Quaternion.AngleAxis(Random.Range(-angle, angle), transform.forward);
var position = rotationZ * rotationX * transform.up * radius + transform.position;
return position;
}
}
改編自 Nice Schertler,這是我正在使用的代碼
Vector3 GetRandomAroundSphere(float angleA, float angleB, Vector3 aroundPosition)
{
Assert.IsTrue(angleA >= 0 && angleB >= 0 && angleA <= 180 && angleB <= 180, "Both angles should be[0, 180]");
var v = Random.Range(0F, 1F);
var a = Mathf.Cos(Mathf.Deg2Rad * angleA);
var b = Mathf.Cos(Mathf.Deg2Rad * angleB);
float azimuth = v * 2.0F * UnityEngine.Mathf.PI;
float cosDistFromZenith = Random.Range(Mathf.Min(a, b), Mathf.Max(a, b));
float sinDistFromZenith = UnityEngine.Mathf.Sqrt(1.0F - cosDistFromZenith * cosDistFromZenith);
Vector3 pqr = new Vector3(UnityEngine.Mathf.Cos(azimuth) * sinDistFromZenith, UnityEngine.Mathf.Sin(azimuth) * sinDistFromZenith, cosDistFromZenith);
Vector3 rAxis = aroundPosition; // Vector3.up when around zenith
Vector3 pAxis = UnityEngine.Mathf.Abs(rAxis[0]) < 0.9 ? new Vector3(1F, 0F, 0F) : new Vector3(0F, 1F, 0F);
Vector3 qAxis = Vector3.Normalize(Vector3.Cross(rAxis, pAxis));
pAxis = Vector3.Cross(qAxis, rAxis);
Vector3 position = pqr[0] * pAxis + pqr[1] * qAxis + pqr[2] * rAxis;
return position;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.