# Generating Equidistant Points on a Sphere

In this post, I’ll be covering how to generate points in 3D space that are equally spaced on the unit sphere. The method is based on a spiral walk of the spherical surface in angular increments equal to the golden angle. At the end of this post, I’ll also briefly go over some potential uses in your games, and what I’m personally using it for.

### 1. The Golden Ratio

Let’s first illustrate what the golden ratio is. Say you have a line segment `a`

, and another shorter segment `b`

.

When the sum of both lengths, divided by the long segment, is equal to the long segment divided by the short segment then this is the golden ratio.

This is represented by the greek symbol Phi *φ*.

We can then find the positive solution to this equation, which will give us the value of φ.

The golden angle is the angle subtended by the small arc `b`

. This is approximately 2.39 radians.

We will come back to this later on in the post, but for now, let’s start coding.

### 2. Generating points on a circle

Before we jump straight into implementing this algorithm in 3D space, let’s first consider generating the points on a 2D circle.

The approach is based on the fact that if you stand in the center of this circle, and then turn a golden ratio of whole turns, and then place a point in that direction, you will construct a spiral pattern with points that line up nicely to give us a uniform distribution.

Firstly, I’ll create a new object:

```
local Object = {}
Object.__index = Object
return Object
```

And inside of the constructor, I’ll define a table to add the `Vector3`

points to, so we can access them later.

```
function Object.new()
local self = setmetatable({}, Object)
self._points = {}
return self
end
```

Next, I’ll create a new method called `Object:GeneratePoints(n)`

, where `n`

is the number of points we want to generate on the circle.

```
function Object:GeneratePoints(n)
end
```

We can express our golden ratio, φ, as `1 + math.sqrt(5) / 2`

, which came from our positive solution to the equation. Let’s put that into a variable.

```
local goldenRatio = 1 + math.sqrt(5) / 4
```

We now want to calculate how much we want to increment the angle for every “turn”, where we want to plot the points with even-area spacing around the circle. We’re going to use a method called *sampling the inverse cumulative distribution function*. This is used as a basic method for generating sample numbers at random from any probability distribution given its cumulative distribution function.

Further reading for those interested:

This gives us:

```
local angleIncrement = math.pi * 2 * math.random()
```

As we want to uniformly sample this function, as opposed to randomly sampling it, we’ll replace `math.random()`

with our golden ratio to get equal-area spacing and avoid “lines” of points upon generation.

```
local angleIncrement = math.pi * 2 * goldenRatio
```

Lastly, we want to create a loop which will generate each point according to `n`

, the number of points we want.

First I’ll create the loop:

```
for i = 0, n do
end
```

Then, inside the loop I’m going to define a distance, which will go from 0 to 1 over the course of the loop.

```
for i = 0, n do
local distance = i / n
end
```

I’ll then define an angle, where each iteration of the loop it will turn some fraction of the circle. In our case, we will use the `angleIncrement`

variable we have just defined above, and multiply that with the current increment of the loop `i`

.

```
for i = 0, n do
local distance = i / n
local angle = angleIncrement * i
end
```

Next, we want to calculate the position of the point on the circle.

We can use basic trigonometry, where we can calculate the `X`

and `Y`

positions using a right-angled triangle, where our hypotenuse is equal to the distance.

Therefore:

```
for i = 0, n do
local distance = i / n
local angle = angleIncrement * i
local x = distance * math.cos(angle)
local y = distance * math.sin(angle)
end
```

And lastly, we want to render this point somehow into the game world. For this example, I’m going to create a new method which will spawn a part into the workspace with a `Vector3`

position of `(x, y, 0)`

. The `z`

value of the vector doesn’t matter at the moment, since we’re only creating the points in two dimensions, so for now I’ll set the value to `0`

.

Here’s what I’m going to use:

```
function Object:PlotPoints(x, y)
local point = Instance.new("Part")
point.Anchored = true
point.Material = "Neon"
point.CanCollide = false
point.Color = Color3.fromRGB(69, 99, 115)
point.Transparency = 0
point.Size = Vector3.new(0.25, 0.25, 0.25)
point.Parent = game.Workspace
point.Position = Vector3.new(x, y, 0) -- Here is where I plug in our point values
end
```

Let’s call `PlotPoints`

inside of our `GeneratePoints`

method.

```
for i = 0, n do
local distance = i / n
local angle = angleIncrement * i
local x = distance * math.cos(angle)
local y = distance * math.sin(angle)
self:PlotPoints(x, y)
end
```

Great! Let’s create a new object with the constructor and call the `GeneratePoints`

method with 100 points to start off with. Here is the result:

That’s a great start, however, there seems to be some issues. The values returned are way too small, so all the parts are bunched up together. To fix this, I’m going to create a new variable inside of our `GeneratePoints`

method called `multiplier`

, which I’ll multiply with the `x`

