# I've developed a new Perlin-style noise. Can you improve it?

This is a simplified version of the OpenSimplex noise (and all its versions).

``````
seed = 0x120367e73b381c44
#Modified version of random number generator by Bernard Widynski. https://arxiv.org/abs/2004.06278

get_random_number(pos,seed) = get_random_number(convert(UInt64,pos),convert(UInt64,seed))
get_random_noise(pos,seed) = get_random_noise(convert(UInt64,pos),convert(UInt64,seed))

function get_random_number(pos::UInt64,seed::UInt64)
y = x = pos*seed; z = y+seed
x = x*x+y; x = (x>>32)|(x<<32)
x = x*x+z; x = (x>>32)|(x<<32)
x = x*x+y; x = (x>>32)|(x<<32)
return ((x*x+z)>>32)%UInt32
end

function get_random_noise(pos::UInt64,seed::UInt64)
y = x = pos*seed; z = y+seed
x = x*x+y; x = (x>>32)|(x<<32)
x = x*x+z; x = (x>>32)|(x<<32)
x = x*x+y; x = (x>>32)|(x<<32)
return reinterpret(Float32,(((x*x+z)>>41)%UInt32)|reinterpret(UInt32,Float32(1.0)))-Float32(1.0)
end

function smooth_transition(x)

x = x*(1-x)
x2 = x*x
return 16*x2
end

function smoother_step_7_th_order(x)
x_sq = x*x

return x_sq*x_sq*(((-20*x+70)*x-84)*x+35)
end

using Plots
using Random
using LoopVectorization
x = range(0, 1, length=100)
y = smoother_step_7_th_order.(x)
plot(x,y)

const main_seed::UInt64 = 0xb6e06d07c7dd3a5f
#seeder = Xoshiro(main_seed)

@inline function noise2d(x_in,y_in,xx_seed,xy_seed,xz_seed,yx_seed,yy_seed,yz_seed,zx_seed,zy_seed,zz_seed)
#x = 0.66666667*y_in

eps = 1e-6
unit = one(UInt64)
y = 0.57735027*x_in -0.33333333*y_in
z = -0.57735027*x_in -0.33333333*y_in
x = -y-z
#(X,x) = (reinterpret(UInt64,floor(Int64,x)),x%1)
X = reinterpret(UInt64,floor(Int64,x))
x = x-floor(x)
Y = reinterpret(UInt64,floor(Int64,y))
y = y-floor(y)
Z = reinterpret(UInt64,floor(Int64,z))
z = z-floor(z)
#(Y,y) = (reinterpret(UInt64,floor(Int64,y)),y%1)
#(Z,z) = (reinterpret(UInt64,floor(Int64,z)),z%1)
next_X = X+unit
next_Y = Y+unit
next_Z = Z+unit

x_seed = xx_seedâŠ»get_random_number(Y,xy_seed)âŠ»get_random_number(Z,xz_seed)
#println(xx_seed)

current_x_value = get_random_noise(X,x_seed)
next_x_value = get_random_noise(next_X,x_seed)
true_x_value = (smoother_step_7_th_order(x)*(next_x_value-current_x_value) + current_x_value-0.5)*2
#println("Debug")
#println(current_x_value)
#println(next_x_value)
true_x_value *= smooth_transition(y)*smooth_transition(z)
#println(true_x_value)
y_seed = yy_seedâŠ»get_random_number(X,yx_seed)âŠ»get_random_number(Z,yz_seed)
current_y_value = get_random_noise(Y,y_seed)
next_y_value = get_random_noise(next_Y,y_seed)
true_y_value = (smoother_step_7_th_order(y)*(next_y_value-current_y_value) + current_y_value-0.5)*2

true_y_value *= smooth_transition(x)*smooth_transition(z)

z_seed = zz_seedâŠ»get_random_number(X,zx_seed)âŠ»get_random_number(Y,zy_seed)
current_z_value = get_random_noise(Z,z_seed)
next_z_value = get_random_noise(next_Z,z_seed)
true_z_value = (smoother_step_7_th_order(z)*(next_z_value-current_z_value) + current_z_value-0.5)*2

true_z_value *= smooth_transition(x)*smooth_transition(y)

return (true_x_value+true_y_value+true_z_value)/3

end

function main(seed)
seeder = Xoshiro(seed)

#Seed can be generated in any random way.
xx_seed = rand(seeder,UInt64)
xy_seed = rand(seeder,UInt64)
xz_seed = rand(seeder,UInt64)
yx_seed = rand(seeder,UInt64)
yy_seed = rand(seeder,UInt64)
yz_seed = rand(seeder,UInt64)
zx_seed = rand(seeder,UInt64)
zy_seed = rand(seeder,UInt64)
zz_seed = rand(seeder,UInt64)

A = Array{Float64,2}(undef,(1024,1024))
for y in 1:1024, x in 1:1024
@inbounds @fastmath A[x,y] = noise2d(x/64,y/64,xx_seed,xy_seed,xz_seed,yx_seed,yy_seed,yz_seed,zx_seed,zy_seed,zz_seed)
end
return A
end
Xoshiro(main_seed)

heatmap(main(main_seed))
``````

@Siddharth_Bhatia, @Kyjor
@cormullion you might be interested.

1 Like

So can you describe a bit more what you changed? What properties this has? How it compares to other implementations?

Also what do you have in mind with â€ścan you improve itâ€ť? Are there allocation you think are unnecessary? Do you think it could be faster (and have some benchmarks and reasoning why you think that is)? Do you want stylistic suggestions?

Please put in a bit more effort in your post if you want people to work on/with your code.

2 Likes

Nice. You might want to compare against the Julia state of the art: Michael Fianoâ€™s CoherentNoise.jl.

3 Likes

My implementation:

1. sidestep the need to determine which simplex weâ€™re in by overparameterizing the coordinate by 1 dimension.
2. sidestep the requirement for the coordinate of the vertices by interpolating between the (N-1)-dimensional lines inside two adjacent simplices. The 2d noise inside a triangle is made of three sets of two triangles averaged together, with the triangle itself forming the pair with each adjacent triangle.
3. Is completely branchless.
4. Use a new square-based pseudorandom number generator instead of lookup-based hash.

Okay.

1. Currently, this doesnâ€™t work with loopvectorization. I think it has to do with bit operations not being supported. Can this be made to work with SIMD/GPU/etc?
2. How can the noise artifacts be reduced?
2 Likes