Difference between revisions of "BGRABitmap tutorial 8/de"

From Free Pascal wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; deleted category already in page template)
 
(2 intermediate revisions by 2 users not shown)
Line 14: Line 14:
  
 
Im Objektinspektor fügen Sie einen OnPaint-Handler hinzu und schreiben Sie:
 
Im Objektinspektor fügen Sie einen OnPaint-Handler hinzu und schreiben Sie:
<delphi>procedure TForm1.FormPaint(Sender: TObject);
+
<syntaxhighlight lang="pascal">procedure TForm1.FormPaint(Sender: TObject);
 
var
 
var
 
   image,tex: TBGRABitmap;
 
   image,tex: TBGRABitmap;
Line 40: Line 40:
 
     image.Draw(Canvas,0,0,True);
 
     image.Draw(Canvas,0,0,True);
 
     image.free;   
 
     image.free;   
end;</delphi>
+
end;</syntaxhighlight>
  
 
Wie Sie sehen ist eine Textur einfach eine Bitmap. Um eine Ellipse mit einer Textur zu füllen, übergeben Sie die Textur einfach als Parameter anstelle der Farbe.
 
Wie Sie sehen ist eine Textur einfach eine Bitmap. Um eine Ellipse mit einer Textur zu füllen, übergeben Sie die Textur einfach als Parameter anstelle der Farbe.
Line 49: Line 49:
  
 
Fügen Sie die folgenden Zeilen ein vor 'tex.Free' :
 
Fügen Sie die folgenden Zeilen ein vor 'tex.Free' :
<delphi>    image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,c,11);
+
<syntaxhighlight lang="pascal">    image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,c,11);
     image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,tex,9); </delphi>
+
     image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,tex,9); </syntaxhighlight>
  
 
Der erste Befehl zeichnet ein breites, abgerundetes Rechteck (mit Breite 11), das den Rahmen enthält. Der zweite Befehl füllt mit einer Textur und einer geringeren Breite (9). Das klappt perfekt, solange die Textur nicht transparent ist.
 
Der erste Befehl zeichnet ein breites, abgerundetes Rechteck (mit Breite 11), das den Rahmen enthält. Der zweite Befehl füllt mit einer Textur und einer geringeren Breite (9). Das klappt perfekt, solange die Textur nicht transparent ist.
Line 64: Line 64:
 
==== Basic Perlin noise map ====
 
==== Basic Perlin noise map ====
  
Es ist möglich, anreihbare zufällige Texturen zu erzeugen mit der Funktion 'CreateCyclicPerlinNoiseMap', die Sie in der Unit 'BGRAGradient' finden.
+
Es ist möglich, anreihbare zufällige Texturen zu erzeugen mit der Funktion 'CreateCyclicPerlinNoiseMap', die Sie in der Unit 'BGRAGradients' finden.
  
 
Im Objektinspektor definieren Sie einen OnPaint-Handler mit:
 
Im Objektinspektor definieren Sie einen OnPaint-Handler mit:
<delphi>procedure TForm1.FormPaint(Sender: TObject);
+
<syntaxhighlight lang="pascal">procedure TForm1.FormPaint(Sender: TObject);
 
var
 
var
 
   image,tex: TBGRABitmap;
 
   image,tex: TBGRABitmap;
Line 75: Line 75:
  
 
     tex := CreateCyclicPerlinNoiseMap(100,100);
 
     tex := CreateCyclicPerlinNoiseMap(100,100);
     image.FillRect(0,0,image.Width,image.Height, tex);
+
     image.FillRect(0,0,image.Width,image.Height, tex, dmset);
 
     tex.free;
 
     tex.free;
  
 
     image.Draw(Canvas,0,0,True);
 
     image.Draw(Canvas,0,0,True);
 
     image.free;   
 
     image.free;   
end;</delphi>
+
end;</syntaxhighlight>
  
 
Dies erzeugt eine 100x100 Textur und füllt damit das Formular. Sie erhalten etwas ähnliches wie:
 
Dies erzeugt eine 100x100 Textur und füllt damit das Formular. Sie erhalten etwas ähnliches wie:
Line 89: Line 89:
  
 
Dies ist zu sehr schwarz-weiß. Geben wir etwas Farbe hinein. Dazu brauchen wir eine Funktion, die die Werte interpoliert. Hier ist sie:
 
