Difference between revisions of "BGRABitmap tutorial 9/de"

From Free Pascal wiki
Jump to navigationJump to search
m (Text replace - "delphi>" to "syntaxhighlight>")
Line 14: Line 14:
  
 
Fügen wir eine Variable in der Formulardefinition hinzu:
 
Fügen wir eine Variable in der Formulardefinition hinzu:
<delphi>TForm1 = class(TForm)  
+
<syntaxhighlight>TForm1 = class(TForm)  
 
   ...
 
   ...
   phong: TPhongShading;    </delphi>
+
   phong: TPhongShading;    </syntaxhighlight>
  
 
Wenn das Formular erzeugt wird, erzeugen wir auch die Klasse:
 
Wenn das Formular erzeugt wird, erzeugen wir auch die Klasse:
<delphi>procedure TForm1.FormCreate(Sender: TObject);
+
<syntaxhighlight>procedure TForm1.FormCreate(Sender: TObject);
 
begin
 
begin
 
   phong := TPhongShading.Create;
 
   phong := TPhongShading.Create;
Line 27: Line 27:
 
   phong.LightSourceIntensity := 250;
 
   phong.LightSourceIntensity := 250;
 
   phong.LightSourceDistanceTerm := 200;   
 
   phong.LightSourceDistanceTerm := 200;   
end;  </delphi>
+
end;  </syntaxhighlight>
  
 
Der Specularindex zeigt an, wie konzentriert das reflektierte Licht ist.
 
Der Specularindex zeigt an, wie konzentriert das reflektierte Licht ist.
  
 
Wenn das Formular zerstört wird:
 
Wenn das Formular zerstört wird:
<delphi>procedure TForm1.FormDestroy(Sender: TObject);
+
<syntaxhighlight>procedure TForm1.FormDestroy(Sender: TObject);
 
begin
 
begin
 
   phong.Free;
 
   phong.Free;
end; </delphi>
+
end; </syntaxhighlight>
  
 
Wenn das Formular gezeichnet wird, fügen wir ein phong-schattiertes Objekt ein:
 
Wenn das Formular gezeichnet wird, fügen wir ein phong-schattiertes Objekt ein:
<delphi>var
+
<syntaxhighlight>var
 
   image: TBGRABitmap;
 
   image: TBGRABitmap;
  
Line 48: Line 48:
 
     image.Draw(Canvas,0,0,True);
 
     image.Draw(Canvas,0,0,True);
 
     image.free;
 
     image.free;
end;  </delphi>
+
end;  </syntaxhighlight>
  
 
Die Parameter von DrawSphere sind das Zielbild, die Abmessungen des Objektes, die maximale Höhe und die Farbe. Der Durchmesser der Kugelist 100, also ist die maximale Höhe der Halbkugel 50.
 
Die Parameter von DrawSphere sind das Zielbild, die Abmessungen des Objektes, die maximale Höhe und die Farbe. Der Durchmesser der Kugelist 100, also ist die maximale Höhe der Halbkugel 50.
  
 
Zuletzt wäre es nett, wenn die Lichtquelle den Mauszeigerbewegungen folgen würde:
 
Zuletzt wäre es nett, wenn die Lichtquelle den Mauszeigerbewegungen folgen würde:
<delphi>procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
+
<syntaxhighlight>procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
 
   Y: Integer);
 
   Y: Integer);
 
begin
 
begin
 
   phong.LightPosition := point(X,Y);
 
   phong.LightPosition := point(X,Y);
 
   FormPaint(Sender);
 
   FormPaint(Sender);
end;  </delphi>
+
end;  </syntaxhighlight>
  
 
=== Starten Sie das Programm ===
 
=== Starten Sie das Programm ===
Line 69: Line 69:
  
 
Die folgende Prozedur erzeugt ein Stück Schokolade:
 
Die folgende Prozedur erzeugt ein Stück Schokolade:
<delphi>function CreateChocolateTexture(tx,ty: integer): TBGRABitmap;
+
<syntaxhighlight>function CreateChocolateTexture(tx,ty: integer): TBGRABitmap;
 
