exsnake
exsnake

Reputation: 1823

Interpolate Gradient noise in Julia

I'm trying to implement and learn the code from here but I'm having problems with interpolate it. This is my code from my implementation in Julia:

lerp(a, b, w) = a * (1 - w) + b * w

function Noise(x, y)
    n = x + y * 53;
    n = (n << 13) $ n;
    return (1.0 - ( (n * ((n * n * 15731) + 789221) +  1376312589) & 0x7fffffff) / 1073741824.0);
end

function coherentNoise(x,y)
   x₁ = convert(Int64,modf(x)[2])
   y₁ = convert(Int64,modf(y)[2])
   xᵣ = x - x₁
   yᵣ = y - y₁
   q1 = Noise(x₁-1,y₁+1)
   q2 = Noise(x₁+1,y₁+1)
   q3 = Noise(x₁-1,y₁-1)
   q4 = Noise(x₁-1,y₁+1)

   v = lerp(q1,q2,xᵣ)
   v1 = lerp(q3,q4,xᵣ)
   return abs(lerp(v,v1,yᵣ))
   #value = bilinear(q1,q2,q3,q4,x₁,x₁-1,x₁+1,y₁,y₁-1,y₁+1)
   #return abs(value)
end


arr = Array{Float64}(height,width)

for x = 1:height
    for y = 1:width
        nx = x/60
        ny = y/60
        arr[x,y]=coherentNoise(nx,ny)
    end
end

this is the result:

enter image description here

I tried also use this bilinear interpolation function but i get a worse result.

# bilinear interpolation
function bilinear(Q1,Q2,Q3,Q4,x,x1,x2,y,y1,y2)
    R1 = ((x2-x)/(x2-x1))*Q3 + ((x-x1)/(x2-x1))*Q4
    R2 = ((x2-x)/(x2-x1))*Q1 + ((x-x1)/(x2-x1))*Q2
    P = ((y2-y)/(y2-y1))*R1 + ((y-y1)/(y2-y1))*R2
    return P
end

the result:

enter image description here

I don't know if is a problem with theNoise function or something else.

Update

I'm obtaining best results using bilinear interpolation with the next noise generator function:

function coherentNoise(x,y,n::Int64)
    #octa3e
    o = 0.25
    #octave x and y
    x /=n
    y /=n 
    #decimal value
    x₁ = x < 0 ? x : x-o
    y₁ = y < 0 ? y : y-o
    x₂ = x+o
    y₂ = y+o
    #calculate corners data
    q1 = abs(Noise(floor(Int64,x₁),floor(Int64,y₂)))
    q2 = abs(Noise(floor(Int64,x₂),floor(Int64,y₂)))
    q3 = abs(Noise(floor(Int64,x₁),floor(Int64,y₁)))
    q4 = abs(Noise(floor(Int64,x₂),floor(Int64,y₁)))
    #bilenear interpolation

    value = bilinear(q2,q1,q4,q3,x,x₁,x₂,y,y₁,y₂)
    return abs(value)
end

Result:

enter image description here

I'm thinking that the problem is how I select the points to do the bilinear interpolation, no idea how to do it right.

Update 2:

This is the code to generate the images:

for x = 1:height
    for y = 1:width
        arr[x,y]=coherentNoise(x,y,50)
    end
end

imwrite(convert(Image,arr),"desktop/projects/julia/Noise/test2.png")

Update 3

Thanks to the answer of Dan Getz I'm getting a better output but with some weird "worms"

output:

enter image description here

Upvotes: 2

Views: 620

Answers (1)

Dan Getz
Dan Getz

Reputation: 18217

Without the ability to test the code, this should need verification. Basically, I think your intuition the problem is in the choice of interpolation points is right. This is an attempt to fix it (see also comments).

function coherentNoise(x,y)
   x₁,xᵣ = floor(Int64,x),mod(x,1)
   y₁,yᵣ = floor(Int64,y),mod(y,1)
   q00 = Noise(x₁,y₁)
   q10 = Noise(x₁+1,y₁)
   q01 = Noise(x₁,y₁+1)
   q11 = Noise(x₁+1,y₁+1)

   v0 = lerp(q00,q10,xᵣ)
   v1 = lerp(q01,q11,xᵣ)
   noise = lerp(v0,v1,yᵣ)
   return noise
end

Notes:

  • qs renamed to q00 and such to indicate which corner by the offset to X (first number) and Y (second number).
  • The identical q1 and q4 fixed.
  • floor and mod( ,1) used instead of modf since they choose consistent rounding direction (downward) regardless of sign.
  • The Noise function should type annotate its parameters to restrict to Int32 values, and the ; at line ends in this function not needed. This is a hash function, and the same effect could be easily coded with Julia hash function and a little wrapper.

For example:

Noise(x,y) = 1.0-2.0*hash((x,y))/typemax(UInt)

is a more Julia styled drop-in replacement for Noise.

If you try this and comment on bugs, we can try to fixup function.

Upvotes: 2

Related Questions