# Resource:

Hello. I made a modulescript that can be used as an alternative to perlin noise. This type of noise is called “Value Noise” (Similar, but Not the same as perlin noise). Here is the code for it:

``````local noise = {}
noise.__index = noise

function noise.new(seed)
local self = {}
setmetatable(self, noise)
self.seed = seed or Random.new():NextNumber(-256, 256)
return self
end

local randomConstant = 2^32
local function rand(worldSeed, x, y, z)
local random = Random.new(worldSeed)

random = Random.new(bit32.bxor(x, random:NextInteger(-randomConstant, randomConstant)))
random = Random.new(bit32.bxor(y, random:NextInteger(-randomConstant, randomConstant)))
random = Random.new(bit32.bxor(z, random:NextInteger(-randomConstant, randomConstant)))

return random:NextNumber(-1, 1)
end

local function lerp(x, y, a1)
local a2 = (1-math.cos(a1*math.pi))/2
return x*(1-a2)+y*a2
end

local function bilinear(x, y, j, k, a, b)
return lerp(lerp(x, y, a), lerp(j, k, a), b)
end

local function trilinear(x, y, j, k, l, m, n, o, a, b, c)
return lerp(bilinear(x, y, j, k, a, b), bilinear(l, m, n, o, a, b), c)
end

function noise:normalSample(x, y, z)
local
bx,
ax = math.floor(x), math.ceil(x)

local
by,
ay = math.floor(y), math.ceil(y)

local
bz,
az = math.floor(z), math.ceil(z)

local tx, ty, tz = x - bx, y - by, z - bz
local a,
b,
c,
d,
e,
f,
g,
h = rand(self.seed, bx, by, bz), rand(self.seed, ax, by, bz), rand(self.seed, bx, ay, bz), rand(self.seed, ax, ay, bz), rand(self.seed, bx, by, az), rand(self.seed, ax, by, az), rand(self.seed, bx, ay, az), rand(self.seed, ax, ay, az)

local noiseValue = trilinear(a, b, c, d, e, f, g, h, tx, ty, tz)
return noiseValue
end

return noise
``````

# What does this do?

This resource can be used as an alternative to perlin noise. It generates a 3D grid of random numbers, and then it uses cosine interpolation to figure out what the random numbers in between of the grid coordinates should be, and then returns this number.

# Why / Why not?

There is no particular reason why anyone would ever want to use this except for if they just like the results of value noise more than they do perlin noise. I have not benchmarked this module yet, so I don’t know if there is any performance benefits to using this over perlin noise. If you ever want to use this for terrain generation, just keep in mind that terrain generated with perlin noise looks more natural, and less artificial than terrain that is generated with value noise (this module). There are however many different uses for some of the code that makes this module. This modulescript generates a random value for each coordinate in the `rand(worldSeed, x, y, z)` function, and then it uses something called “Tri-Cosine Interpolation” to calculate / approximate what the random number should be for all of the possible coordinates in between, so what you could do is modify this code, so it could do other things that have nothing to do with procedural generation / random number generation (ie downsampling some data). Overall, this module is somewhat useful (in my opinion).

# How to use it?

Here is a script that generates 3D Value noise:

``````local noise = require(script.Noise)

local noiseClass = noise.new()

for x = 1, 64 do
for y = 1, 64 do
for z = 1, 64 do
local noiseValue = noiseClass:normalSample(x / 16, y / 16, z / 16) - ((y - 32)/24)
if noiseValue >= 0 then
local p = Instance.new("Part")
p.Parent = workspace
p.CFrame = CFrame.new(x, y, z)
p.Size = Vector3.one
p.Anchored = true
end
end
end
end
``````

Here is also a script that generates 2D Value noise:

``````local noise = require(script.Noise)

local noiseClass = noise.new()

for x = 1, 256 do
for z = 1, 256 do
local noiseValue = ((noiseClass:normalSample(x / 32, 0, z / 32) + 1) / 2) * 64
local p = Instance.new("Part")
p.Parent = workspace
p.CFrame = CFrame.new(x, noiseValue / 2, z)
p.Size = Vector3.new(1, noiseValue, 1)
p.Anchored = true
end
end
``````

# Extras:

Side effects

One thing you might notice is that terrain generated with this looks like it’s been generated on some type of grid:

I did a horrible job of drawing those grid lines, but you might notice that some of the terrain looks “squared” / “cubed”. This isn’t a glitch, but rather an artifact / side effect of using Value Noise.

This issue can be mitigated by adding some type of random offset to the coordinates going into the `:normalSample` function.

2D Value noise (With Offset):

``````local noise = require(script.Noise)

local noiseClass = noise.new()
local offsetNoiseClassX = noise.new()
local offsetNoiseClassZ = noise.new()

for x = 1, 256 do
for z = 1, 256 do
local xOffset = offsetNoiseClassX:normalSample(x / 32, z / 32, 0)
local zOffset = offsetNoiseClassZ:normalSample(x / 32, z / 32, 0)
local noiseValue = ((noiseClass:normalSample(x / 32 + xOffset, 0, z / 32 + zOffset) + 1) / 2) * 64
local p = Instance.new("Part")
p.Parent = workspace
p.CFrame = CFrame.new(x, noiseValue / 2, z)
p.Size = Vector3.new(1, noiseValue, 1)
p.Anchored = true
p.Color = Color3.new(noiseValue / 64, noiseValue / 64, noiseValue / 64)
end
end
``````

Picture:

As you can see, doing this will generate some pretty interesting results that look similar to Perlin noise (But, not quite the same), and I was not able to draw that grid, that I drew in the other screenshot. This can be done with 3D Value noise too, but when you want to do that, you also need to add an offset to the third coordinate (the y coordinate).

3D Value noise (With Offset):

``````local noise = require(script.Noise)

local noiseClass = noise.new()
local offsetXNoiseClass = noise.new()
local offsetYNoiseClass = noise.new()
local offsetZNoiseClass = noise.new()

for x = 1, 64 do
for y = 1, 64 do
for z = 1, 64 do
local noiseOffsetX = offsetXNoiseClass:normalSample(x / 16, y / 16, z / 16)
local noiseOffsetY = offsetYNoiseClass:normalSample(x / 16, y / 16, z / 16)
local noiseOffsetZ = offsetZNoiseClass:normalSample(x / 16, y / 16, z / 16)
local noiseValue = noiseClass:normalSample(x / 16 + noiseOffsetX, y / 16 + noiseOffsetY, z / 16 + noiseOffsetZ) - ((y - 32)/24)
if noiseValue >= 0 then
local p = Instance.new("Part")
p.Parent = workspace
p.CFrame = CFrame.new(x, y, z)
p.Size = Vector3.one
p.Anchored = true
end
end
end
end
``````

You can also make this linear instead of cosine by replacing the `lerp(x, y, a1)` function (that is inside of the value noise module) with:

``````local function lerp(x, y, a1)
return x*(1-a1)+y*a1
end
``````
2 Likes

really cool resource! i quickly coded it into my 2D Perlin Noise Visualizer and got some cool results!
(rather low resolution because i’m doing this on my old laptop)

1 Like