I am looking for a simple way to generate something similar to procedural\perlin noise in matlab.
It just needs to have the general perlin noise traits, not to replicate ken perlin's method exactly.
I am looking for a simple way to generate something similar to procedural\perlin noise in matlab.
It just needs to have the general perlin noise traits, not to replicate ken perlin's method exactly.
An easy way to do it in in Matlab, as shown in Octave by the Nullprogramm blog is:
n = 64;
m = 64;
im = zeros(n, m);
im = perlin_noise(im);
figure; imagesc(im); colormap gray;
function im = perlin_noise(im)
[n, m] = size(im);
i = 0;
w = sqrt(n*m);
while w > 3
i = i + 1;
d = interp2(randn(n, m), i-1, 'spline');
im = im + i * d(1:n, 1:m);
w = w - ceil(w/2 - 1);
end
perlin noise implementation already exists in several programming languages and is freely available on the internet. For instance, there is a java class on Ken Perlin's homepage (3D version / 4D version) that could be used with Matlab.
However, if you absolutely want to use Matlab language, I guess it is just a matter of "translating" which should be pretty straightforward. Here is a try for dimensions 1 to 3 which should work. It is not optimized nor thoroughly tested (seem to have some border problems). Hope it helps you.
function noise=perlin(values,x,y,z)
if(numel(values)~=512)
values=randperm(256)-1;
values=[values values];
end
x=abs(x);
X=bitand(floor(x),255);
x=x-floor(x);
u=fade(x);
A=values(1+X);
noise=linterp(u,grad1d(values(1+X),x),grad1d(values(1+X+1),x-1));
if(nargin>2)
y=abs(y);
Y=bitand(floor(y),255);
y=y-floor(y);
v=fade(y);
A=A+Y;
B=values(1+X+1)+Y;
noise=linterp(u,linterp(u,grad2d(values(1+A),x,y),grad2d(values(1+B),x-1,y)),linterp(u,grad2d(values(1+A+1),x,y-1),grad2d(values(1+B+1),x-1,y-1)));
end
if(nargin>3)
z=abs(z);
Z=bitand(floor(z),255);
z=z-floor(z);
w=fade(z);
AA=values(1+A)+Z;
AB=values(1+A+1)+Z;
BA=values(1+B)+Z;
BB=values(1+B+1)+Z;
noise=linterp( w, ...
linterp(v, ...
linterp(u, ...
grad3d(values(1+AA),x,y,z), ...
grad3d(values(1+BA),x-1,y,z)), ...
linterp(u, ...
grad3d(values(1+AB),x,y-1,z), ...
grad3d(values(1+BB),x-1,y-1,z))), ...
linterp(v, ...
linterp(u, ...
grad3d(values(1+AA+1),x,y,z-1), ...
grad3d(values(1+BA+1),x-1,y,z-1)), ...
linterp(u, ...
grad3d(values(1+AB+1),x,y-1,z-1), ...
grad3d(values(1+BB+1),x-1,y-1,z-1))));
end
end
function l=linterp(t,a,b)
l=a+t*(b-a);
end
function t=fade(t)
t=6*t^5-15*t^4+10*t^3;
end
function g=grad1d(hash,x)
if(bitand(hash,1))
g=-x;
else
g=x;
end
end
function g=grad2d(hash,x,y)
h=bitand(hash,3);
if(bitand(h,2))
u=-x;
else
u=x;
end
if(bitand(h,1))
v=-y;
else
v=y;
end
g=u+v;
end
function g=grad3d(hash,x,y,z)
h=bitand(hash,15);
if(h<8)
u=x;
else
u=y;
end
if(h<4)
v=y;
elseif(h==12 || h==14)
v=x;
else
v=z;
end
if(bitand(h,1))
if(bitand(h,2))
g=-u-v;
else
g=-u+v;
end
else
if(bitand(h,2))
g=u-v;
else
g=u+v;
end
end
end
I have recently tried to translate Ken Perlin's "Improved Noise". The results are at the end of this post. Note that it takes 10 seconds to make a 256 by 256 matrix. Visualize with imagesc
.
Not that I have found that gradient(rand(w, h))
seems to give decent results. I don't know much about noise, so I don't know if this is the "same thing", but it sure seems to work. For larger grids, all you need is to interpolate points in a smaller rand
.
function n = noise(x, y, z)
% noise(x, y, z) generates (I believe) 3 dimensional noise. To use, iterate
% through your array and generate each value with a call this function.
% Note that it is very slow.
%
% Based Ken Perlin's "Improved Noise" in 2001, or 2002, or something.
% The unit cube which contains this point
uX = mod(floor(x), 256);
uY = mod(floor(y), 256);
uZ = mod(floor(z), 256);
% Find relative x, y, z of point in cube
x =x- floor(x);
y =y- floor(y);
z =z- floor(z);
% The mysterious "fade" function
fade = @(t) t * t * t * (t * (t * 6 - 15) + 10);
% Compute fade curved for each of x, y, z
u = fade(x);
v = fade(y);
w = fade(z);
% Hash coordinates of the 8 cube corners
p_half = randi(256, 256, 1) - 1;
p = [p_half, p_half];
a = p(1+uX) + uY;
aa = p(1+a) + uZ;
ab = p(1+a + 1) + uZ;
b = p(1+uX + 1) + uY;
ba = p(1+b) + uZ;
bb = p(1+b + 1) + uZ;
% "Lerp" is a shorter, more confusing name for "linear interpolation"
lerp = @(t, a, b) a + t * (b - a);
% This is how he gets the gradient
function g = grad(hash, x, y, z)
%Convert the low 4 bits of hash code into 12 gradient directions
h = mod(hash, 16);
% 50% chance for u to be on x or y
if h < 8
u_comp = x;
else
u_comp = y;
end
% 50% chance to reverse either component
if mod(h, 2) == 0
u_comp = -u_comp;
end
% 12.5% chance for v to be on x, 25% chance to be on y, 62.5% chance for z
if (h == 12 || h == 14)
v_comp = x;
elseif (h < 4)
v_comp = y;
else
v_comp = z;
end
% 50% chance to reverse either component
if mod(h/2, 2) == 0
v_comp = -v_comp;
end
g = u_comp + v_comp;
end
% And add blended results from 8 corners of cube
n = lerp(w, lerp(v, lerp(u, grad(p(1+aa), x, y, z), ...
grad(p(1+ba), x - 1, y, z)), ...
lerp(u, grad(p(1+ab), x, y - 1, z), ...
grad(p(1+bb), x - 1, y - 1, z))), ...
lerp(v, lerp(u, grad(p(1+aa + 1), x, y, z - 1), ...
grad(p(1+ba + 1), x - 1, y, z - 1)), ...
lerp(u, grad(p(1+ab + 1), x, y - 1, z - 1), ...
grad(p(1+bb + 1), x - 1, y - 1, z - 1))));
end