Dies ist zu sehr schwarz-weiß. Geben wir etwas Farbe hinein. Dazu brauchen wir eine Funktion, die die Werte interpoliert. Hier ist sie:
<delphi>  function Interp256(value1,value2,position: integer): integer; inline;
+
<syntaxhighlight lang="pascal">  function Interp256(value1,value2,position: integer): integer; inline;
 
   begin
 
   begin
 
       result := (value1*(256-position) + value2*position) shr 8;
 
       result := (value1*(256-position) + value2*position) shr 8;
   end;</delphi>
+
   end;</syntaxhighlight>
 
Diese Funktion berechnet einen Wert, der von Wert1 bis zu Wert2 läuft. Position ist eine Zahl zwischen 0 und 256 die anzeigt, wie nahe das Ergebnis bei Wert2 liegt. Der Ausdruck "shr 8" ist das optimierte Äquivalent zu "div 256" für positive Werte. Es ist eine binäre Verschiebung um 8 Stellen.
 
Diese Funktion berechnet einen Wert, der von Wert1 bis zu Wert2 läuft. Position ist eine Zahl zwischen 0 und 256 die anzeigt, wie nahe das Ergebnis bei Wert2 liegt. Der Ausdruck "shr 8" ist das optimierte Äquivalent zu "div 256" für positive Werte. Es ist eine binäre Verschiebung um 8 Stellen.
  
 
Wir wollen Farben interpolieren, also schreiben wir eine Funktion, die Farben interpoliert:
 
Wir wollen Farben interpolieren, also schreiben wir eine Funktion, die Farben interpoliert:
<delphi>  function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline;
+
<syntaxhighlight lang="pascal">  function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline;
 
   begin
 
   begin
 
       result.red := Interp256(color1.red,color2.red, position);
 
       result.red := Interp256(color1.red,color2.red, position);
Line 102: Line 102:
 
       result.blue := Interp256(color1.blue,color2.blue, position);
 
       result.blue := Interp256(color1.blue,color2.blue, position);
 
       result.alpha := Interp256(color1.alpha,color2.alpha, position);
 
       result.alpha := Interp256(color1.alpha,color2.alpha, position);
   end;</delphi>
+
   end;</syntaxhighlight>
 
Das ist geradlinig: jeder Farbanteil wird zwischen color1 und color2 interpoliert.
 
Das ist geradlinig: jeder Farbanteil wird zwischen color1 und color2 interpoliert.
  
 
Jetzt haben wir alles, um es uns etwas bunter zu machen. Nach 'CreatePerlinNoiseMap' fügen Sie folgende Zeilen ein:
 
Jetzt haben wir alles, um es uns etwas bunter zu machen. Nach 'CreatePerlinNoiseMap' fügen Sie folgende Zeilen ein:
<delphi>    p := tex.Data;
+
<syntaxhighlight lang="pascal">    p := tex.Data;
 
     for i := 0 to tex.NbPixels-1 do
 
     for i := 0 to tex.NbPixels-1 do
 
     begin
 
     begin
 
       p^ := Interp256( BGRA(0,128,0), BGRA(192,255,0), p^.red );
 
       p^ := Interp256( BGRA(0,128,0), BGRA(192,255,0), p^.red );
 
       inc(p);
 
       inc(p);
     end;  </delphi>
+
     end;  </syntaxhighlight>
 
Sie brauchen die Variablen 'p' und 'i', also klicken Sie auf eine jede und drücken Sie Strg-Umsch-C.
 
Sie brauchen die Variablen 'p' und 'i', also klicken Sie auf eine jede und drücken Sie Strg-Umsch-C.
  
Line 123: Line 123:
  
 
Anstatt ständig zu variieren, kann die Farbänderung einen Schwellenwert berücksichtigen. Im Beispiel ergibt das ein Meer mit Inseln:
 
Anstatt ständig zu variieren, kann die Farbänderung einen Schwellenwert berücksichtigen. Im Beispiel ergibt das ein Meer mit Inseln:
<delphi>    p := tex.Data;
+
<syntaxhighlight lang="pascal">    p := tex.Data;
 
     for i := 0 to tex.NbPixels-1 do
 
     for i := 0 to tex.NbPixels-1 do
 
     begin
 
     begin
Line 130: Line 130:
 
         p^ := BGRA(0,128,196); //sea
 
         p^ := BGRA(0,128,196); //sea
 
       inc(p);
 
       inc(p);
     end;  </delphi>
+
     end;  </syntaxhighlight>
  
 