var
 
var
 
   square,map: TBGRABitmap;
 
   square,map: TBGRABitmap;
Line 102: Line 102:
 
   map.Free;
 
   map.Free;
 
   phong.Free;
 
   phong.Free;
end; </delphi>
+
end; </syntaxhighlight>
  
 
Der Phong-Shader benutzt eine Höhenkarte zum Darstellen der Lichteffekte. Hier enthält die Karte ein Quadrat.
 
Der Phong-Shader benutzt eine Höhenkarte zum Darstellen der Lichteffekte. Hier enthält die Karte ein Quadrat.
Line 109: Line 109:
  
 
Wenn jetzt das Formular erzeugt wird, dann erzeugen wir auch die Schokoladentextur:
 
Wenn jetzt das Formular erzeugt wird, dann erzeugen wir auch die Schokoladentextur:
<delphi>  chocolate := CreateChocolateTexture(80,80);  </delphi>
+
<syntaxhighlight>  chocolate := CreateChocolateTexture(80,80);  </syntaxhighlight>
  
 
Und wenn das Formular zerstört wird:
 
Und wenn das Formular zerstört wird:
<delphi>  chocolate.Free; </delphi>
+
<syntaxhighlight>  chocolate.Free; </syntaxhighlight>
  
 
Fügen Sie im OnPaint-Ereignis vor 'phong.DrawSphere' folgende Zeile ein:
 
Fügen Sie im OnPaint-Ereignis vor 'phong.DrawSphere' folgende Zeile ein:
<delphi>    image.FillRect(0,0,80*7,80*4,chocolate,dmSet);  </delphi>
+
<syntaxhighlight>    image.FillRect(0,0,80*7,80*4,chocolate,dmSet);  </syntaxhighlight>
  
 
=== Resultierender Code ===
 
=== Resultierender Code ===
  
<delphi>unit UMain;
+
<syntaxhighlight>unit UMain;
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
Line 221: Line 221:
 
   {$I UMain.lrs}
 
   {$I UMain.lrs}
  
end.    </delphi>
+
end.    </syntaxhighlight>
  
 
=== Starten Sie das Programm ===
 
=== Starten Sie das Programm ===
Line 233: Line 233:
 
Die Idee dahinter ist, eine Höhenkarte mit dem Perlinrauschen zu erzeugen und dann zur Darstellung die Phong-Schattierung anzuwenden. Damit erzeugen wir hier eine Steintextur:
 
Die Idee dahinter ist, eine Höhenkarte mit dem Perlinrauschen zu erzeugen und dann zur Darstellung die Phong-Schattierung anzuwenden. Damit erzeugen wir hier eine Steintextur:
  
<delphi>  function CreateStoneTexture(tx,ty: integer): TBGRABitmap;
+
<syntaxhighlight>  function CreateStoneTexture(tx,ty: integer): TBGRABitmap;
 
   var
 
   var
 
     temp: TBGRABitmap;
 
     temp: TBGRABitmap;
Line 252: Line 252:
 
     phong.Free;
 
     phong.Free;
 
     temp.Free;
 
     temp.Free;
   end;  </delphi>
+
   end;  </syntaxhighlight>
  
 
Zuerst erzeugen wir eine zyklische Karte. Zyklisch ist wichtig für eine anreihbare Textur. Aber wenn wir die Phong-Schattierung anwenden wollen, müssen wir dem Shader diesen Zyklus bekanntgeben. Also extrahieren wir mittels Getpart die erzeugte Karte mit 2 zusätzlichen Pixels zu jeder Seite, sodass der Shader zyklisch auf die Karte angewendet werden kann.
 
Zuerst erzeugen wir eine zyklische Karte. Zyklisch ist wichtig für eine anreihbare Textur. Aber wenn wir die Phong-Schattierung anwenden wollen, müssen wir dem Shader diesen Zyklus bekanntgeben. Also extrahieren wir mittels Getpart die erzeugte Karte mit 2 zusätzlichen Pixels zu jeder Seite, sodass der Shader zyklisch auf die Karte angewendet werden kann.
Line 259: Line 259:
  
 
Jetzt lautet das OnPaint-Ereignis:
 
