Sign in Join
Wiki copytemporary

BGRABitmap tutorial 8/fr: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: BGRABitmap tutorial 8/fr

OldNewLine
1+Ce tutoriel montre comment utiliser les textures.
2+
3+### Création d'un nouveau projet
4+
5+Créez un nouveau projet et ajoutez la référence à [[BGRABitmap]], de la même façon que dans [[BGRABitmap tutorial/fr|le premier tutoriel]].
6+
7+### Utilisation des textures de pinceau
8+
9+La texture la plus simple est hachurée.
10+
11+Avec l'inspecteur d'objet, ajoutez un gestionnaire *OnPaint* et écrivez :
12+
13+``` pascal
14+procedure TForm1.FormPaint(Sender: TObject);
15+var
16+ image,tex: TBGRABitmap;
17+ c: TBGRAPixel;
18+ x,y,rx,ry: single;
19+
20+begin
21+ image := TBGRABitmap.Create(ClientWidth,ClientHeight,ColorToBGRA(ColorToRGB(clBtnFace)));
22+ c := ColorToBGRA(ColorToRGB(clWindowText));
23+
24+ //coordonnées de l'ellipse
25+ x := 150;
26+ y := 100;
27+ rx := 100;
28+ ry := 50;
29+
30+ //charge un pinceau "diagcross" avec un motif blanc sur fond orange
31+ tex := image.CreateBrushTexture(bsDiagCross,BGRAWhite,BGRA(255,192,0)) as TBGRABitmap;
32+
33+ image.FillEllipseAntialias(x,y,rx-0.5,ry-0.5,tex);
34+ image.EllipseAntialias(x,y,rx,ry,c,1); //draw outline
35+
36+ tex.Free;
37+
38+ image.Draw(Canvas,0,0,True);
39+ image.free;
40+end;
41+```
42+
43+Comme vous pouvez le voir, une texture est juste une image. Pour remplir une ellipse avec une texture, passez simplement la texture en paramètre à la place de la couleur.
44+
45+Deux commandes définissent l'ellipse. La première est le remplissage, la seconde le contour. Notez que le rayon est 0,5 pixel plus petit pour le remplissage. En effet, quand la taille du pinceau est 1, le rayon intérieur est 0,5 plus petit et le rayon extérieur 0,5 pixel plus grand.
46+
47+En utilisant la commande pour le contour, vous dessinez une ellipse avec texture et un bord. Mais si la fonction de contour n'est pas disponible, vous pouvez aussi utiliser une autre commande de remplissage avec un plus grand rayon et la couleur du bord, puis un rayon plus petit pour l'intérieur.
48+
49+Ajoutez les lignes suivantes avant *tex.Free* :
50+
51+``` pascal
52+ image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,c,11);
53+ image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,tex,9);
54+```
55+
56+La première commande dessine un rectangle arrondi large (un pinceau de largeur 11) qui inclut le bord. La seconde commande remplit la texture avec une largeur plus petite (9). Cela fonctionne parfaitement tant que la texture n'est pas transparente.
57+
58+### Exécution du programme
59+
60+Vous devriez obtenir un rectangle arrondi avec une ellipse à l'intérieur. Chaque forme est remplie avec une texture orange.
61+
62+<figure>
63+<img src="BGRATutorial8.png" title="BGRATutorial8.png" />
64+<figcaption>BGRATutorial8.png</figcaption>
65+</figure>
66+
67+### Génération de textures
68+
69+#### Bruit de Perlin simple
70+
71+Il est possible de générer des textures aléatoires reproductibles en utilisant *CreateCyclicPerlinNoiseMap*, qui peut être trouvé dans l'unité *BGRAGradient*.
72+
73+Avec l'inspecteur d'objet, définissez un gestionnaire *OnPaint* avec :
74+
75+``` pascal
76+procedure TForm1.FormPaint(Sender: TObject);
77+var
78+ image,tex: TBGRABitmap;
79+
80+begin
81+ image := TBGRABitmap.Create(ClientWidth,ClientHeight);
82+
83+ tex := CreateCyclicPerlinNoiseMap(100,100);
84+ image.FillRect(0,0,image.Width,image.Height, tex);
85+ tex.free;
86+
87+ image.Draw(Canvas,0,0,True);
88+ image.free;
89+end;
90+```
91+
92+Ce code crée une texture de 100x100 et remplit la fenêtre avec elle. Vous devriez obtenir quelque chose comme cela :
93+
94+<figure>
95+<img src="BGRATutorial8b.png" title="BGRATutorial8b.png" />
96+<figcaption>BGRATutorial8b.png</figcaption>
97+</figure>
98+
99+#### Changer la couleur
100+
101+Le résultat est très noir et blanc. Vous pouvez ajouter quelques couleurs. Pour cela, vous disposez d'une fonction pour interpoler les valeurs. En voici une :
102+
103+``` pascal
104+ function Interp256(value1,value2,position: integer): integer; inline;
105+ begin
106+ result := (value1*(256-position) + value2*position) shr 8;
107+ end;
108+```
109+
110+Cette fonction calcule une valeur allant de *value1* à *value2*. *Position* est un nombre entre 0 et 256 indiquant si on se rapproche de la deuxième valeur. L'expression *shr 8* est un équivalent optimisé de *div 256* pour des valeurs positives. C'est un décalage binaire de 8 chiffres.
111+
112+Comme vous voulez interpoler des couleurs, écrivez une fonction pour cela :
113+
114+``` pascal
115+ function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline;
116+ begin
117+ result.red := Interp256(color1.red,color2.red, position);
118+ result.green := Interp256(color1.green,color2.green, position);
119+ result.blue := Interp256(color1.blue,color2.blue, position);
120+ result.alpha := Interp256(color1.alpha,color2.alpha, position);
121+ end;
122+```
123+
124+C'est assez évident : chaque composante est interpolée entre la valeur pour *color1* et pour *color2*.
125+
126+Maintenant, vous avez tout le nécessaire pour faire de la couleur. Après *CreatePerlinNoiseMap*, ajoutez les lignes suivantes :
127+
128+``` pascal
129+ p := tex.Data;
130+ for i := 0 to tex.NbPixels-1 do
131+ begin
132+ p^ := Interp256( BGRA(0,128,0), BGRA(192,255,0), p^.red );
133+ inc(p);
134+ end;
135+```
136+
137+Vous aurez besoin des variables 'p' et 'i', alors cliquez sur chacune et pressez *Ctrl-Shift-C*.
138+
139+Cette boucle prend chaque pixel et crée une couleur de vert foncé jusqu'à jaune-vert.
140+
141+Vous obtenez une couleur vert-arbre :
142+
143+<figure>
144+<img src="BGRATutorial8c.png" title="BGRATutorial8c.png" />
145+<figcaption>BGRATutorial8c.png</figcaption>
146+</figure>
147+
148+#### Utilisation de seuils
149+
150+Au lieu de varier continuellement, la couleur peut être changée avec un seuil. Par exemple, vous pouvez délimiter la mer et des îles :
151+
152+``` pascal
153+ p := tex.Data;
154+ for i := 0 to tex.NbPixels-1 do
155+ begin
156+ if p^.red > 196 then
157+ p^ := BGRA(192,160,96) else //mer
158+ p^ := BGRA(0,128,196); //île
159+ inc(p);
160+ end;
161+```
162+
163+Vous pouvez utiliser davantage de seuils. Voilà par exemple un camouflage militaire :
164+
165+``` pascal
166+ p := result.Data;
167+ for i := 0 to result.NbPixels-1 do
168+ begin
169+ v := p^.red;
170+ if v < 64 then p^:= BGRA(31,33,46) else
171+ if v < 128 then p^:= BGRA(89,71,57) else
172+ if v < 192 then p^:= BGRA(80,106,67) else
173+ p^:= BGRA(161,157,121);
174+ inc(p);
175+ end;
176+```
177+
178+<figure>
179+<img src="BGRATutorial8d.png" title="BGRATutorial8d.png" />
180+<figcaption>BGRATutorial8d.png</figcaption>
181+</figure>
182+
183+#### Fonction sinus
184+
185+Vous pouvez appliquer la fonction sinus aux valeurs du bruit. Créez pour cela une procédure :
186+
187+``` pascal
188+ function CreateCustomTexture(tx,ty: integer): TBGRABitmap;
189+ var
190+ colorOscillation: integer;
191+ p: PBGRAPixel;
192+ i: Integer;
193+ begin
194+ result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1);
195+ p := result.Data;
196+ for i := 0 to result.NbPixels-1 do
197+ begin
198+ colorOscillation := round(((sin(p^.red*Pi/32)+1)/2)*256);
199+ p^ := Interp256(BGRA(181,157,105),BGRA(228,227,180),colorOscillation);
200+ inc(p);
201+ end;
202+ end;
203+```
204+
205+L'oscillation de couleur est une valeur entre 0 et 256. Elle est calculée à partir de l'intensité (*p^.red*). On y applique la fonction sinus avec une demi-période de 32. Cela donne un nombre entre -1 et 1. Pour le ramener dans l'intervalle 0..1, vous ajoutez 1 et divisez par 2. Enfin, vous multipliez par 256 pour avoir un entier pour *Interp256*.
206+
207+La procédure *OnPaint* devient plus simple :
208+
209+``` pascal
210+var
211+ image,tex: TBGRABitmap;
212+
213+begin
214+ image := TBGRABitmap.Create(ClientWidth,ClientHeight,ColorToBGRA(ColorToRGB(clBtnFace)));
215+
216+ tex := CreateCustomTexture(100,100);
217+ image.FillRoundRectAntialias(20,20,300,200,20,20,tex);
218+ image.RoundRectAntialias(20,20,300,200,20,20,BGRABlack,1);
219+ tex.free;
220+
221+ image.Draw(Canvas,0,0,True);
222+ image.free;
223+end;
224+```
225+
226+Vous devriez obtenir une image ressemblant à cela :
227+
228+<figure>
229+<img src="BGRATutorial8e.png" title="BGRATutorial8e.png" />
230+<figcaption>BGRATutorial8e.png</figcaption>
231+</figure>
232+
233+À présent, si vous voulez que votre texture ressemble à du marbre, vous avez besoin de moins d'oscillation. Par exemple, vous pouvez utiliser une demi-période de 80. Sur le marbre, les parties sombres sont très fines. Vous pouvez déformer les oscillations en appliquant une fonction 'puissance' : un exposant entre 0 et 1 rendra la valeur plus proche de 1 et un exposant plus grand que 1 rendra la valeur plus proche de 0. Changez donc l'oscillation dans *CreateCustomTexture* :
234+
235+``` pascal
236+ colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256);
237+```
238+
239+Vous avez alors quelque chose qui ressemble beaucoup plus à du marbre :
240+
241+<figure>
242+<img src="BGRATutorial8f.png" title="BGRATutorial8f.png" />
243+<figcaption>BGRATutorial8f.png</figcaption>
244+</figure>
245+
246+#### Texture de bois
247+
248+Une texture de bois peut être réalisée avec des fonctions sinus également. La texture de bois contient deux oscillations : une avec des couleurs claires et une autre avec des couleurs foncées. Alors, vous devons appliquer une variation globale entre ces oscillations :
249+
250+``` pascal
251+ function CreateWoodTexture(tx,ty: integer): TBGRABitmap;
252+ var
253+ colorOscillation, globalColorVariation: integer;
254+ p: PBGRAPixel;
255+ i: Integer;
256+ begin
257+ result := CreateCyclicPerlinNoiseMap(tx,ty);
258+ p := result.Data;
259+ for i := 0 to result.NbPixels-1 do
260+ begin
261+ colorOscillation := round(sqrt((sin(p^.red*Pi/16)+1)/2)*256);
262+ globalColorVariation := p^.red;
263+ p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
264+ Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
265+ inc(p);
266+ end;
267+ end;
268+```
269+
270+Ici, la demi-période est 16 et la variation globale est simplement l'intensité. Le résultat ressemble à cela :
271+
272+<figure>
273+<img src="BGRATutorial8g.png" title="BGRATutorial8g.png" />
274+<figcaption>BGRATutorial8g.png</figcaption>
275+</figure>
276+
277+La plupart du temps, une texture de bois est orientée selon un axe. Pour faire cela, à la place d'utiliser l'intensité seulement comme position globale, vous avez besoin de la combiner avec la position x :
278+
279+``` pascal
280+ function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap;
281+ var
282+ globalPos: single;
283+ colorOscillation, globalColorVariation: integer;
284+ p: PBGRAPixel;
285+ i: Integer;
286+ x: integer;
287+ begin
288+ result := CreateCyclicPerlinNoiseMap(tx,ty);
289+ p := result.Data;
290+ x := 0;
291+ for i := 0 to result.NbPixels-1 do
292+ begin
293+ globalPos := p^.red*Pi/32 + x*2*Pi/tx*8;
294+ colorOscillation := round(sqrt((sin(globalPos)+1)/2)*256);
295+ globalColorVariation := round(sin(globalPos/8)*128+128);
296+ p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
297+ Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
298+ inc(p);
299+ inc(x);
300+ if x = tx then x := 0;
301+ end;
302+ end;
303+```
304+
305+Vous obtenez cela :
306+
307+<figure>
308+<img src="Tutorial8h.png" title="Tutorial8h.png" />
309+<figcaption>Tutorial8h.png</figcaption>
310+</figure>
311+
312+[[BGRABitmap tutorial 7/fr|Tutoriel précédent (splines)]] [[BGRABitmap tutorial 9/fr|Tutoriel suivant (éclairage phong et textures)]]
313+
314+---
315+*From the Free Pascal wiki, page "BGRABitmap tutorial 8/fr", as it stood on 2020-02-10 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history