Wir nehmen mehrere Schwellenwerte. Fertig ist der Tarnanzug:
 
Wir nehmen mehrere Schwellenwerte. Fertig ist der Tarnanzug:
<delphi>    p := result.Data;
+
<syntaxhighlight lang="pascal">    p := result.Data;
 
     for i := 0 to result.NbPixels-1 do
 
     for i := 0 to result.NbPixels-1 do
 
     begin
 
     begin
Line 142: Line 142:
 
         p^:= BGRA(161,157,121);
 
         p^:= BGRA(161,157,121);
 
       inc(p);
 
       inc(p);
     end;  </delphi>
+
     end;  </syntaxhighlight>
  
 
[[Image:BGRATutorial8d.png]]
 
[[Image:BGRATutorial8d.png]]
Line 150: Line 150:
 
Hier wenden wir eine Sinusfunktion für die Noise-Werte an. Erzeugen wir uns dazu eine Prozedur:
 
Hier wenden wir eine Sinusfunktion für die Noise-Werte an. Erzeugen wir uns dazu eine Prozedur:
  
<delphi>
+
<syntaxhighlight lang="pascal">
 
   function CreateCustomTexture(tx,ty: integer): TBGRABitmap;
 
   function CreateCustomTexture(tx,ty: integer): TBGRABitmap;
 
   var
 
   var
Line 166: Line 166:
 
     end;
 
     end;
 
   end;
 
   end;
</delphi>
+
</syntaxhighlight>
 
ColorOscillation ist ein Wert zwischen 0 und 256. Er wird aus der Intensität von (p^.red) berechnet. Hier wenden wir die Sinusfunktion mit einer halben Periode von 32 an. Dies liefert uns eine Zahl zwischen -1 und 1. Um sie auf den Bereich 0..1 abzubilden, addieren wir 1 und dividieren durch 2. Zuletzt multiplizieren wir mit 256 und haben eine Ganzzahl für Interp256.
 
ColorOscillation ist ein Wert zwischen 0 und 256. Er wird aus der Intensität von (p^.red) berechnet. Hier wenden wir die Sinusfunktion mit einer halben Periode von 32 an. Dies liefert uns eine Zahl zwischen -1 und 1. Um sie auf den Bereich 0..1 abzubilden, addieren wir 1 und dividieren durch 2. Zuletzt multiplizieren wir mit 256 und haben eine Ganzzahl für Interp256.
  
 
Die OnPaint-Prozedur wird einfacher:
 
Die OnPaint-Prozedur wird einfacher:
<delphi>
+
<syntaxhighlight lang="pascal">
 
var
 
var
 
   image,tex: TBGRABitmap;
 
   image,tex: TBGRABitmap;
Line 185: Line 185:
 
     image.free;
 
     image.free;
 
end;  
 
end;  
</delphi>
+
</syntaxhighlight>
  
 
Sie erhalten etwas in der Art:
 
Sie erhalten etwas in der Art:
Line 192: Line 192:
  
 
Jetzt wollen wir, dass es wie Marmor ausschaut, wir brauchen weniger Schwingungen. Zum Beispiel können wir eine halbe Periode von 80 nehmen. Bei Marmor sind die dunklen Stellen sehr dünn. Wir verzerren die Schwingung durch Anwendung der Potenzfunktion: ein Exponent zwischen 0 und 1 bringt die Werte näher an 1 und ein Exponent größer als 1 bringt die Werte näher an 0. So ändern wir die Schwingung in 'CreateCustomTexture':
 
Jetzt wollen wir, dass es wie Marmor ausschaut, wir brauchen weniger Schwingungen. Zum Beispiel können wir eine halbe Periode von 80 nehmen. Bei Marmor sind die dunklen Stellen sehr dünn. Wir verzerren die Schwingung durch Anwendung der Potenzfunktion: ein Exponent zwischen 0 und 1 bringt die Werte näher an 1 und ein Exponent größer als 1 bringt die Werte näher an 0. So ändern wir die Schwingung in 'CreateCustomTexture':
<delphi>    colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256); </delphi>
+
<syntaxhighlight lang="pascal">    colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256); </syntaxhighlight>
  
 
Jetzt haben wir etwas marmorartiges:
 
Jetzt haben wir etwas marmorartiges:
Line 201: Line 201:
  
 
Auch eine Holztextur erzielen wir mit Sinusfunktionen. Holztexturen enthalten zwei Schwingungen. Eine mit hellen Farben und eine weitere mit dunklen Farben. Deshalb wenden wir eine globale Variation auf diese Schwingungen an:
 