Jetzt lautet das OnPaint-Ereignis:
<delphi>procedure TForm1.FormPaint(Sender: TObject);
+
<syntaxhighlight>procedure TForm1.FormPaint(Sender: TObject);
 
var
 
var
 
   image: TBGRABitmap;
 
   image: TBGRABitmap;
Line 272: Line 272:
 
     image.Draw(Canvas,0,0,True);
 
     image.Draw(Canvas,0,0,True);
 
     image.free;
 
     image.free;
end;</delphi>
+
end;</syntaxhighlight>
  
 
=== Starten Sie das Programm ===
 
=== Starten Sie das Programm ===
Line 284: Line 284:
 
Fast die gleiche Prozedur erzeugt uns eine Wassertextur:
 
Fast die gleiche Prozedur erzeugt uns eine Wassertextur:
  
<delphi>function CreateWaterTexture(tx,ty: integer): TBGRABitmap;
+
<syntaxhighlight>function CreateWaterTexture(tx,ty: integer): TBGRABitmap;
 
const blurSize = 5;
 
const blurSize = 5;
 
var
 
var
Line 307: Line 307:
 
   phong.Free;
 
   phong.Free;
 
   temp.Free;
 
   temp.Free;
end; </delphi>
+
end; </syntaxhighlight>
  
 
Der Hauptunterschied ist, dass wir einen Weichzeichner auf das Wasser anwenden und die Lichtfarbe anders setzen.
 
Der Hauptunterschied ist, dass wir einen Weichzeichner auf das Wasser anwenden und die Lichtfarbe anders setzen.
Line 316: Line 316:
  
 
Wenn wir uns auf auf einen kleinen Bereich von Höhen beschränken, haben wir eine Textur, die Fussabdrücken im Schnee ähnelt.
 
Wenn wir uns auf auf einen kleinen Bereich von Höhen beschränken, haben wir eine Textur, die Fussabdrücken im Schnee ähnelt.
<delphi>function CreateSnowPrintTexture(tx,ty: integer): TBGRABitmap;
+
<syntaxhighlight>function CreateSnowPrintTexture(tx,ty: integer): TBGRABitmap;
 
var
 
var
 
   v: integer;
 
   v: integer;
Line 355: Line 355:
 
   phong.Free;
 
   phong.Free;
 
   temp.Free;
 
   temp.Free;
end;  </delphi>
+
end;  </syntaxhighlight>
  
 
Wir erhalten dies:
 
Wir erhalten dies:

Revision as of 13:55, 24 March 2012

Deutsch (de) English (en) français (fr) русский (ru)


Home | Tutorial 1 | Tutorial 2 | Tutorial 3 | Tutorial 4 | Tutorial 5 | Tutorial 6 | Tutorial 7 | Tutorial 8 | Tutorial 9 | Tutorial 10 | Tutorial 11 | Tutorial 12 | Tutorial 13 | Tutorial 14 | Tutorial 15 | Tutorial 16 | Edit

Dieses Tutorial zeigt, wie Sie mit der Phong-Schattierung Texturen erzeugen können.

Erzeugen Sie ein neues Projekt

Erzeugen Sie ein neues Projekt und fügen Sie eine Referenz auf BGRABitmap hinzu, genau so wie im ersten Tutorial.

Phong-Schattierung und Beleuchtung

Um die Phong-Schattierung zu benutzen, müssen Sie eine TPhongShading-Klasse instantiieren. Diese befindet sich in der Unit 'BGRAGradients'.

Fügen wir eine Variable in der Formulardefinition hinzu:

TForm1 = class(TForm) 
  ...
  phong: TPhongShading;

Wenn das Formular erzeugt wird, erzeugen wir auch die Klasse:

