简体   繁体   English

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

[英]Random Rotation on a 3D sphere given an angle

This question is in between computer graphic, probability, and programming, but since I am coding it for an Unity project in C# I decided to post it here.这个问题介于计算机图形、概率和编程之间,但由于我是用 C# 为 Unity 项目编写代码,因此我决定将其发布在这里。 Sorry if not appropriate.不合适请见谅。

I need to solve this problem: given a object on a 3d sphere at a certain position, and given a range of degrees, sample points on the sphere uniformly within the given range.我需要解决这个问题:给定一个 3d 球体上某个位置的物体,并给定一个度数范围,球体上的采样点在给定范围内均匀分布。

For example: Left picture: the cube represents the center of the sphere, the green sphere is the starting position.例如: 左图:立方体代表球体的中心,绿色球体是起始位置。 I want to uniformly cover all surface of the circle within a certain degree, for example from -90 to 90 degrees around the green sphere.我想在一定程度上均匀覆盖圆的所有表面,例如围绕绿色球体从 -90 到 90 度。 My approach (right picture) doesn't work as it over-samples points that are close to the starting position.我的方法(右图)不起作用,因为它对靠近起始位置的点进行了过采样。

在此处输入图片说明

My sampler:我的采样器:


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)));
}

and for covering the top half of the sphere I would call getRandomEulerAngles(-90, 90) .为了覆盖球体的上半部分,我会调用getRandomEulerAngles(-90, 90)

Any idea?任何的想法?

We can use a uniform sphere sampling for that.我们可以为此使用均匀球体采样。 Given two random variables u and v (uniformly distributed), we can calculate a random point (p, q, r) on the sphere (also uniformly distributed) with:给定两个随机变量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);

If we put our reference direction (your object location) into zenithal position, we need to sample v from [0, 1] to get all directions around the object and u in [cos(minDistance), cos(maxDistance)] , where minDistance and maxDistance are the angle distances from the object you want to allow.如果我们将我们的参考方向(您的物体位置)置于天顶位置,我们需要从[0, 1]采样v以获得围绕物体的所有方向,而u[cos(minDistance), cos(maxDistance)] ,其中minDistancemaxDistance是您想要允许的对象的角度距离。 A distance of 90° or Pi/2 will give you a hemisphere. 90°Pi/2距离会给你一个半球。 A distance of 180° or Pi will give you the full sphere. 180°Pi距离将为您提供完整的球体。

Now that we can sample the region around the object in zenithal position, we need to account for other object locations as well.现在我们可以在天顶位置对物体周围的区域进行采样,我们还需要考虑其他物体的位置。 Let the object be positioned at (ox, oy, oz) , which is a unit vector describing the direction from the sphere center.让物体位于(ox, oy, oz) ,这是一个单位向量,描述了从球心开始的方向。

We then build a local coordinate system:然后我们建立一个局部坐标系:

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)

And finally, we can get our random point (x, y, z) on the sphere surface:最后,我们可以得到球面上的随机点 (x, y, z):

(x, y, z) = p * pAxis + q * qAxis + r * rAxis

Try this:尝试这个:

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;
    }
}

Adapted from Nice Schertler, this is the code I am using改编自 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