Auch eine Holztextur erzielen wir mit Sinusfunktionen. Holztexturen enthalten zwei Schwingungen. Eine mit hellen Farben und eine weitere mit dunklen Farben. Deshalb wenden wir eine globale Variation auf diese Schwingungen an:
<delphi>  function CreateWoodTexture(tx,ty: integer): TBGRABitmap;
+
<syntaxhighlight lang="pascal">  function CreateWoodTexture(tx,ty: integer): TBGRABitmap;
 
   var
 
   var
 
     colorOscillation, globalColorVariation: integer;
 
     colorOscillation, globalColorVariation: integer;
Line 217: Line 217:
 
       inc(p);
 
       inc(p);
 
     end;
 
     end;
   end;</delphi>
+
   end;</syntaxhighlight>
  
 
Hier ist die Halbperiode 16, und die globale Variation ist einfach die Intensität. Das Ergebnis sieht etwa so aus:
 
Hier ist die Halbperiode 16, und die globale Variation ist einfach die Intensität. Das Ergebnis sieht etwa so aus:
Line 224: Line 224:
  
 
Meistens ist eine Holztextur entlang einer Achse ausgerichtet. Dazu müssen wir, anstatt die Intensität als globale Position zu verwenden, sie  mit der x-Position kombinieren:
 
Meistens ist eine Holztextur entlang einer Achse ausgerichtet. Dazu müssen wir, anstatt die Intensität als globale Position zu verwenden, sie  mit der x-Position kombinieren:
<delphi>  function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap;
+
<syntaxhighlight lang="pascal">  function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap;
 
   var
 
   var
 
     globalPos: single;
 
     globalPos: single;
Line 246: Line 246:
 
       if x = tx then x := 0;
 
       if x = tx then x := 0;
 
     end;
 
     end;
   end; </delphi>
+
   end; </syntaxhighlight>
  
 
Wir erhalten dies:
 
Wir erhalten dies:
Line 254: Line 254:
  
 
[[BGRABitmap tutorial 7/de|Voriges Tutorial (Splines)]] | [[BGRABitmap tutorial 9/de|Nächstes Tutorial (Phong-Schattierung und Texturen)]]
 
[[BGRABitmap tutorial 7/de|Voriges Tutorial (Splines)]] | [[BGRABitmap tutorial 9/de|Nächstes Tutorial (Phong-Schattierung und Texturen)]]
 
[[Category:Graphics/de]]
 

Latest revision as of 06:13, 10 February 2020

Deutsch (de) English (en) español (es) français (fr)


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

Verwenden des Pinsels als Textur

Die einfachste Textur ist ein schraffierter Pinsel.

Im Objektinspektor fügen Sie einen OnPaint-Handler hinzu und schreiben Sie:

procedure TForm1.FormPaint(Sender: TObject);
var
  image,tex: TBGRABitmap;
  c: TBGRAPixel;
  x,y,rx,ry: single;

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

    //Ellipsenkoordinaten
    x := 150;
    y := 100;
    rx := 100;
    ry := 50;

    //lädt einen diagonal gekreuzten Pinsel ("diagcross") mit weißem Muster und orangem Hintergrund
    tex := image.CreateBrushTexture(bsDiagCross,BGRAWhite,BGRA(255,192,0)) as TBGRABitmap;

    image.FillEllipseAntialias(x,y,rx-0.5,ry-0.5,tex);
    image.EllipseAntialias(x,y,rx,ry,c,1); //draw outline

    tex.Free;

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

Wie Sie sehen ist eine Textur einfach eine Bitmap. Um eine Ellipse mit einer Textur zu füllen, übergeben Sie die Textur einfach als Parameter anstelle der Farbe.

Zwei Befehle bestimmen die Ellipse. Der erste ist die Füllung, der zweite die Rahmenlinie. Beachten Sie, dass der Radius für die Füllung um 0,5 Pixel kleiner ist. Genau genommen ist bei einer Stiftbreite von 1 der innere Radius um 0,5 kleiner und der äußere Radius um 0,5 größer. Aber sogar wenn Sie sorgfältig darauf achten, wird das Ergebnis nicht perfekt sein. Falls Sie eine Verbindung der Polygone mit Antialiasing wollen, sollten Sie 'TBGRAMultishapeFiller' aus der Unit 'BGRAPolygon' verwenden und alles zugleich zeichnen lassen.

