MATLAB Interpolation: Perlin Noise Interpolation

This is the fourth issue of the MATLAB interpolation fitting series, here are the links to the previous issues:

Issue 3: MATLAB Numerical Fitting: Least Squares Polynomial Fitting

Issue 2: matlab interpolation: Lagrangian interpolation

Phase 1: [Numerical Analysis and Fitting] Matlab Cubic Spline Interpolation Fitting Data

         First of all, before this article, please allow me to declare that this article is indeed obtained by referring to other people's algorithms (the original creator of the algorithm is Ken Perlin), so it is not my original content (it is Ken Perlin's), let me talk about the algorithm first The implementation process, and then the actual code and demonstration. The words of this article are indeed not original, (although all the codes in it are original and written by myself), (in fact, the second half of the article is really almost a translation [3], just try to make it understandable to as many people as possible) (like me If this kind of person reprints or translates, he must contact the original author. I really don’t know which type to mark. I hope that station C can add several types of articles in the future)

        (Even if this article allows me to only mark the original part of the code, other algorithms and so on are others’, and explanations and the like are all additional or translated content, not original by the author)

        Give the reference article first:

        For more than a day, I searched many things on stations B and C in order to get Berlin noise. Although I found some resources, I really didn’t find anything that could explain the algorithm clearly. Although there are some things on the wiki, I also Just realize one-dimensional interpolation according to the wiki above, and many articles often just have code but no way

     [1]   Perlin Noise (Perlin Noise) But the main reason I haven't figured out how it performs two-dimensional interpolation after the lerp inside

     [2]  An article to understand the Perlin noise algorithm, with code to explain  that although this is indeed relatively useful, it does not talk much about the two-dimensional interpolation later (the water will start later)

     However, in the end, Lucky, I finally found the reference article of the article [2], which is [3]. This article is very detailed, and it finally made me understand the algorithm process.

     [3]  The Perlin noise math FAQ  (by Matt Zucker)

Enter the topic below——

        Perlin noise is a very widely used interpolation noise algorithm, including applications to generate cloud effects, flame effects, lava, etc. and terrain (of course, I have to mention that Minecraft terrain generation can actually use this algorithm), Including cloud rendering effects applied in PhotoShop, also applied to terrain in Unity, wave generation, etc.

        1. The principle of one-dimensional Perlin noise interpolation

        We first introduce two smoothing functions Smoothstep and Fade:

        Both Smoothstep and Fade are smoothing functions on [0,1], the expression of Smoothstep

        Smoothstep(x)= \begin{Bmatrix} 0& x\leqslant 0\\ 3x^2-2x^3 & 0<x<1 \\ 1& x\geqslant 1 \end{Bmatrix}The image in [0,1] is as follows:

   

The Fade function is a higher-order fitting, the formula is

Fade(x) = \begin{Bmatrix} 0& x\leqslant 0 \\ 6x^5-15x^4+10x^3 & 0<x<1\\ 1 & x\geqslant 1 \end{Bmatrix}

(For these details, please refer to smoothstep in the wiki, which also has higher-order formulas)

   

 For one-dimensional Perlin noise interpolation, the principle is to use the Smoothstep or Faded function on each segment:

Since it is interpolated in the (0,1) interval, we generally limit it to the (0,1) range, so we generally take

x_0=floor(x), where floor is rounded to negative infinity, so that it is guaranteed to be inserted x-x_0within the range of (0,1), and then use the formula:

y = x_0+ Smoothstep(x-x_0)*(y_1-y_0)

Where y1 is the function value above the second node, (of course, the Smoothstep function can also be replaced by the Fade function here)

So I modified the one-dimensional Perlin noise interpolation function so that it can interpolate any given vector:

I "falsified" the formula y = x_0+ Smoothstep(\frac{x-x_0}{x_1-x_0})*(y_1-y_0)so that it works for any step size.

2. One-dimensional perlin noise code implementation

First give the functions I additionally wrote (I usually put them in a folder and add them to the path to call)

1. len.m ​​(calculate the length of the vector (because it is a bit troublesome to use length))

(Some comments I wrote in English, although they are not well written, there may be grammatical errors)

%返回一维数组长度(行数或列数其中大的一个,必须是一位数组)
function [length]=len(A)

    s=size(A);
    if s(2)==1
        length=s(1); %取行
    end
    if s(1)==1
        length=s(2);
    end
    if s(1)~=1 && s(2)~=1
        disp('必须是单行向量或单列向量')
        return
    end
            
end

2. Smoothstep.m

function f = Smoothstep(x)
% define the function smoothstep that could be probably used in the Perlin noise
if x<=0
    f=0;
else
    if x<1
        f=3*x^2-2*x^3;
    else
        f=1;
    end
end
end

3.Fade.m

function f = Fade(x)
% define the function smoothstep that could be probably used in the Perlin noise
if x<=0
    f=0;
else
    if x<1
        f=6*x^5-15*x^4+10*x^3;
    else
        f=1;
    end
end

end

4. (This is the main function, where Fade is used for interpolation by default if two parameters are input), my function improves compatibility as much as possible, and the third parameter can be used for both the interpolation function @Fade/@Smoothstep and the interpolation step Long dx (default 0.01), up to four parameters are input, if there is no output, draw an interpolated image

function [x2,y2] = PerlinNoise_1(x,y,dx,Func)
% 柏林噪声一维插值函数
% 函数输入量只能为@Fade或者@Smoothstep
if nargin <=3
    Func = @Fade;   % use the function handle to give it a definition
    dx = 0.01;
end

if nargin ==3
    try
        test = dx;   %test if the dx is valid
    catch
        dx = 0.01;
    end
    try
        test = Func(0.8); %test if the Func is valid
    catch
        Func = @Fade;    
    end
end

x2 = min(x):dx:max(x);
y2 = zeros(1,len(x2));

j = 2;
% assure that the initial value should not be zero and(the initial value 
% of two function are equal,so the first j should be 2)
% this denotes the x(j-1) which indicates the initial value of per step
for i= 1:len(x2)
    if x2(i)>x(j)
        j=j+1;
    end
    y2(i) = y(j-1) + Func((x2(i)-x(j-1))/(x(j)-x(j-1)))*(y(j)-y(j-1));  
    % attention ((x2(i)-x(j-1))/(x(j)-x(j-1))) is the ratio of every step
end
if nargout == 0
    plot(x2,y2,'-r','LineWidth',2)
end

Then use the test code:

x = 0:1.2:12;    % 这个是为了说明在步长不等于1时仍然适用的
y = cos(x);
PerlinNoise_1(x,y);

One-dimensional interpolation image can be obtained:

3. The principle of two-dimensional Perlin noise interpolation

Let's talk about two-dimensional Perlin noise interpolation, which I refer to from the article [3]

For a two-dimensional grid, we still need to perform interpolation. At this time, after we divide the lattice, a three-dimensional random vector is generated for each point of the lattice (this is as long as it is random, and there is no need for too many requirements), We call it the gradient vector

Of course, a better choice of stochastic gradient vectors is from[[1,1,0];[-1,1,0];[1,-1,0];[-1,-1,0];[1,0,1];[-1,0,1];[1,0,-1]; [-1,0,-1];[0,1,1];[0,-1,1];[0,1,-1];[0,-1,-1]]

Randomly select one of these twelve vectors (in fact, corresponding to the 12 vectors from the center of a cube pointing to the midpoint of its 12 sides)

So I wrote RandomVector to generate the selected random vector: (that is, randomly generate a subscript and select)

function k = RandomVector()  % generate the vector we need 
b_ = [[1,1,0];[-1,1,0];[1,-1,0];[-1,-1,0];[1,0,1];[-1,0,1];[1,0,-1];...
    [-1,0,-1];[0,1,1];[0,-1,1];[0,1,-1];[0,-1,-1]];
a_ = randRange(1,12.99999);
k = b_(fix(a_),:);
end

Among them, I wrote randRange.m to generate random numbers within the specified range (a, b are required to specify the range, and Size is used to specify the size of the random number matrix generated within the specified range (if not filled in, a random number is returned by default, such as Fill in [3,2], then the return will be a 3*2 random number matrix, but if you only fill in one number such as Size and fill in 4, then a 4*4 square matrix of random numbers in the specified range will be generated))

function X = randRange(a,b,Size)
% generate a matrix that cantains the random number in a specify range
% the third Agrument should be a vector
% if there is just one parament in the argument "size",it will create a 
if nargin == 2
    Size = 1;
end

X = ones(Size);
i=1;

while i <= numel(X) 
    % numel is used to get the number of the items of the matrix
    % for every item in the matrix
    X(i) = a+rand(1)*(b-a);
    i=i+1;
end

end
% 当然其实这个我写的算法不是很好的,好的算法是直接用a + rand(Size)*(b-a),这样就不用for循环了

In this way, random gradient vectors can be generated

We generate random vectors (including boundaries) at each lattice point (let me illustrate with a drawing) 

So put it in the three-dimensional space and generate it with matlab, the result is as follows:

For each interpolation point in the lattice, define four distance vectors d1,d2,d3,d4:

Then we have the formula:

\begin{Bmatrix} d1 = (x-x_0,y-y_0)\\ d2 = (x-x_1,y-y_0)\\ d3 = (x-x_0,y-y_1)\\ d4 = (x-x_1,y-y_1) \end{Bmatrix}

