Difference between revisions of "Perlin Noise"

From Free Pascal wiki
Jump to navigationJump to search
 
Line 15: Line 15:
  
 
==First Example==
 
==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
  
 
[[Image:Noise1D.png]]
 
[[Image:Noise1D.png]]

Revision as of 17:25, 25 December 2006

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

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.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