繁体   English   中英

给定角度的 3D 球体上的随机旋转

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

任何的想法?

我们可以为此使用均匀球体采样。 给定两个随机变量uv (均匀分布),我们可以计算球体(也是均匀分布)上的随机点(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)] ,其中minDistancemaxDistance是您想要允许的对象的角度距离。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM