Perlin Noise
│
English (en) │
français (fr) │
中文(中国大陆) (zh_CN) │
This page is the start of a tutorial about using Perlin Noise on LCL applications to generate natural looking images. It will cover both basic theory and real usage examples, with a focus on compilable examples.
Perlin Noise was invented by [Ken Perlin|http://mrl.nyu.edu/~perlin/] to generate textures for a movie called Tron. Today it is widely used on movies and video games to produce natural looking smoke, landscapes, clouds and any texture including marble, irregular glass, etc.
Getting Started
Perlin Noise is based on the idea of fractals, that things in nature show different degrees of change. On a rocky mountain landscape for example when can see changes with a very big amplitude, which are the mountains themselves. Smaller changes represent irregularities on those mountains and even smaller ones represent rocks.
First Example
This application demonstrates a simple noise function with the following properties:
- Only 1 harmonic present
- Amplitude of 256 pixels
- Wavelength of 16 pixels
- Frequency of 0.05
- You can use a combo box to choose between Linear, Cossine and Cubic interpolation
Noise1D.dpr file:
program noise1d; {$ifdef fpc} {$mode delphi} {$endif} uses {$ifdef fpc} Interfaces, // this includes the LCL widgetset {$endif} Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, noise; type { TMainWindow } TMainWindow = class(TForm) private { private declarations } SelectInterpolation: TComboBox; procedure DoPaint(Sender: TObject); procedure DoRefresh(Sender: TObject); function NormalizeNoise(x: Double): Integer; public { public declarations } constructor Create(AOwner: TComponent); override; destructor Destroy; override; end; var vMainWindow: TMainWindow; { TMainWindow } procedure TMainWindow.DoPaint(Sender: TObject); var i, j, interpolation: Integer; begin { Draws rulers } Canvas.MoveTo(25, 25 ); Canvas.LineTo(25, 275); Canvas.LineTo(275, 275); { Draws 12 points and the interpolation between them } for i := 0 to 11 do begin Canvas.Ellipse(i * 20 + 25 + 1, NormalizeNoise(IntNoise(i)) + 1, i * 20 + 25 - 1, NormalizeNoise(IntNoise(i)) - 1); if (i = 11) then Continue; for j := 1 to 19 do begin case SelectInterpolation.ItemIndex of 0: interpolation := Linear_Interpolate(NormalizeNoise(IntNoise(i)), NormalizeNoise(IntNoise(i + 1)), j / 20); 1: interpolation := Cosine_Interpolate(NormalizeNoise(IntNoise(i)), NormalizeNoise(IntNoise(i + 1)), j / 20); else interpolation := Cubic_Interpolate(NormalizeNoise(IntNoise(i - 1)), NormalizeNoise(IntNoise(i)), NormalizeNoise(IntNoise(i + 1)), NormalizeNoise(IntNoise(i + 2)), j / 20); end; Canvas.Pixels[i * 20 + 25 + j, interpolation] := clBlack; end; end; end; procedure TMainWindow.DoRefresh(Sender: TObject); begin Repaint; end; function TMainWindow.NormalizeNoise(x: Double): Integer; begin Result := Round( 25 + (x + 1.0) * 125 ); end; constructor TMainWindow.Create(AOwner: TComponent); begin inherited Create(AOwner); Position := poScreenCenter; Width := 300; Height := 300; Caption := 'Noise 1D'; OnPaint := DoPaint; SelectInterpolation := TComboBox.Create(Self); SelectInterpolation.Parent := Self; SelectInterpolation.Items.Add('Linear Interpolation'); SelectInterpolation.Items.Add('Cosine Interpolation'); SelectInterpolation.Items.Add('Cubic Interpolation'); SelectInterpolation.Left := 100; SelectInterpolation.Width := 200; SelectInterpolation.ItemIndex := 0; SelectInterpolation.OnChange := DoRefresh; end; destructor TMainWindow.Destroy; begin inherited Destroy; end; begin Application.Initialize; Application.CreateForm(TMainWindow, vMainWindow); Application.Run; end.
noise.pas file:
unit noise; {$ifdef fpc} {$mode delphi} {$endif} interface uses Classes, SysUtils; function IntNoise(x: Integer): Double; function Linear_Interpolate(a, b: Integer; x: Double): Integer; function Cosine_Interpolate(a, b: Integer; x: Double): Integer; function Cubic_Interpolate(v0, v1, v2, v3: Integer; x: Double): Integer; implementation function IntNoise(x: Integer): Double; var xl: Integer; begin xl := (x shl 13) xor x; Result := (xl * (xl * xl * 15731 + 789221) + 1376312589) and $7fffffff; Result := 1.0 - (Result / 1073741824.0); end; function Linear_Interpolate(a, b: Integer; x: Double): Integer; begin Result := Round(a * (1-x) + b * x); end; function Cosine_Interpolate(a, b: Integer; x: Double): Integer; var f, ft: Double; begin ft := x * Pi; f := (1.0 - cos(ft)) * 0.5; Result := Round( a * (1 - f) + b * f ); end; function Cubic_Interpolate(v0, v1, v2, v3: Integer; x: Double): Integer; var P, Q, R, S: Double; begin P := (v3 - v2) - (v0 - v1); Q := (v0 - v1) - P; R := v2 - v0; S := v1; Result := Round( P * x * x * x + Q * x * x + R * x + S ); end; end.