and `y`

values we calculate.

```
function Object:GeneratePoints(n)
local goldenRatio = 1 + math.sqrt(5)
local angleIncrement = math.pi * 2 * goldenRatio
local multiplier = 10 -- 10 seems to be a reasonable number
for i = 0, n do
local distance = i / n
local angle = angleIncrement * i
-- For both x and y, I'll multplier the value by our multiplier
local x = distance * math.cos(angle) * multiplier
local y = distance * math.sin(angle) * multiplier
self:PlotPoints(x, y)
end
end
```

At the moment, you can see that the points follow this spiral pattern, which comes from out golden ratio. In order to space the points out a bit more, I’m going to divide the golden ratio by a factor of 4.

```
local goldenRatio = 1 + math.sqrt(5) / 4
```

On a circle, this result looks a little underwhelming, but as we convert this into a sphere, our equidistant points will fall into place nicely.

Our final result should look something like this:

### 3. Generating points on a sphere

In order to create the points on a sphere, we’re going to need to make some adjustments to our existing code.

We will use our method of *sampling the inverse cumulative distribution function* again, however, this time we’ll be using spherical coordinates as opposed to polar coordinates. As we’re on a unit sphere, the radial coordinate doesn’t matter. 0 ≤ φ ≤ π is the longitude (the lines comes down from the poles), and 0 ≤ θ ≤ 2π is the lattitude.

Let’s assign the result to a new variable called `incline`

. Our incline is the angle between the zenith direction and the line segment from our origin to the point.

```
local incline = math.acos(1 - 2 * distance)
```

Our angle that we calculate will stay the same:

```
local angle = angleIncrement * i
```

However, as we are talking about spherical coordinates and not polar coordinates, the correct term is the *azimuth*, which is the angle measured from the azimuth reference direction to the projection of the line segment created from the origin to the point.

Let’s appropriately rename the variable:

```
local azimuth = angleIncrement * i
```

As with before, we want to calculate the position of the point on the sphere.

Firstly, we’ll find the spherical coordinates in terms of r, z, φ and ϱ.

The above image is an easier representation of our point, which comes from this diagram:

Now, we’ll convert from the spherical coordinates for our point into Cartesian form, which will give us the values in `x, y and z`

.

Substituting the first set of equations into the second set:

Writing this in code:

```
local x = math.sin(incline) * math.cos(azimuth) * multiplier
local y = math.sin(incline) * math.sin(azimuth) * multiplier
local z = math.cos(incline) * multiplier
```

In our `GeneratePoints`

method, we can now pass the `z`

value into the `PlotPoints`

method. Don’t forget to also change the value in the `PlotPoints`

method.

```
self:PlotPoints(x, y, z)
```

The `GeneratePoints`

method should look something like this:

```
function Object:GeneratePoints(n)
local goldenRatio = 1 + math.sqrt(5) / 4
local angleIncrement = math.pi * 2 * goldenRatio
local multiplier = 10
for i = 0, n do
local distance = i / n
local incline = math.acos(1 - 2 * distance)
local azimuth = angleIncrement * i
local x = math.sin(incline) * math.cos(azimuth) * multiplier
local y = math.sin(incline) * math.sin(azimuth) * multiplier
local z = math.cos(incline) * multiplier
self:PlotPoints(x, y, z)
end
end
```

Cool! Once again, I’ll call the `GeneratePoints`

method with 400 points. Here is the result:

Lastly, I’ll put each point’s `Vector3`

position into a table.

```
self.points[i] = Vector3.new(x, y, z)
```

I’ll put this inside of the loop:

```
for i = 0, n do
local distance = i / n
local incline = math.acos(1 - 2 * distance)
local azimuth = angleIncrement * i
local x = math.sin(incline) * math.cos(azimuth) * multiplier
local y = math.sin(incline) * math.sin(azimuth) * multiplier
local z = math.cos(incline) * multiplier
self._points[i] = Vector3.new(x, y, z) -- Right here!
self:PlotPoints(x, y, z)
end
```

That’s all! As a closing note for this section, I’ll include some images of what happens when you change around the sines and cosines. Have a play around yourself.

### 4. Uses and closing notes

Other than being a visually beautiful representation of mathematics, there are a few cases where this may come in helpful in your own games that I can think of:

- Visual effects, such as spells for an RPG game.
- Can be used in tandem with raycasting to create basic character vision.

You can also generate points, as I did further up the post, at a slow rate. Perhaps you could also animate them in some way, or maybe apply a trail to one point and move that point through its positions.

Personally, I’m using this technique to implement Boids into my own game.

I’m currently working on creating basic obstacle avoidance using raycasting. I’m limiting the number of points that are generated to simulate the field of view of the Boid. The Boid will go in the best direction to avoid obstacles and other Boids. That’s the idea anyways, but it’s still in early stages.