Perlin Noise

From Free Pascal wiki
Revision as of 15:33, 25 December 2006 by Sekelsenmat (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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.

mountain landscape.png


First Example

Noise1D.png

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.

External Links