Random pt inside a sphere

What’s a way to calculate a point inside a sphere given radius (with uniformity)?

1 Like

Very similar post from a week ago:

I would go for @buildthomas solution from that post unless you need incredibly high efficiency, in which case you would need to do a lot more maths.

That’s for a circle.

I know but the specific reply I linked to could also be applied to spheres

1 Like

Simple solution. Here’s some code I threw together. It’ll grab any point within a sphere’s bounds (defined by origin and radius) and can be anywhere from 0.01 studs away from the origin all the way up to resting on the surface of the sphere.

function FindPointInSphere(Origin, Radius)
    local CF = CFrame.new(Origin) * CFrame.fromOrientation(math.rad(math.random(0, 359)), math.rad(math.random(0, 359)), 0)
    return CF * Vector3.new(0, 0, -math.random(1, Radius * 100) / 100)
end

Working with spherical coordinates, you can easily get a random point in a sphere.
Hope this helps!

local MAX_RADIUS = 10 
local RandomRadius = math.random(1, MAX_RADIUS)
local Theta = math.random(0, 359)
local Phi = math.random(-180, 180)

local X = RandomRadius * math.cos(math.rad(Theta)) * math.cos(math.rad(Phi))
local Y = RandomRadius * math.sin(math.rad(Phi))
local Z = RandomRadius * math.sin(math.rad(Theta)) * math.cos(math.rad(Phi))

https://i.gyazo.com/8b065baa1e97f370b611c72b082603dc.mp4

1 Like

It isn’t uniform distance.
image

3 Likes

or

I think you’re on the right track. My code is wrong in that it just makes a random point in the sphere.

My second attempt (which failed and I have yet to have another go at it) was to base the chance of a point being placed based on the area of the sphere if its outer edge were however far out the selected point is. Smaller areas would have smaller chances, larger areas would have larger chances.

I may try this later but if you (or any other reader) wants to crack at it, go on.

local rand = Random.new()
function getUnitSpherePoint()
	local vec
	local attempts = 10
	while attempts > 0 do
		vec = Vector3.new(rand:NextNumber(-1,1), rand:NextNumber(-1,1), rand:NextNumber(-1,1))
		if vec.magnitude <= 1 then
			return vec
		end
		attempts = attempts - 1
	end
	-- couldn't find a solution within 10 tries, so let's just normalize the last one:
	return vec.unit
end
2 Likes

I wouldn’t rely on random logic to generate points. Sure it may work, but it’s just a messy practice in general.

Here’s an implementation of the algorithm from the StackOverflow post I linked.

function gaussian ()
    return  math.sqrt(-2 * math.log(math.random())) *
            math.cos(2 * math.pi * math.random())
end

-- Generates a random point inside a unit sphere.
local function getUnitSpherePoint()
	local u = math.random()
	local v = Vector3.new(gaussian(), gaussian(), gaussian())
	return v * (u^(1/3)) / v.magnitude
end

image

BUT! @buildthomas’s non-deterministic implementation is faster than mine on average!

function gaussian ()
    return  math.sqrt(-2 * math.log(math.random())) *
            math.cos(2 * math.pi * math.random())
end

-- Generates a random point inside a unit sphere.
local function getUnitSpherePoint()
	local u = math.random()
	local v = Vector3.new(gaussian(), gaussian(), gaussian())
	return v * (u^(1/3)) / v.magnitude
end

local rand = Random.new()
function getUnitSpherePointBrute()
	local vec
	local attempts = 1000
	while attempts > 0 do
		vec = Vector3.new(rand:NextNumber(-1,1), rand:NextNumber(-1,1), rand:NextNumber(-1,1))
		if vec.magnitude <= 1 then
			return vec
		end
		attempts = attempts - 1
	end
	-- couldn't find a solution within 1000 tries, so let's just normalize the last one:
	return vec.unit
end

-- Brute force performance test.
local begin = tick()
for i = 1, 100000 do
	local x = getUnitSpherePointBrute()
end
print(tick() - begin) -- 0.14083695411682

-- Algorithm from SO
local begin = tick()
for i = 1, 100000 do
	local x = getUnitSpherePoint()
end
print(tick() - begin) -- 0.2488055229187

Just go with the non-deterministic approach.

11 Likes

The technique is called rejection sampling and it has its use cases, as visible above (saving performance by giving up determinism and a tiny tiny bit of randomness).

As a little side note, if performance is an issue then the magnitude check (costly square root) could be replaced with checking that vec:Dot(vec) <= r*r.

2 Likes