procedure TForm1.FormCreate(Sender: TObject);
begin
  phong := TPhongShading.Create;
  phong.LightPositionZ := 150;
  phong.SpecularIndex := 20;
  phong.AmbientFactor := 0.4;
  phong.LightSourceIntensity := 250;
  phong.LightSourceDistanceTerm := 200;  
end;

Der Specularindex zeigt an, wie konzentriert das reflektierte Licht ist.

Wenn das Formular zerstört wird:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  phong.Free;
end;

Wenn das Formular gezeichnet wird, fügen wir ein phong-schattiertes Objekt ein:

var
  image: TBGRABitmap;

begin
    image := TBGRABitmap.Create(ClientWidth,ClientHeight,ColorToBGRA(ColorToRGB(clBtnFace)));

    phong.DrawSphere(image,rect(20,20,120,120),50,BGRA(255,0,0));

    image.Draw(Canvas,0,0,True);
    image.free;
end;

Die Parameter von DrawSphere sind das Zielbild, die Abmessungen des Objektes, die maximale Höhe und die Farbe. Der Durchmesser der Kugelist 100, also ist die maximale Höhe der Halbkugel 50.

Zuletzt wäre es nett, wenn die Lichtquelle den Mauszeigerbewegungen folgen würde:

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  phong.LightPosition := point(X,Y);
  FormPaint(Sender);
end;

Starten Sie das Programm

Sie sollten mit dem Licht auf der Kugel spielen können:

BGRATutorial9a.png

Verwenden Sie die Phong-Schattierung, um Texturen zu erzeugen

Die folgende Prozedur erzeugt ein Stück Schokolade:

function CreateChocolateTexture(tx,ty: integer): TBGRABitmap;
var
  square,map: TBGRABitmap;
  phong: TPhongShading;
  margin: integer;
begin
  margin := tx div 20; //empty space around the square
  square := CreateRectangleMap(tx-2*margin,ty-2*margin,tx div 8);

  //erzeugt eine Bitmap mit einem Quadrat in der Mitte
  map := TBGRABitmap.Create(tx,ty,BGRABlack);
  map.PutImage(margin,margin,square,dmDrawWithTransparency);

  //durch Verwischen erscheint es weicher
  BGRAReplace(map,map.FilterBlurRadial(tx div 40,rbFast));
  square.free;

  //erzeugt die resultierende Bitmap
  result := TBGRABitmap.Create(tx,ty);

  //benutzt die Phong-Schattierung
  phong := TPhongShading.Create;
  phong.LightSourceDistanceFactor := 0;
  phong.LightDestFactor := 0;
  phong.LightSourceIntensity := 200;
  phong.AmbientFactor := 0.5;
  phong.LightPosition := Point(-50,-100);
  phong.LightPositionZ := 80;

  //zeichnet ein Stück Schokolade mit maximaler Höhe 20
  phong.Draw(result,map,20,0,0,BGRA(86,41,38));
  map.Free;
  phong.Free;
end;

Der Phong-Shader benutzt eine Höhenkarte zum Darstellen der Lichteffekte. Hier enthält die Karte ein Quadrat.

Unter den Eigenschaften des Phong-Shaders gibt es 'LightSourceDistanceFactor' und 'LightDestFactor'. Setzt man diese Werte auf 0 wird das Ergebnis anreihbar. Genau genommen: Wenn der DistanceFactor 0 ist, bleibt die Distanz zwischen der Lichtquelle und dem Objekt unberücksichtigt. Und wenn der LightDestinationFactor 0 ist, dann bleibt die Position des Objektes beim Berechnen des Lichteinfallswinkels unberücksichtigt.

Wenn jetzt das Formular erzeugt wird, dann erzeugen wir auch die Schokoladentextur:

  chocolate := CreateChocolateTexture(80,80);

Und wenn das Formular zerstört wird:

  chocolate.Free;

Fügen Sie im OnPaint-Ereignis vor 'phong.DrawSphere' folgende Zeile ein:

    image.FillRect(0,0,80*7,80*4,chocolate,dmSet);