Mit einem Befehl für die Rahmenlinie erzielen wir eine texturierte Ellipse mit einem Rahmen. Falls die Rahmenlinienfunktion nicht verfügbar ist, können Sie auch einen Füllbefehl benutzen, zuerst mit einem größeren Radius für die Rahmenfarbe, dann mit einem kleineren Radius für das Innere.

Fügen Sie die folgenden Zeilen ein vor 'tex.Free' :

    image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,c,11);
    image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,tex,9);

Der erste Befehl zeichnet ein breites, abgerundetes Rechteck (mit Breite 11), das den Rahmen enthält. Der zweite Befehl füllt mit einer Textur und einer geringeren Breite (9). Das klappt perfekt, solange die Textur nicht transparent ist.

Starten Sie das Programm

Sie erhalten ein abgerundetes Rechteck mit einer eingeschlossenen Ellipse. Jede Figur ist mit einer orangen Textur gefüllt.

BGRATutorial8.png

Generieren von Texturen

Basic Perlin noise map

Es ist möglich, anreihbare zufällige Texturen zu erzeugen mit der Funktion 'CreateCyclicPerlinNoiseMap', die Sie in der Unit 'BGRAGradients' finden.

Im Objektinspektor definieren Sie einen OnPaint-Handler mit:

procedure TForm1.FormPaint(Sender: TObject);
var
  image,tex: TBGRABitmap;

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

    tex := CreateCyclicPerlinNoiseMap(100,100);
    image.FillRect(0,0,image.Width,image.Height, tex, dmset);
    tex.free;

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

Dies erzeugt eine 100x100 Textur und füllt damit das Formular. Sie erhalten etwas ähnliches wie:

BGRATutorial8b.png

Ändern der Farben

Dies ist zu sehr schwarz-weiß. Geben wir etwas Farbe hinein. Dazu brauchen wir eine Funktion, die die Werte interpoliert. Hier ist sie:

  function Interp256(value1,value2,position: integer): integer; inline;
  begin
       result := (value1*(256-position) + value2*position) shr 8;
  end;

Diese Funktion berechnet einen Wert, der von Wert1 bis zu Wert2 läuft. Position ist eine Zahl zwischen 0 und 256 die anzeigt, wie nahe das Ergebnis bei Wert2 liegt. Der Ausdruck "shr 8" ist das optimierte Äquivalent zu "div 256" für positive Werte. Es ist eine binäre Verschiebung um 8 Stellen.

Wir wollen Farben interpolieren, also schreiben wir eine Funktion, die Farben interpoliert:

  function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline;
  begin
       result.red := Interp256(color1.red,color2.red, position);
       result.green := Interp256(color1.green,color2.green, position);
       result.blue := Interp256(color1.blue,color2.blue, position);
       result.alpha := Interp256(color1.alpha,color2.alpha, position);
  end;

Das ist geradlinig: jeder Farbanteil wird zwischen color1 und color2 interpoliert.

Jetzt haben wir alles, um es uns etwas bunter zu machen. Nach 'CreatePerlinNoiseMap' fügen Sie folgende Zeilen ein:

    p := tex.Data;
    for i := 0 to tex.NbPixels-1 do
    begin
      p^ := Interp256( BGRA(0,128,0), BGRA(192,255,0), p^.red );
      inc(p);
    end;

Sie brauchen die Variablen 'p' und 'i', also klicken Sie auf eine jede und drücken Sie Strg-Umsch-C.

Diese Schleife nimmt jedes Pixel und erzeugt eine Farbe von Dunkelgrün bis zu hellem Gelbgrün.

Wir erhalten einen baumartigen Grünton:

BGRATutorial8c.png

Verwendung von Schwellenwerten

Anstatt ständig zu variieren, kann die Farbänderung einen Schwellenwert berücksichtigen. Im Beispiel ergibt das ein Meer mit Inseln:

    p := tex.Data;
    for i := 0 to tex.NbPixels-1 do
    begin
      if p^.red > 196 then
        p^ := BGRA(192,160,96) else //island
        p^ := BGRA(0,128,196); //sea
      inc(p);
    end;

