Difference between revisions of "BGRABitmap tutorial 8/fr"
m (Text replace - "delphi>" to "syntaxhighlight>") |
|||
Line 7: | Line 7: | ||
=== Création d'un nouveau projet === | === Création d'un nouveau projet === | ||
− | Créez un nouveau projet et | + | 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]]. |
=== Utilisation des textures de pinceau === | === Utilisation des textures de pinceau === | ||
Line 13: | Line 13: | ||
La texture la plus simple est hachurée. | La texture la plus simple est hachurée. | ||
− | Avec l'inspecteur d'objet, | + | Avec l'inspecteur d'objet, ajoutez un gestionnaire ''OnPaint'' et écrivez : |
<syntaxhighlight>procedure TForm1.FormPaint(Sender: TObject); | <syntaxhighlight>procedure TForm1.FormPaint(Sender: TObject); | ||
var | var | ||
Line 42: | Line 42: | ||
end;</syntaxhighlight> | end;</syntaxhighlight> | ||
− | Comme vous pouvez le voir, une texture est juste une image. Pour remplir une ellipse avec une texture, passez | + | 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. |
− | Deux commandes définissent l'ellipse. La première est le remplissage, la seconde | + | 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. |
− | En utilisant la commande pour le contour, | + | 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. |
− | Ajoutez les lignes suivantes avant tex.Free : | + | Ajoutez les lignes suivantes avant ''tex.Free'' : |
<syntaxhighlight> image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,c,11); | <syntaxhighlight> 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); </syntaxhighlight> | image.RoundRectAntialias(x-rx-10,y-ry-10,x+rx+10,y+ry+10,20,20,tex,9); </syntaxhighlight> | ||
Line 64: | Line 64: | ||
==== Bruit de Perlin simple ==== | ==== Bruit de Perlin simple ==== | ||
− | Il est possible de générer des textures aléatoires | + | Il est possible de générer des textures aléatoires reproductibles en utilisant ''CreateCyclicPerlinNoiseMap'', qui peut être trouvé dans l'unité ''BGRAGradient''. |
− | Avec l'inspecteur d'objet, définissez un gestionnaire OnPaint avec : | + | Avec l'inspecteur d'objet, définissez un gestionnaire ''OnPaint'' avec : |
<syntaxhighlight>procedure TForm1.FormPaint(Sender: TObject); | <syntaxhighlight>procedure TForm1.FormPaint(Sender: TObject); | ||
var | var | ||
Line 82: | Line 82: | ||
end;</syntaxhighlight> | end;</syntaxhighlight> | ||
− | Ce code crée une texture de 100x100 | + | Ce code crée une texture de 100x100 et remplit la fenêtre avec elle. Vous devriez obtenir quelque chose comme cela : |
[[Image:BGRATutorial8b.png]] | [[Image:BGRATutorial8b.png]] | ||
Line 88: | Line 88: | ||
==== Changer la couleur ==== | ==== Changer la couleur ==== | ||
− | + | 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 : | |
<syntaxhighlight> function Interp256(value1,value2,position: integer): integer; inline; | <syntaxhighlight> 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;</syntaxhighlight> | end;</syntaxhighlight> | ||
− | 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 | + | 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. |
− | + | Comme vous voulez interpoler des couleurs, écrivez une fonction pour cela : | |
<syntaxhighlight> function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline; | <syntaxhighlight> function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline; | ||
begin | begin | ||
Line 103: | Line 103: | ||
result.alpha := Interp256(color1.alpha,color2.alpha, position); | result.alpha := Interp256(color1.alpha,color2.alpha, position); | ||
end;</syntaxhighlight> | end;</syntaxhighlight> | ||
− | C'est assez évident : chaque composante est interpolée entre la valeur pour color1 et pour color2. | + | C'est assez évident : chaque composante est interpolée entre la valeur pour ''color1'' et pour ''color2''. |
− | Maintenant, | + | Maintenant, vous avez tout le nécessaire pour faire de la couleur. Après ''CreatePerlinNoiseMap'', ajoutez les lignes suivantes : |
<syntaxhighlight> p := tex.Data; | <syntaxhighlight> p := tex.Data; | ||
for i := 0 to tex.NbPixels-1 do | for i := 0 to tex.NbPixels-1 do | ||
Line 112: | Line 112: | ||
inc(p); | inc(p); | ||
end; </syntaxhighlight> | end; </syntaxhighlight> | ||
− | Vous aurez besoin des variables 'p' et 'i', alors cliquez sur chacune et pressez Ctrl-Shift-C. | + | Vous aurez besoin des variables 'p' et 'i', alors cliquez sur chacune et pressez ''Ctrl-Shift-C''. |
Cette boucle prend chaque pixel et crée une couleur de vert foncé jusqu'à jaune-vert. | Cette boucle prend chaque pixel et crée une couleur de vert foncé jusqu'à jaune-vert. | ||
− | + | Vous obtenez une couleur vert-arbre : | |
[[Image:BGRATutorial8c.png]] | [[Image:BGRATutorial8c.png]] | ||
Line 122: | Line 122: | ||
==== Utilisation de seuils ==== | ==== Utilisation de seuils ==== | ||
− | Au lieu de varier continuellement, la couleur peut être changée avec un seuil. Par exemple, | + | 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 : |
<syntaxhighlight> p := tex.Data; | <syntaxhighlight> p := tex.Data; | ||
for i := 0 to tex.NbPixels-1 do | for i := 0 to tex.NbPixels-1 do | ||
Line 132: | Line 132: | ||
end; </syntaxhighlight> | end; </syntaxhighlight> | ||
− | + | Vous pouvez utiliser davantage de seuils. Voilà par exemple un camouflage militaire : | |
<syntaxhighlight> p := result.Data; | <syntaxhighlight> p := result.Data; | ||
for i := 0 to result.NbPixels-1 do | for i := 0 to result.NbPixels-1 do | ||
Line 148: | Line 148: | ||
==== Fonction sinus ==== | ==== Fonction sinus ==== | ||
− | + | Vous pouvez appliquer la fonction sinus aux valeurs du bruit. Créez pour cela une procédure : | |
<syntaxhighlight> | <syntaxhighlight> | ||
Line 167: | Line 167: | ||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | 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, | + | 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''. |
− | La procédure OnPaint devient plus simple : | + | La procédure ''OnPaint'' devient plus simple : |
<syntaxhighlight> | <syntaxhighlight> | ||
var | var | ||
Line 191: | Line 191: | ||
[[Image:BGRATutorial8e.png]] | [[Image:BGRATutorial8e.png]] | ||
− | + | À 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'' : | |
<syntaxhighlight> colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256); </syntaxhighlight> | <syntaxhighlight> colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256); </syntaxhighlight> | ||
− | + | Vous avez alors quelque chose qui ressemble beaucoup plus à du marbre : | |
[[Image:BGRATutorial8f.png]] | [[Image:BGRATutorial8f.png]] | ||
Line 200: | Line 200: | ||
==== Texture de bois ==== | ==== Texture de bois ==== | ||
− | Une texture de bois peut être réalisée avec des fonctions sinus également. La texture de bois contient | + | 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 : |
<syntaxhighlight> function CreateWoodTexture(tx,ty: integer): TBGRABitmap; | <syntaxhighlight> function CreateWoodTexture(tx,ty: integer): TBGRABitmap; | ||
var | var | ||
Line 223: | Line 223: | ||
[[Image:BGRATutorial8g.png]] | [[Image:BGRATutorial8g.png]] | ||
− | 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, | + | 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 : |
<syntaxhighlight> function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap; | <syntaxhighlight> function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap; | ||
var | var | ||
Line 248: | Line 248: | ||
end; </syntaxhighlight> | end; </syntaxhighlight> | ||
− | + | Vous obtenez cela : | |
[[Image:Tutorial8h.png]] | [[Image:Tutorial8h.png]] |
Revision as of 18:23, 16 May 2015
│ Deutsch (de) │ English (en) │ español (es) │ français (fr) │
Accueil | Tutoriel 1 | Tutoriel 2 | Tutoriel 3 | Tutoriel 4 | Tutoriel 5 | Tutoriel 6 | Tutoriel 7 | Tutoriel 8 | Tutoriel 9 | Tutoriel 10 | Tutoriel 11 | Tutoriel 12 | Edit
Ce tutoriel montre comment utiliser les textures.
Création d'un nouveau projet
Créez un nouveau projet et ajoutez la référence à BGRABitmap, de la même façon que dans le premier tutoriel.
Utilisation des textures de pinceau
La texture la plus simple est hachurée.
Avec l'inspecteur d'objet, ajoutez un gestionnaire OnPaint et écrivez :
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));
//coordonnées de l'ellipse
x := 150;
y := 100;
rx := 100;
ry := 50;
//charge un pinceau "diagcross" avec un motif blanc sur fond orange
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;
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.
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.
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.
Ajoutez les lignes suivantes avant 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);
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.
Exécution du programme
Vous devriez obtenir un rectangle arrondi avec une ellipse à l'intérieur. Chaque forme est remplie avec une texture orange.
Génération de textures
Bruit de Perlin simple
Il est possible de générer des textures aléatoires reproductibles en utilisant CreateCyclicPerlinNoiseMap, qui peut être trouvé dans l'unité BGRAGradient.
Avec l'inspecteur d'objet, définissez un gestionnaire OnPaint avec :
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;
Ce code crée une texture de 100x100 et remplit la fenêtre avec elle. Vous devriez obtenir quelque chose comme cela :
Changer la couleur
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 :
function Interp256(value1,value2,position: integer): integer; inline;
begin
result := (value1*(256-position) + value2*position) shr 8;
end;
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.
Comme vous voulez interpoler des couleurs, écrivez une fonction pour cela :
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;
C'est assez évident : chaque composante est interpolée entre la valeur pour color1 et pour color2.
Maintenant, vous avez tout le nécessaire pour faire de la couleur. Après CreatePerlinNoiseMap, ajoutez les lignes suivantes :
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;
Vous aurez besoin des variables 'p' et 'i', alors cliquez sur chacune et pressez Ctrl-Shift-C.
Cette boucle prend chaque pixel et crée une couleur de vert foncé jusqu'à jaune-vert.
Vous obtenez une couleur vert-arbre :
Utilisation de seuils
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 :
p := tex.Data;
for i := 0 to tex.NbPixels-1 do
begin
if p^.red > 196 then
p^ := BGRA(192,160,96) else //mer
p^ := BGRA(0,128,196); //île
inc(p);
end;
Vous pouvez utiliser davantage de seuils. Voilà par exemple un camouflage militaire :
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;
Fonction sinus
Vous pouvez appliquer la fonction sinus aux valeurs du bruit. Créez pour cela une procédure :
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;
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.
La procédure OnPaint devient plus simple :
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;
Vous devriez obtenir une image ressemblant à cela :
À 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 :
colorOscillation := round(power((sin(p^.red*Pi/80)+1)/2,0.2)*256);
Vous avez alors quelque chose qui ressemble beaucoup plus à du marbre :
Texture de bois
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 :
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;
Ici, la demi-période est 16 et la variation globale est simplement l'intensité. Le résultat ressemble à cela :
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 :
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;
Vous obtenez cela :
Tutoriel précédent (splines) Tutoriel suivant (éclairage phong et textures)