Resultierender Code

unit UMain;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  ExtCtrls, Buttons, BGRABitmap, BGRABitmapTypes, BGRAGradients;

type
  { TForm1 }

  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FormPaint(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    phong: TPhongShading;
    chocolate: TBGRABitmap;
  end; 

var
  Form1: TForm1; 

implementation

function CreateChocolateTexture(tx,ty: integer): TBGRABitmap;
var
  square,map: TBGRABitmap;
  phong: TPhongShading;
  margin: integer;
begin
  margin := tx div 20;
  square := CreateRectangleMap(tx-2*margin,ty-2*margin,tx div 8);
  map := TBGRABitmap.Create(tx,ty,BGRABlack);
  map.PutImage(margin,margin,square,dmDrawWithTransparency);
  BGRAReplace(map,map.FilterBlurRadial(tx div 40,rbFast));
  square.free;

  result := TBGRABitmap.Create(tx,ty);
  phong := TPhongShading.Create;
  phong.LightSourceDistanceFactor := 0;
  phong.LightDestFactor := 0;
  phong.LightSourceIntensity := 200;
  phong.AmbientFactor := 0.5;
  phong.LightPosition := Point(-50,-100);
  phong.LightPositionZ := 80;
  phong.Draw(result,map,20,0,0,BGRA(86,41,38));
  map.Free;
  phong.Free;
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  phong := TPhongShading.Create;
  phong.LightPositionZ := 150;
  phong.SpecularIndex := 20;
  phong.AmbientFactor := 0.4;
  phong.LightSourceIntensity := 250;
  phong.LightSourceDistanceTerm := 200;

  chocolate := CreateChocolateTexture(80,80);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  phong.Free;
  chocolate.Free;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  phong.LightPosition := point(X,Y);
  FormPaint(Sender);
end;


procedure TForm1.FormPaint(Sender: TObject);
var
  image: TBGRABitmap;
begin
    image := TBGRABitmap.Create(ClientWidth,ClientHeight,ColorToBGRA(ColorToRGB(clBtnFace)));

    image.FillRect(0,0,80*7,80*4,chocolate,dmSet);
    phong.DrawSphere(image,rect(20,20,120,120),50,BGRA(255,0,0));

    image.Draw(Canvas,0,0,True);
    image.free;
end;

initialization
  {$I UMain.lrs}

end.

Starten Sie das Programm

Sie sehen hoffentlich eine leckere Tafel Schokolade mit einer großen Kirsche:

BGRATutorial9b.png

Gemeinsame Nutzung von Perlin noise und Phong-Schattierung

Die Idee dahinter ist, eine Höhenkarte mit dem Perlinrauschen zu erzeugen und dann zur Darstellung die Phong-Schattierung anzuwenden. Damit erzeugen wir hier eine Steintextur:

  function CreateStoneTexture(tx,ty: integer): TBGRABitmap;
  var
    temp: TBGRABitmap;
    phong: TPhongShading;
  begin
    result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,0.6);
    temp:= result.GetPart(rect(-2,-2,tx+2,ty+2)) as TBGRABitmap;

    phong := TPhongShading.Create;
    phong.LightSourceDistanceFactor := 0;
    phong.LightDestFactor := 0;
    phong.LightSourceIntensity := 100;
    phong.LightPositionZ := 100;
    phong.NegativeDiffusionFactor := 0.3;
    phong.AmbientFactor := 0.5;
    phong.Draw(result,temp,30,-2,-2,BGRA(170,170,170));

    phong.Free;
    temp.Free;
  end;

Zuerst erzeugen wir eine zyklische Karte. Zyklisch ist wichtig für eine anreihbare Textur. Aber wenn wir die Phong-Schattierung anwenden wollen, müssen wir dem Shader diesen Zyklus bekanntgeben. Also extrahieren wir mittels Getpart die erzeugte Karte mit 2 zusätzlichen Pixels zu jeder Seite, sodass der Shader zyklisch auf die Karte angewendet werden kann.