Wir nehmen mehrere Schwellenwerte. Fertig ist der Tarnanzug:

    p := result.Data;
    for i := 0 to result.NbPixels-1 do
    begin
      v := p^.red;
      if v < 64 then p^:= BGRA(31,33,46) else
      if v < 128 then p^:= BGRA(89,71,57) else
      if v < 192 then p^:= BGRA(80,106,67) else
        p^:= BGRA(161,157,121);
      inc(p);
    end;

BGRATutorial8d.png

Sinusfunktion

Hier wenden wir eine Sinusfunktion für die Noise-Werte an. Erzeugen wir uns dazu eine Prozedur:

  function CreateCustomTexture(tx,ty: integer): TBGRABitmap;
  var
    colorOscillation: integer;
    p: PBGRAPixel;
    i: Integer;
  begin
    result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1);
    p := result.Data;
    for i := 0 to result.NbPixels-1 do
    begin
      colorOscillation := round(((sin(p^.red*Pi/32)+1)/2)*256);
      p^ := Interp256(BGRA(181,157,105),BGRA(228,227,180),colorOscillation);
      inc(p);
    end;
  end;

ColorOscillation ist ein Wert zwischen 0 und 256. Er wird aus der Intensität von (p^.red) berechnet. Hier wenden wir die Sinusfunktion mit einer halben Periode von 32 an. Dies liefert uns eine Zahl zwischen -1 und 1. Um sie auf den Bereich 0..1 abzubilden, addieren wir 1 und dividieren durch 2. Zuletzt multiplizieren wir mit 256 und haben eine Ganzzahl für Interp256.

Die OnPaint-Prozedur wird einfacher:

var
  image,tex: TBGRABitmap;

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

    tex := CreateCustomTexture(100,100);
    image.FillRoundRectAntialias(20,20,300,200,20,20,tex);
    image.RoundRectAntialias(20,20,300,200,20,20,BGRABlack,1);
    tex.free;

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

Sie erhalten etwas in der Art:

BGRATutorial8e.png

Jetzt wollen wir, dass es wie Marmor ausschaut, wir brauchen weniger Schwingungen. Zum Beispiel können wir eine halbe Periode von 80 nehmen. Bei Marmor sind die dunklen Stellen sehr dünn. Wir verzerren die Schwingung durch Anwendung der Potenzfunktion: ein Exponent zwischen 0 und 1 bringt die Werte näher an 1 und ein Exponent größer als 1 bringt die Werte näher an 0. So ändern wir die Schwingung in 'CreateCustomTexture':

    colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256);

Jetzt haben wir etwas marmorartiges:

BGRATutorial8f.png

Holztextur

Auch eine Holztextur erzielen wir mit Sinusfunktionen. Holztexturen enthalten zwei Schwingungen. Eine mit hellen Farben und eine weitere mit dunklen Farben. Deshalb wenden wir eine globale Variation auf diese Schwingungen an:

  function CreateWoodTexture(tx,ty: integer): TBGRABitmap;
  var
    colorOscillation, globalColorVariation: integer;
    p: PBGRAPixel;
    i: Integer;
  begin
    result := CreateCyclicPerlinNoiseMap(tx,ty);
    p := result.Data;
    for i := 0 to result.NbPixels-1 do
    begin
      colorOscillation := round(sqrt((sin(p^.red*Pi/16)+1)/2)*256);
      globalColorVariation := p^.red;
      p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
                      Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
      inc(p);
    end;
  end;

Hier ist die Halbperiode 16, und die globale Variation ist einfach die Intensität. Das Ergebnis sieht etwa so aus:

BGRATutorial8g.png

Meistens ist eine Holztextur entlang einer Achse ausgerichtet. Dazu müssen wir, anstatt die Intensität als globale Position zu verwenden, sie mit der x-Position kombinieren:

  function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap;
  var
    globalPos: single;
    colorOscillation, globalColorVariation: integer;
    p: PBGRAPixel;
    i: Integer;
    x: integer;
  begin
    result := CreateCyclicPerlinNoiseMap(tx,ty);
    p := result.Data;
    x := 0;
    for i := 0 to result.NbPixels-1 do
    begin
      globalPos := p^.red*Pi/32+x*2*Pi/tx*8;
      colorOscillation := round(sqrt((sin(globalPos)+1)/2)*256);
      globalColorVariation := round(sin(globalPos/8)*128+128);
      p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
                      Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
      inc(p);
      inc(x);
      if x = tx then x := 0;
    end;
  end;

Wir erhalten dies:

Tutorial8h.png


Voriges Tutorial (Splines) | Nächstes Tutorial (Phong-Schattierung und Texturen)