Then define the influence of each point on the interpolation point as g\cdot d(where g, d are the gradient vector and distance vector respectively) (the figure below is from the article [3]) 

 , as the corresponding influence value. (I don't understand why it uses this as the influence value. I think it is selected randomly. You can regard s, t, u, v as the function value at the grid point)

Then, we need to perform a series of weights on s, t, u, and v to determine the z value of the final intermediate interpolation point, so we consider weighting s, t, u, and v respectively first

S_x = Smoothstep(x-x_0)  (In the code I am using Fade)

Then in terms of distribution in the x-axis (st) direction, there is

 a = s + S_x*(t-s)    This is the impact on the st column (weighted)

b = u + S_x(v-u)        This is the influence of the uv column (weighted)

Doing so is equivalent to interpolating the two rows separately

Then use S_y = Smoothstep(y-y_0), (Perlin noise in the y direction)

In this way, a and b (two rows) can be weighted and averaged in the y direction, and the weight is Sy

Right now:z = a +S_y *(b-a)

This way we can get the interpolated image:

(As usual, first upload the code and then put the picture: (I put the RandomVector below, and this one was also posted once)

% Perlin noise interpolation in the second dimension
x = 0:1:4;
y = x;
[x1,y1] = meshgrid(x,y);
dx = 0.05;


% ----------柏林噪声插值部分----------------------------------

V = zeros(3,len(x),len(y));   % 生成随机向量矩阵
for i = 1:len(x)
    for j = 1:len(y)
        V(:,i,j) = RandomVector();
    end
end

x2 = min(x):dx:max(x);
y2 = min(y):dx:max(y);

%---------V(:,i,j)代表了在i,j坐标处的向量----------
Z2 = zeros(len(x2),len(y2));  %构造初始的插值矩阵


for i = 1:len(x2)-1
    for j = 1:len(y2)-1  % 对中间的点进行插值
        % 使用floor向-inf取整
        g1 = V(:,floor(x2(i)+1),floor(y2(j)+1));  % 取第一个向量
        g2 = V(:,floor(x2(i)+2),floor(y2(j)+1));  % 取第二个向量
        g3 = V(:,floor(x2(i)+1),floor(y2(j)+2));  % 取第三个向量
        g4 = V(:,floor(x2(i)+2),floor(y2(j)+2));  
        % 注意:得到的向量是列向量
        %----------距离向量-------------------
        d1 = [x2(i)-floor(x2(i)),y2(j)-floor(y2(j)),0];
        d2 = [x2(i)-floor(x2(i)+1),y2(j)-floor(y2(j)),0];
        d3 = [x2(i)-floor(x2(i)),y2(j)-floor(y2(j)+1),0];
        d4 = [x2(i)-floor(x2(i)+1),y2(j)-floor(y2(j)+1),0];
        
        s = d1*g1 ;
        t = d2*g2 ;
        u = d3*g3 ;
        v = d4*g4 ;
        
        Sx = Fade(x2(i)-floor(x2(i)));
% in this case, the x2(i)-floor(x2(i))<1,so we can use this function to caculate it
        a = s + Sx*(t-s);
        b = u + Sx*(v-u);
        
        Sy = Fade(y2(j)-floor(y2(j)));
        Z2(i,j) = a + Sy*(b-a);
        
%         hold on
%         plot3([floor(x2(i)),floor(x2(i))+g1(1)],[floor(y2(j)),floor(y2(j))+g1(2)],[0,g1(3)],'-r','LineWidth',2)
    end
end
subplot(1,2,1)
grid off
% colormap(hot)
surf(x2,y2,Z2)
% shading interp  %这两句是呈现熔岩效果的
subplot(1,2,2)
grid off
contour(x2,y2,Z2)

%---------------------------------------------------


function k = RandomVector()  % generate the vector we need 
b_ = [[1,1,0];[-1,1,0];[1,-1,0];[-1,-1,0];[1,0,1];[-1,0,1];[1,0,-1];...
    [-1,0,-1];[0,1,1];[0,-1,1];[0,1,-1];[0,-1,-1]];
a_ = randRange(1,12.99999);
k = b_(fix(a_),:);   % fix向0取整
end

Interpolation renderings:

 You can also turn around

Or change the colormap to create a lava effect (I commented this code in the code)

 But this article is not over yet

——In order to illustrate that s, t, u, and v are random effects (you can think of it as a random function value at a certain point), so I use the s, t, u, and v of the above part to be between 3 and 4 Random numbers instead, the generation effect is as follows (to avoid the problem of array overflow, I did not set the boundary value, so the function value of 4 is 0)

According to the drawing results, the function value is between 3 and 4

Guess you like

Origin blog.csdn.net/sbsbsb666666/article/details/122978832