Search code examples
pythonrandomblender

Why is this randomly generated spherical point cloud not uniformly distributed?


I'm trying to simulate radiation emitting from a point source. To do this, given the coordinates of a source and the desired length of emitted rays, I randomly generate a direction vector in spherical coordinates, convert it to cartesian, and return the correct end point. However, when I run this, and visualize the resulting point cloud (consisting of all the randomly generated end points) in Blender, I see that it's more densely populated at the "poles" of the sphere. I'd like the points to be uniformly distributed along the sphere. How can I achieve this?

enter image description here

The random generation function:

def getRadiationEmissionLineSeg(p, t):
    if(p.size == 4):
        #polar angle spans [0, pi] from +Z axis to -Z axis
        #azimuthal angle spans [0, 2*pi] orthogonal to the zenith (in the XY plane)
        theta = math.pi * random.random()
        phi = 2 * math.pi * random.random()

        #use r = 1 to get a unit direction vector
        v = sphericalToCartesian(1, theta, phi)

        #parametric vector form: vec = p + tv
        #p = point that lies on vector (origin point in case of a ray)
        #t = parameter (-inf, inf) for lines, [0, inf) for rays
        #v = direction vector (must be normalized)
        return p + t * v

The spherical coordinates -> cartesian conversion function:

def sphericalToCartesian(r, theta, phi):

    x = r * math.sin(theta) * math.cos(phi)
    y = r * math.sin(theta) * math.sin(phi)
    z = r * math.cos(theta)

    return npy.array([x, y, z, 0])

Solution

  • When you transform points by spherical coordinates and angle theta approaches pi, the circle which is an image of [0,2pi]x{theta} gets smaller and smaller. Since theta is uniformly distributed, there will be more points near poles. It could be seen on image of grid.

    sphere

    If you want to generate uniformly distributed points on sphere, you can use the fact that if you cut a sphere with two parallel planes, the area of the strip of spherical surface between the planes depends only on the distance between the planes. Hence, you can get a uniform distribution on the sphere using two uniformly distributed random variables:

    • z coordinate between -r and r,
    • an angle theta between [0, 2pi) corresponding to a longitude.

    Then you can easily calculate x and y coordiantes.

    Example code:

    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    
    r = 1
    n = 1000
    
    z = np.random.random(n)*2*r - r
    phi = np.random.random(n)*2*np.pi
    
    x = np.sqrt(1 - z**2)*np.cos(phi)
    y = np.sqrt(1 - z**2)*np.sin(phi)
    
    fig = plt.figure(figsize=(8, 8))
    ax = plt.axes(projection='3d')
    ax.scatter(x, y, z)
    plt.show()
    

    Results for n=100,250,1000:

    Results