Der Aufruf von 'phong.Draw' mit einem Offset (-2,-2) stellt die Karte am rechten Ort dar und berücksichtigt auch unsere zwei zusätzlichen Pixel.

Jetzt lautet das OnPaint-Ereignis:

procedure TForm1.FormPaint(Sender: TObject);
var
  image: TBGRABitmap;
  stone: TBGRABitmap;
begin
    image := TBGRABitmap.Create(ClientWidth,ClientHeight,ColorToBGRA(ColorToRGB(clBtnFace)));

    stone := CreateStoneTexture(100,100);
    image.FillEllipseAntialias(200,100,150,50,stone);
    stone.free;

    image.Draw(Canvas,0,0,True);
    image.free;
end;

Starten Sie das Programm

Sie sehen ein Formular mit einem steinartigen Hintergrund.

BGRATutorial9c.png

Darstellung von Wasser

Fast die gleiche Prozedur erzeugt uns eine Wassertextur:

function CreateWaterTexture(tx,ty: integer): TBGRABitmap;
const blurSize = 5;
var
  temp: TBGRABitmap;
  phong: TPhongShading;
begin
  result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1.2);
  temp:= result.GetPart(rect(-blurSize,-blurSize,tx+blurSize,ty+blurSize)) as TBGRABitmap;
  BGRAReplace(temp,temp.FilterBlurRadial(blurSize,rbFast));

  phong := TPhongShading.Create;
  phong.LightSourceDistanceFactor := 0;
  phong.LightDestFactor := 0;
  phong.LightSourceIntensity := 150;
  phong.LightPositionZ := 80;
  phong.LightColor := BGRA(105,233,240);
  phong.NegativeDiffusionFactor := 0.3;
  phong.SpecularIndex := 20;
  phong.AmbientFactor := 0.4;

  phong.Draw(result,temp,20,-blurSize,-blurSize,BGRA(28,139,166));
  phong.Free;
  temp.Free;
end;

Der Hauptunterschied ist, dass wir einen Weichzeichner auf das Wasser anwenden und die Lichtfarbe anders setzen.

BGRATutorial9d.png

Verwendung von Schwellenwerten liefert Spuren im Schnee

Wenn wir uns auf auf einen kleinen Bereich von Höhen beschränken, haben wir eine Textur, die Fussabdrücken im Schnee ähnelt.

function CreateSnowPrintTexture(tx,ty: integer): TBGRABitmap;
var
  v: integer;
  p: PBGRAPixel;
  i: Integer;

  temp: TBGRABitmap;
  phong: TPhongShading;
begin
  //hier wird eine Zufallskarte erzeugt
  result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1.2);

  //jetzt die Schwellenwerte anwenden
  p := result.Data;
  for i := 0 to result.NbPixels-1 do
  begin
    v := p^.red;
    //wenn der Wert über 80 oder unter 50 ist, dividieren wir durch 10, dadurch wird es fast horizontal
    if v > 80 then v := (v-80) div 10+80;
    if v < 50 then v := 50-(50-v) div 10;
    p^.red := v;
    p^.green := v;
    p^.blue := v;
    inc(p);
  end;

  //wir geben dem Phong-Shader den Zyklus bekannt
  temp:= result.GetPart(rect(-2,-2,tx+2,ty+2)) as TBGRABitmap;
  //einen radialen Weichzeichner anwenden
  BGRAReplace(temp,temp.FilterBlurRadial(2,rbFast));
  phong := TPhongShading.Create;
  phong.LightSourceDistanceFactor := 0;
  phong.LightDestFactor := 0;
  phong.LightSourceIntensity := 100;
  phong.LightPositionZ := 100;
  phong.NegativeDiffusionFactor := 0.3; //ergibt Schatten
  phong.Draw(result,temp,30,-2,-2,BGRAWhite);
  phong.Free;
  temp.Free;
end;

Wir erhalten dies:

BGRATutorial9e.png

Voriges Tutorial (Texturen) | Nächstes Tutorial (Textur-Transformationen)