[英]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.