Hello Everyone! My name is paswa!
Today I’m going to be showing you step-by-step on how 3D Perlin Noise can be created and used.
Firstly, we need to understand Perlin Noise! I explained it in a post before this one, you can find it here.
Highly recommended reviewing the Perlin Noise section of the post before progressing this post
Alrighty, assuming you have read up on Perlin noise were gonna start talking about 3D Perlin Noise.
What is 3D Perlin?
3D Perlin Noise is a series/collection of noises along different axis’s. One for each of the x, y, and z axis.
What are the terms your gonna be using?
Density. Density is a number assigned to each pixel within your 3D Perlin noise. Its essentially the equivalent to what color the noise is, but we call it Density since its the collection of multiple noise values.
Here is a small picture resembling 3D Perlin and its colors/densities.
How do we apply this all?
local block_size = 5
local height, width = 30, 100
local blocks = game.Workspace.Blocks
local xOffset = math.random(-100000, 100000)
local yOffset = math.random(-100000, 100000)
local zOffset = math.random(-100000, 100000)
local freq = 2
local ampltiude = 10
local scale = 200
local maxDensity = 0
for x=0, width do
task.wait()
for y=0, width do
if y % 10 == 0 then
task.wait()
end
for z=0, width do
local xP = math.noise(((y + yOffset) / scale) * freq , ((z + zOffset) / scale) * freq) * ampltiude
local yP = math.noise(((x + xOffset) / scale) * freq , ((z + zOffset) / scale) * freq) * ampltiude
local zP = math.noise(((x + xOffset) / scale) * freq , ((y + yOffset) / scale) * freq) * ampltiude
local density = xP + yP + zP
if density >= maxDensity then
local part = Instance.new("Part")
part.Anchored = true
part.Position = Vector3.new(x * block_size, y * block_size, z * block_size)
part.Size = Vector3.new(block_size, block_size, block_size)
part.Parent = blocks
end
end
end
end
This looks like gibberish?
Unless your fluent in Lua you would understand this, I completely don’t expect many of you to fully understand this snippet. Therefore I’ll be explaining portions of this code, mainly ones relating to the topic.
Snippet 1: 3D Perlin Noise
local xP = math.noise(((y + yOffset) / scale) * freq , ((z + zOffset) / scale) * freq) * ampltiude
local yP = math.noise(((x + xOffset) / scale) * freq , ((z + zOffset) / scale) * freq) * ampltiude
local zP = math.noise(((x + xOffset) / scale) * freq , ((y + yOffset) / scale) * freq) * ampltiude
local density = xP + yP + zP
Don’t let the tons of parentheses and math equations get to you, all we need is a basic understanding of how this functions to understand it. First off, please read the past post before progressing as I will not be re-explaining it! Upon finishing it, you will fully grasp this snippet.
Essentially we are combining multiple noise maps (one per axis) into a variable called density, which as we stated earlier is “the collection of multiple noise values”. This variable is used to alter certain aspects of the noise above, below, or between certain ranges.
if density >= maxDensity then
local part = Instance.new("Part")
part.Anchored = true
part.Position = Vector3.new(x * block_size, y * block_size, z * block_size)
part.Size = Vector3.new(block_size, block_size, block_size)
part.Parent = blocks
end
As said above we just select portions of the noise by limiting/picking certain densities. We then simply place a “block” in that location.
Misc. Snippet
if y % 10 == 0 then
task.wait()
end
I’m sure many of you absolutely do not know what this is or what this does.
Essentially this checks every time the loop runs 10 times and runs the code inside. Which is task.wait(). We use this to yield our generation to allow for more performance on all/lower devices.
% -- Modulo
is a math symbol used within lua along with it’s counter partner / -- Division
Modulo is used to return the remainder of divisible value.
For more information on what this does you can visit this website
Thanks for reading! If you don’t fully understand something feel free to ask in the comments!