Difference between revisions of "BGRABitmap tutorial 8/de"

From Free Pascal wiki
Jump to navigationJump to search
m (link to german index)
Line 76: Line 76:
 
     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);
 +
    tex.free;
  
 
     image.Draw(Canvas,0,0,True);
 
     image.Draw(Canvas,0,0,True);

Revision as of 00:10, 11 April 2011

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: <delphi>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;</delphi>

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.

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' : <delphi> 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>

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 'BGRAGradient' finden.

Im Objektinspektor definieren Sie einen OnPaint-Handler mit: <delphi>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);
   tex.free;
   image.Draw(Canvas,0,0,True);
   image.free;  

end;</delphi>

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: <delphi> function Interp256(value1,value2,position: integer): integer; inline;

 begin
      result := (value1*(256-position) + value2*position) shr 8;
 end;</delphi>

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: <delphi> 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;</delphi>

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: <delphi> 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;  </delphi>

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: <delphi> 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;  </delphi>

Wir nehmen mehrere Schwellenwerte. Fertig ist der Tarnanzug: <delphi> 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;  </delphi>

BGRATutorial8d.png

Sinusfunktion

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

<delphi>

 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;

</delphi> 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: <delphi> 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);
   image.Draw(Canvas,0,0,True);
   image.free;

end; </delphi>

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': <delphi> colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256); </delphi>

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: <delphi> 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;</delphi>

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: <delphi> 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; </delphi>

Wir erhalten dies:

Tutorial8h.png


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