Difference between revisions of "BGRABitmap tutorial 8/de"
m (→Basic Perlin noise map: memory leak) |
m (→Sinusfunktion: memory leak) |
||
Line 180: | Line 180: | ||
image.FillRoundRectAntialias(20,20,300,200,20,20,tex); | image.FillRoundRectAntialias(20,20,300,200,20,20,tex); | ||
image.RoundRectAntialias(20,20,300,200,20,20,BGRABlack,1); | image.RoundRectAntialias(20,20,300,200,20,20,BGRABlack,1); | ||
+ | tex.free; | ||
image.Draw(Canvas,0,0,True); | image.Draw(Canvas,0,0,True); |
Revision as of 01:11, 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.
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:
Ä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:
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>
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); tex.free;
image.Draw(Canvas,0,0,True); image.free;
end; </delphi>
Sie erhalten etwas in der Art:
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:
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:
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:
Voriges Tutorial (Splines) Nächstes Tutorial (Phong-Schattierung und Texturen)