Using Math to generate Spheres

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve?
    I want to be able to generate spheres using math.

  2. What is the issue?
    I do not know the math used to generate spheres. I only know the math for a circle.

  3. What solutions have you tried so far?
    I have tried asking in various discords.

Circle Generation.

local Radius = 1
local Circle = math.pi * 2
local Amt = 10000

for Number = 1, Amt do
	local Angle = Circle / Amt * Number
	local X = math.sin(Angle) * Radius
	local Z = math.cos(Angle) * Radius
end

If anyone could help that would be nice (Iā€™m trying to create a game like no mans sky).

5 Likes

not sure but I found this https://rosettacode.org/wiki/Draw_a_sphere#Lua

2 Likes

Iā€™m pretty sure thatā€™s drawing a 2D sphere not a 3D sphere.

1 Like

I donā€™t know how to help but I have to ask, what is this for?

Procedural generation for a game like No Mans Sky.

1 Like

What I did is start with an octahedron containing 8 triangles. (I used OOP to represent the triangles which allowed me to do additional operations on them.)

Then over several iterations (I used 3 or 4) split each of those triangles into 4 smaller connected triangles, and shift the vertices so that they are one radius away from the center. The next iteration will then do this to the smaller triangles. Over time the polyhedron will look more sphere-like.

The octahedron in this chart should be what the result looks like:

5 Likes

How would I generate 8 triangles using math?

I just defined the vertices with Vector3s, then I made a function that takes a triangle (a set of 3 vector3s) and returns 4 new triangles like the wikipedia link shows. The new vertices then get moved outwards so that their distance from the center is the sphereā€™s radius.

That process then gets repeated several times, generating even more triangles which make the sphere look smoother with each iteration.

Can you explain what that means in laymens terms?

Here is my code, but beware it isnā€™t documented so you might have to sift through it a little since I donā€™t remember exactly how it is structured anymore.

OldSphereScript.rbxl (25.2 KB)

1 Like

Since you did polar to cartesian coordinate transformation in your code, you can just replace it with sphere to cartesian.


Edit: You will need one nested for loop more because there are 2 angles now.

2 Likes

Oh, I meant using Parts like Instance.new(ā€œPartā€) not premade parts.

1 Like

Can you elaborate and explain it in a more simple way?

1 Like

Basically what you did is using Polar coordinates to make a sphere in the cartesian system (x,y,z)

For a sphere you basically do the same thing but with two angles and the Y-direction. You are changing the angle with a for loop which means you now need 2 loops which are nested so you can change phi (from 0 to 2pi) and theta (from 0 to pi).

2 Likes

This is the farthest I could get

local Radius = 10
local Circle = math.pi * 2
local Amt = 100

local Folder = Instance.new("Folder")
Folder.Name = "Part"
Folder.Parent = workspace

for Number = 1, Amt do
	local Angle = Circle / Amt * Number
	local X = math.sin(Angle) * Radius
	local Z = math.cos(Angle) * Radius
	
	function Part(OrientationX, OrientationY, OrientationZ)
		local Part = Instance.new("Part")
		Part.Orientation = Vector3.new(OrientationX, OrientationY, OrientationZ)
		Part.Position = Vector3.new(X, Z)
		Part.Anchored = true
		Part.Size = Vector3.new(1, 1, 1)
		Part.Parent = Folder
	end
	
	Part()
	
	for Number2 = 1, Amt do
		Part(Number2, 0, 0)
	end
end

But it is still not working how I want it and I do not know why.

Iā€™m sorry if my method was a bit too complex. I would actually recommend starting out with @NachtHemdā€™s suggestion since that can make it easier to understand vectors and coordinate systems better.

Basically youā€™d iterate the inclination from -90 degrees (the south pole) to 90 degrees (north pole) giving you each possible latitude. For each latitude you could then generate a ā€˜ringā€™ by iterating from 0 to 360 degrees in azimuth. Your code could look something like this:

In addition you can use CFrame operations to get around converting spherical to cartesian coordinates.

local origin = CFrame.new(0, 0, 0)
local radius = 10

for lat = -80, 80, 10 do
	for lon = 0, 350, 10 do
		---fromEulerAnglesYXZ will rotate along Y axis first, then X
		local cf = origin * CFrame.fromEulerAnglesYXZ(math.rad(lat), math.rad(lon), 0) * CFrame.new(0, 0, -radius)
		local part = Instance.new("Part")
		part.Size = Vector3.new(1, 1, 1)
		part.CFrame = cf
		part.Parent = workspace
	end
end

Result:
Screenshot (74)

Also be aware that since the number of longitudes are distributed equally for every latitude the parts are more denser at the poles, so you might have to come up with some way to limit the number of parts at the higher latitudes.

5 Likes

You could create a fibonacci sphere, theyā€™re quite easy and also very efficient. Normal spheres have a high amount of detail at the bottom / tops of the spheres, while fibonacci spheres have evenly spread-out points all across the sphere.

4 Likes

Is there a way to make the sphere look better?

Yes I am trying to re-create the code in lua but I am getting a bit stuck on the for loop.

local Number = (math.sqrt(5) + 1.0) / 2.0
local Number2 = (2.0 - Number) * 2.0 * math.pi
std::vector<Vec3> fibonacci_spiral_sphere(const int num_points) {
    std::vector<Vec3> vectors;
    vectors.reserve(num_points);
    
    const double gr=(sqrt(5.0) + 1.0) / 2.0;  // golden ratio = 1.6180339887498948482
    const double ga=(2.0 - gr) * (2.0*M_PI);  // golden angle = 2.39996322972865332

    for (size_t i=1; i <= num_points; ++i) {
        const double lat = asin(-1.0 + 2.0 * double(i) / (num_points+1));
        const double lon = ga * i;

        const double x = cos(lon)*cos(lat);
        const double y = sin(lon)*cos(lat);
        const double z = sin(lat);

        vectors.emplace_back(x, y, z);
    }
    
    return vectors;
}

Since I do not know C++

Translated into Lua (untested though) (tested)

local function fibonacci_spiral_sphere(num_points)
	local vectors = {}
	local gr = (math.sqrt(5) + 1) / 2
	local ga = (2 - gr) * (2 * math.pi)
	
	for i = 1, num_points do
		local lat = math.asin(-1 + 2 * i / (num_points + 1))
		local lon = ga * i

		local x = math.cos(lon) * math.cos(lat)
		local y = math.sin(lon) * math.cos(lat)
		local z = math.sin(lat)

		table.insert(vectors, Vector3.new(x, y, z))
	end

	return vectors
end
2 Likes