Affichage moteur 3d [Résolu]

md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention - 16 août 2010 à 12:38 - Dernière réponse : md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention
- 4 janv. 2011 à 10:45
Bonjour,

je suis en train de développer un moteur 3D basé sur la technologie du ray-casting utilisée dans les anciens jeux 3D.
Pour le rendu j'utilise des matrices de nombres (3 pour les couches RGB) ce qui me permet de traiter de manière purement numérique l'intégration des textures et des sprites, ainsi que tous les effets (transparence, incrustation, ...).
L'affichage n'a lieu qu'une seule fois par cycle au moyen de la fonction scanline qui est tout à fait adaptée à ce que je fais et qui est très performante.
Mon moteur fonctionne, il n'est pas encore complet, cependant il est encore trop lent dans les hautes résolutions

LE BUT DE MA QUESTION EST DE CHERCHER A AMELIORER LA VITESSE DE TARITEMENT ET D'AFFICHAGE, donc d'améliorer le framerate (nb d'images par secondes)

Avant de continuer, je précise que la résolution de mon écran lors des tests était de 1680 x 1050

les codes qui suivent sont des essais qui ne concernent que le principe de la partie affichage, il ne s'agit bien sûr pas du moteur complet.

J'ai donc déjà essayé la fonction scanline seule en affectant à chaque pixel une couleur aléatoire, j'ai obtenu une vitesse de 57 img/s (cela m'a beaucoup plu !)
voici le code que j'ai utilisé :

unit Main;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TMainFiche = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;

TRGBArray = ARRAY[0..0] OF TRGBTriple; // élément de bitmap (API windows)
pRGBArray = ^TRGBArray; // type pointeur vers tableau 3 octets 24 bits

var
MainFiche: TMainFiche;
Img: TBitMap;

implementation

uses DateUtils;

{$R *.dfm}

procedure TMainFiche.FormCreate(Sender: TObject);
begin
Img := TBitMap.Create;
With Img do
Begin
PixelFormat := pf24bit;
Width := 1680;
Height := 1050;
end;
end;

procedure TMainFiche.FormClick(Sender: TObject);
var
ATB: pRGBArray;
X,Y,P: Integer;
Depart: TDateTime;
couleur: Byte;
begin
Depart := Now;
For P := 0 to 99 do
Begin
For Y := 0 to 1049 do
Begin
ATB := Img.ScanLine[Y];
For X := 0 to 1679 do
Begin
//ATB[X].rgbtRed := Random(256);
//ATB[X].rgbtGreen := Random(256);
//ATB[X].rgbtBlue := Random(256);
Couleur := Random(256);
ATB[X].rgbtRed := Couleur;
ATB[X].rgbtGreen := Couleur;
ATB[X].rgbtBlue := Couleur;
end;
end;
MainFiche.Canvas.Draw(0,0,Img);
end;
MainFiche.Caption := FormatFloat('0.0',1000 / (MilliSecondsBetween(Depart,Now) / 100)) + ' images par secondes';
end;

procedure TMainFiche.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Img.Free;
end;

procedure TMainFiche.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = 27 then Close;
end;

end.

J'ai ensuite intégré ma matrice de nombres, au départ j'ai essayé avec une seule matrice à 3 dimensions, la troisième dimension étant dédiée aux 3 couches RGB (array of array of array of Byte;) et je suis retombé à 6 img/s !
J'ai ensuite essayé avec 3 matrices à deux dimensions (code fourni ci-après) et je repasse à 13 img/s (c'est mieux mais pas suffisant)
J'ai ensuite essayé de couper les matrices en deux parties afin qu'elles soient moins grosses, la vitesse est exactement la même avec des lignes de code en plus, donc aucun intéret (code non fourni). On gagne donc du temps en enlevant une dimension mais pas en reduisant la taille de la matrice.
voici le code que j'ai utilisé (la version avec une seule matrice à 3D est en commentaire):

unit Main;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TMainFiche = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;

TRGBArray = ARRAY[0..0] OF TRGBTriple; // élément de bitmap (API windows)
pRGBArray = ^TRGBArray; // type pointeur vers tableau 3 octets 24 bits

var
MainFiche: TMainFiche;
Img: TBitMap;
//V_SCR_MATRIX: array of array of array of Byte; // matrice associée à l'écran (RGB)
V_SCR_MATRIX_R: array of array of Byte; // matrice associée à l'écran (RGB)
V_SCR_MATRIX_G: array of array of Byte; // matrice associée à l'écran (RGB)
V_SCR_MATRIX_B: array of array of Byte; // matrice associée à l'écran (RGB)

implementation

uses DateUtils;

{$R *.dfm}

procedure TMainFiche.FormCreate(Sender: TObject);
begin
Img := TBitMap.Create;
With Img do
Begin
PixelFormat := pf24bit;
Width := 1680;
Height := 1050;
end;
//SetLength(V_SCR_MATRIX,1680,1050,3);
SetLength(V_SCR_MATRIX_R,1680,1050);
SetLength(V_SCR_MATRIX_G,1680,1050);
SetLength(V_SCR_MATRIX_B,1680,1050);
end;

procedure TMainFiche.FormClick(Sender: TObject);
var
ATB: pRGBArray;
X,Y,P: Integer;
Depart: TDateTime;
couleur: Byte;
begin
Depart := Now;
For P := 0 to 99 do
Begin

For X := 0 to 1679 do
For Y := 0 to 1049 do
Begin
Couleur := Random(256);
//V_SCR_MATRIX[X,Y,0] := Couleur;
//V_SCR_MATRIX[X,Y,1] := Couleur;
//V_SCR_MATRIX[X,Y,2] := Couleur;
V_SCR_MATRIX_R[X,Y] := Couleur;
V_SCR_MATRIX_G[X,Y] := Couleur;
V_SCR_MATRIX_B[X,Y] := Couleur;
end;

For Y := 0 to 1049 do
Begin
ATB := Img.ScanLine[Y];
For X := 0 to 1679 do
Begin
//ATB[X].rgbtRed := V_SCR_MATRIX[X,Y,0];
//ATB[X].rgbtGreen := V_SCR_MATRIX[X,Y,1];
//ATB[X].rgbtBlue := V_SCR_MATRIX[X,Y,2];
ATB[X].rgbtRed := V_SCR_MATRIX_R[X,Y];
ATB[X].rgbtGreen := V_SCR_MATRIX_G[X,Y];
ATB[X].rgbtBlue := V_SCR_MATRIX_B[X,Y];
end;
end;
MainFiche.Canvas.Draw(0,0,Img);

end;
MainFiche.Caption := FormatFloat('0.0',1000 / (MilliSecondsBetween(Depart,Now) / 100)) + ' images par secondes';
end;

procedure TMainFiche.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Img.Free;
end;

procedure TMainFiche.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = 27 then Close;
end;

end.

Visiblement les tableaux de nombres ralentissent beaucoup, cependant j'ai besoin de ce type de structure. En plus, il faut savoir que le ray-casting découpe l'écran en lignes verticales alors que la fonction scanline utilise des lignes horizontales. Ces tableaux de variables sont théoriquement stockés dans la RAM, donc pas d'accès au disque dur, je trouve les temps d'accès bien longs.
C'est pourquoi je cherche tous les moyens pour améliorer ce code et je suis preneur de toutes les idées.

merci par avance
md21
Afficher la suite 

Votre réponse

22 réponses

Meilleure réponse
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 16 août 2010 à 13:01
3
Merci
Salut,

ScanLine c'est bien mais c'est encore mieux quand on l'utilise d'une manière plus adapté.

Voir le code de Caribensila a ce sujet et les commentaires pour un maximum de rapidité avec 1 seul appel à ScanLine


[hr]@+Cirec
[hr]

Merci Cirec 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 68 internautes ce mois-ci

Commenter la réponse de Cirec
Meilleure réponse
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 17 août 2010 à 17:09
3
Merci
Bonjour,

Tu es sur la bonne voie car ScanLine permet effectivement d'optimiser ton affichage. ScanLine travaille directement sur les pixels stockés en mémoire du Bitmap.
Cependant, il te reste encore deux moyens d'optimiser ScanLine dans ton cas :

1) Tu travailles en pf32bit. Dans ce cas, tu n'auras pas besoin de te soucier de l'alignement des lignes mémoire.
2) Tu travailles en pf24bit. Dans ce cas, il faudra t'assurer que la largeur de ton Bitmap est un multiple de 4. Mais comme tu travailles en plein écran, ce sera toujours le cas à condition de faire comme ça (code à placer dans une fiche vierge) :


//Pour utiliser ce bout de code, il faut cliquer sur n'importe quelle touche (et "Echappe" pour quitter).

procedure TForm1.FormCreate(Sender: TObject);
begin
  BorderStyle := bsNone;
  WindowState := wsMaximized;
  KeyPreview  := true;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_ESCAPE  then Application.Terminate;
end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
Canvas.TextOut(100,100,'Dim Canvas : ' + IntToStr(Form1.ClientWidth)
                     + ' x ' + IntToStr(Form1.ClientHeight)
                     +'    (Touche Echap pour quitter)');
end;



Ensuite, pour balayer l'écran, tu feras quelque chose comme ça en 24 bits :

var Scan0 : pRGBTriple;
i : Integer;
BMP : TBitmap;
begin
BMP := TBitmap.Create;
try
  BMP.Width := Form1.Width;
  BMP.Height:= Form1.Height;
  BMP.PixelFormat := pf24bit;
  Scan0 :BMP.ScanLine[BMP.Height - 1];// GDI> toujours BottomUp DIB
  for i := 1 to (BMP.Width*BMP.Height) do begin
  	with Scan0^ do begin
    	rgbtBlue := 50;
      rgbtGreen:= 200;
      rgbtRed  := 60;
    	Inc(Scan0);
    end;
  end;
  Form1.Canvas.Draw(0,0,BMP);
finally  BMP.Free;  end;
end;


Et comme ça en 32 bits :

var Scan0 : pRGBQuad;
i : Integer;
BMP : TBitmap;
begin
BMP := TBitmap.Create;
try
  BMP.Width := Form1.Width;
  BMP.Height:= Form1.Height;
  BMP.PixelFormat := pf32bit;
  Scan0 := BMP.ScanLine[BMP.Height - 1];
  for i := 1 to (BMP.Width*BMP.Height) do begin
  	with Scan0^ do begin
    	rgbBlue  := 150;
      rgbGreen := 20;
      rgbRed   := 30;
      rgbReserved:= 0;//Peut servir à stocker n'importe quelle valeur utile en byte !
    Inc(Scan0);
    end;
  end;
  Form1.Canvas.Draw(0,0,BMP);
finally  BMP.Free;  end;
end;


Remarque que le 32 bits te donne un espace de stockage supplémentaire à chaque pixel. Cela peut être très utile pour stocker une valeur utilisée par ton moteur.

Si (ce qui ne m'étonnerait pas) c'est encore trop lent pour une application 3D plein écran, il te faudra utiliser les accélérations matérielles fournies par la carte graphique en passant par Direct3D ou OpenGL ( comme les pros :)
Et le plus facile pour commencer la 3D avec Delphi est sans doute GLScene.

Bon coding !
Cari

PS: Il faut éviter d'utiliser des tableaux à plusieurs dimensions pour l'optimisation.
Je pense que le plus rapide serait de passer par des Pointers.
Mais, pour trouver le meilleur moyen, il te faudra faire de nombreux tests je crois.

Merci Caribensila 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 68 internautes ce mois-ci

Commenter la réponse de Caribensila
Meilleure réponse
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 17 août 2010 à 17:53
3
Merci
re,

avec ce code j'augmente par 3 le frame rate:
[hr]procedure TMainFiche.Button2Click(Sender: TObject);

[b]var
  /bScan0: Integer; //Valeur, en Integer, de la 1ère adresse de ScanLine.
  MemLineSize: Integer; //Taille d'une ligne de pixels en mémoire (en octets).
  BytesPerPix: Integer; //Format des pixels (en octets).
  CurrentScan: Integer;
  //ATB: pRGBArray;
  X, Y, P: Integer;
  Depart: TDateTime;
  couleur: Byte;
  V_SCR_MATRIX: pRGBTriple;
  MATRIX_Scan0: Integer; //Valeur, en Integer, de la 1ère adresse de ScanLine.
  MATRIX_CurrentScan: Integer;
[b]begin
  /bScan0 :=  Integer(Img.ScanLine[0]);  //Pointe sur la 1ère ligne du Bitmap.
  //MemLineSize sera le plus souvent <0 permettant ainsi de décrémenter l'adresse du pointeur de ligne-mémoire (Y).
  MemLineSize : = Integer(Img.ScanLine[1]) - Scan0;
  //BytesPerPix permettra d'incrémenter l'adresse du pointeur pRGBTriple en fonction de sa position dans la ligne (X).
  BytesPerPix :=  Abs(MemLineSize  div  Img.Width);

  V_SCR_MATRIX : = AllocMem(Img.Width * Img.Height * 4);

  MATRIX_Scan0 :=  Integer(V_SCR_MATRIX);
  Depart := Now;
  [b]try
    for /bP := 0  to  99 [b]do
    begin
      /bMATRIX_CurrentScan : = MATRIX_Scan0;
      for X :=  0  to  1679 [b]do
        for /bY : = 0 to 1049 [b]do
        begin
          with /bpRGBTriple(MATRIX_CurrentScan)^ [b]do
          begin
            /bCouleur :=  Random(256);
            rgbtRed := Couleur;
            rgbtGreen := Couleur;
            rgbtBlue := Couleur;
           end ;
          Inc(MATRIX_CurrentScan, BytesPerPix);
        end;

      MATRIX_CurrentScan : = MATRIX_Scan0;
      for Y :=  0  to  1049 [b]do
      begin
        /b // on se place en début de ligne
        CurrentScan : = Scan0 + Y * MemLineSize;
        for X :=  0  to  1679 [b]do
          with /bPRGBQuad(CurrentScan)^ [b]do
          begin
            /brgbRed : = pRGBTriple(MATRIX_CurrentScan)^.rgbtRed;
            rgbGreen :=  pRGBTriple(MATRIX_CurrentScan)^.rgbtGreen;
            rgbBlue := pRGBTriple(MATRIX_CurrentScan)^.rgbtBlue;
             // on incrémente de BytesPerPixel pour passer à la valeur suivante
            Inc(CurrentScan, BytesPerPix);
            Inc(MATRIX_CurrentScan, BytesPerPix);
           end ;
      end;
      MainFiche.Canvas.Draw(0, 0, Img);

    end;
  [b]finally
    /bReallocMem(V_SCR_MATRIX, 0);
  end;
  MainFiche.Caption : = FormatFloat('0.0', 1000 / (MilliSecondsBetween(Depart,
    Now) / 100)) + ' images par secondes';
end;
[hr]

y a peut être encore moyen de faire mieux ...
bon tests ...


[hr]@+Cirec
[hr]

Merci Cirec 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 68 internautes ce mois-ci

Commenter la réponse de Cirec
Meilleure réponse
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 18 août 2010 à 02:12
3
Merci
@Cirec
Je ne comprends pas bien comment ton code donne de meilleures perf' (mais je ne l'ai peut-être pas bien compris).
C'est comme si tu précalculais les gris avant de les affecter aux pixels du Bitmap, non ?
Tu l'as testé sur un pf32bit, peut-être ? ( Il reste des traces :)
En général, comme tu le sais, le pf32bit donne de meilleurs résultats car le proc est fait pour calculer avec des DWords...

@md21
Je comprends très bien ce que tu veux faire ( j'étais un peu sceptique au début, je l'avoue :)
Une action pour les vioques comme Debiars et Cie... et moi-même, quoi.
C'est louable ! :)))

Mais revenons IRL.
Comme tu travailles en plein écran, la largeur de ton Bitmap doit devrait est toujours divisible par 4.
Il ne faut donc pas te soucier de l'alignement mémoire des lignes. Elle sont alors tramées comme la mémoire Windows; ce qui t'évite beaucoup de précautions inutiles alors.
De plus, tu peux travailler directemennt sur les pointeurs sans les transtyper en Integer. En effet, 'Inc(pRGBTriple)' passe directement au pointeur suivant (voir Aide pour 'Inc').

Je n'ai pas testé, mais ton code devrait être simplifié et plutôt ressembler à ça :




//ATTENTION !  La largeur du Bitmap doit être divisible par 4 !

procedure TMainFiche.FormClick(Sender: TObject);
var
  X,Y,P: Integer;
  Depart: TDateTime;
  CurrentScan : pRGBTriple; // A CHANGE !
  Scan0 : pRGBTriple; // A ETE AJOUTE !
  couleur: Byte;

begin
Depart := Now;
Scan0  := Img.ScanLine[Img.Height - 1];   // A CHANGE !
For P := 0 to 99 do
Begin
  CurrentScan := Scan0; // A CHANGER !
  For X := 1 to (Img.Width*img.Height) do  // A CHANGE !
  Begin
    Couleur := Random(256);
    With CurrentScan^ do
    Begin
      rgbtRed   := Couleur;
      rgbtGreen := Couleur;
      rgbtBlue  := Couleur;
    end;
    Inc(CurrentScan); // A CHANGE !
  end;
  MainFiche.Canvas.Draw(0,0,Img);
end;
MainFiche.Canvas.TextOut(10,10,FormatFloat('0.0',1000 / (MilliSecondsBetween(Depart,Now) / 100)) + ' images par secondes');
end;

Merci Caribensila 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 68 internautes ce mois-ci

Commenter la réponse de Caribensila
Meilleure réponse
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 18 août 2010 à 17:36
3
Merci
Peut-être une solution :
Tu fais tous tes calculs en mémoire sur un bitmap qui aurait le plafond à gauche et le sol à droite, par exemple. Il serait donc plus haut que large et représenterait la scène pivotée de 90°.
Et au moment de l'afficher, le faire tourner de 90° (je crois qu'il y a une API qui fait ça très vite, je vais essayer de la retrouver...)

Merci Caribensila 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 68 internautes ce mois-ci

Commenter la réponse de Caribensila
Meilleure réponse
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 18 août 2010 à 22:19
3
Merci
bon alors en respectant toutes les conditions (normalement ) j'arrive pas mieux que 20 Img/s j'ai aussi essayé avec un pointeur sur tableau de RGBTriple mais à ce niveau ça n'apporte plus rien
[hr][b]type

  /bPDynRGBArray =  ^TDynRGBArray;
  TDynRGBArray =  array of  TRGBTriple;
  PDynRGBArrays  = ^TDynRGBArrays;
  TDynRGBArrays =   array of  TDynRGBArray;


procedure TMainFiche.Button4Click(Sender: TObject);
[b]var
  /bScan0: Integer;  //Valeur, en Integer, de la 1ère adresse de ScanLine.
  MemLineSize: Integer; //Taille d'une ligne de pixels en mémoire (en octets).
  BytesPerPix: Integer; //Format des pixels (en octets).
  CurrentScan: Integer;
  X, Y, P: Integer;
  Depart: TDateTime;
  couleur: Byte;
  V_SCR_MATRIX: TDynRGBArrays;
[b]begin
  /bScan0 : = Integer(Img.ScanLine[0]); //Pointe sur la 1ère ligne du Bitmap.
  //MemLineSize sera le plus souvent <0 permettant ainsi de décrémenter l'adresse du pointeur de ligne-mémoire (Y).
  MemLineSize :=  Integer(Img.ScanLine[1]) - Scan0;
   //BytesPerPix permettra d'incrémenter l'adresse du pointeur pRGBTriple en fonction de sa position dans la ligne (X).
  BytesPerPix : = Abs(MemLineSize  div  Img.Width);

  SetLength(V_SCR_MATRIX, Img.Width, Img.Height);


  Depart : =  Now;
  [b]try
    for /bP :=  0  to  99 [b]do
    begin
      for /bX : = 0 to 1679 [b]do
        for /bY :=  0  to  1049 [b]do
        begin
          with /bV_SCR_MATRIX[X, Y] [b]do
            if /b(X > 600) and (X < 800) and (Y > 450) and (Y < 734) [b]then
            begin
              /brgbtRed : = 62;
              rgbtGreen :=  145;
              rgbtBlue := 23;
            [b]end
            else
            begin
              /bCouleur := Random(256);
              rgbtRed := Couleur;
              rgbtGreen := Couleur;
              rgbtBlue := Couleur;
             end ;
        end;

      for Y : = 0 to 1049 [b]do
      begin
        /b // on se place en début de ligne
        CurrentScan : =  Scan0 + Y * MemLineSize;
         for  X : =  0 to 1679 [b]do
          /b //with pRGBTriple(CurrentScan)^ do
          [b]begin
            /bpRGBTriple(CurrentScan)^ : =  V_SCR_MATRIX[X,Y];
            {on incrémente de BytesPerPixel pour passer à la valeur suivante}
            Inc(CurrentScan, BytesPerPix);
           end ;
      end;
      MainFiche.Canvas.Draw(0, 0, Img);
    end;
  [b]finally
    /bFinalize(V_SCR_MATRIX);
  end;

  MainFiche.Caption : = FormatFloat('0.0', 1000 / (MilliSecondsBetween(Depart,
    Now) / 100)) + ' images par secondes';
end;
[hr]

j'ai pas mieux pour l'instant, ce qui est déjà pas mal, vu les contraintes


[hr]@+Cirec
[hr]

Merci Cirec 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 68 internautes ce mois-ci

Commenter la réponse de Cirec
md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention - 17 août 2010 à 11:10
0
Merci
Bonjour,

tout d'abord je voudrais m'excuser d'avoir posé un code merdique, je n'avais pas vu que le copier-coller avait bouffé les indentations !

merci Cirec, je vais essayer d'optimiser l'affichage avec scanline, ce code a l'air très intéressant.

Mais ce qui me pose un autre problème, ce sont les temps d'accès à mes trois matrices de nombres qui contiennent les codes pour l'affichage. J'ai simulé une double boucle qui rempli les matrices avec des valeurs aléatoires, le moteur, lui, dessine des murs, des sols, des plafonds, des sprites, ... avec différents effets. Avec du calcul matriciel les effets graphiques deviennent très simples.

Cependant, il me faut absolument deux doubles boucles, une pour calculer puis une pour afficher (avec scanline), la liaison entre ces deux boucles, ce sont ces 3 matrices de byte qui représentent les pixels de l'écran. J'aimerai donc également trouver un système qui permettrait de lire et d'écrire dans ces matrices plus rapidement, du genre : des codes pour booster les accès à la mémoire ou tout ce qui peut faire aller plus vite ces échanges. (pour rappel, avec juste l'affichage : 57 img/s, avec l'écriture puis lecture des matrices : 13 img/s dans les mêmes conditions.

Cependant, il est évident que si je peux augmenter la vitesse en optimisant Scanline, cela influera sur l'ensemble et c'est un progrès.

md21
Commenter la réponse de md21
md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention - 17 août 2010 à 16:12
0
Merci
bonjour,

j'ai modifié mes deux petits programmes tests en fonction des recommandations de Caribensila et Cirec concernant l'optimisation de Scanline. Sur le programme qui utilise uniquement l'affichage Scanline, je suis passé de 57 à 63 img/s dans les mêmes conditions de test (1680 x 1050 sur une série de 100 affichages). Ce gain de 6 img/s me parait intéressant et significatif.

Par contre, sur le programme utilisant les tableaux de nombres, je suis toujours bloqué à 13 img/s, ce qui semble prouver que ce sont bien les accès à ces tableaux de bytes qui ralentissent le traitement.

En tout cas merci, je suis bien sûr toujours intéressé par d'autres possibilités d'optimisation

md21
Commenter la réponse de md21
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 17 août 2010 à 17:57
0
Merci
petite erreur remplacer :
  V_SCR_MATRIX := AllocMem(Img.Width * Img.Height * 4);

par
  V_SCR_MATRIX := AllocMem(Img.Width * Img.Height * BytesPerPix);



[hr]@+Cirec
[hr]
Commenter la réponse de Cirec
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 17 août 2010 à 18:03
0
Merci
et préférer ceci:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_ESCAPE  then 
    Close; // manière correcte de fermer une application
end;


à ceci:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_ESCAPE  then 
    Application.Terminate; // manière brutale les évènements ne sont pas déclanchés
end;



ce qui dans le code de "Md21" aura pour effet de ne pas libérer le TBitmap (Img) !!


[hr]@+Cirec
[hr]
Commenter la réponse de Cirec
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 17 août 2010 à 18:21
0
Merci
... C'est vrai !
On ne claque pas la porte. On la 'close' délicatement...

On n'est pas des sauvages, quand même !
Commenter la réponse de Caribensila
md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention - 17 août 2010 à 18:58
0
Merci
merci pour ta réponse Caribensila,

je viens d'essayer tes nouveaux codes dans les mêmes conditions que précédemment et en générant pour chaque pixel un ton de gris aléatoire. En pf24bit ça tourne à 52 img/s et en pf32bit à 36 img/s.
Ton code précédent(légèrement modifié pour mes tests) :
procedure TMainFiche.FormClick(Sender: TObject);
var
  X,Y,P: Integer;
  Depart: TDateTime;
  CurrentScan: Integer;
  couleur: Byte;
begin
Depart := Now;
{Initialisation des paramètres d'affichage}
Scan0       := Integer(Img.ScanLine[0]);         // pointe sur la 1ère ligne du Bitmap
MemLineSize := Integer(Img.ScanLine[1]) - Scan0; // MemLineSize est le plus souvent < 0 et permet ainsi de décrémenter l'adresse du pointeur de ligne-mémoire (Y)
BytesPerPix := Abs( MemLineSize div Img.Width ); // BytesPerPix permet d'incrémenter l'adresse du pointeur pRGBTriple en fonction de sa position dans la ligne (X)
For P := 0 to 99 do
  Begin
    For Y := 0 to 1049 do
      Begin
        CurrentScan := Scan0 + (Y * MemLineSize); // on se place en début de ligne
        For X := 0 to 1679 do
          Begin
            Couleur := Random(256);
            With PRGBTriple(CurrentScan)^do
              Begin
                rgbtRed   := Couleur;
                rgbtGreen := Couleur;
                rgbtBlue  := Couleur;
              end;
            Inc(CurrentScan,BytesPerPix); // on incrémente de BytesPerPix pour passer à la valeur suivante
          end;
      end;
    MainFiche.Canvas.Draw(0,0,Img);
  end;
MainFiche.Caption := FormatFloat('0.0',1000 / (MilliSecondsBetween(Depart,Now) / 100)) + ' images par secondes';
end;

est le plus rapide jusqu'à présent, à savoir 63 img/s !

Le problème venant essentiellement de mes matrices de nombres à deux dimensions, je vais voir ce que je peux faire pour les remplacer avec un système utilisant des pointeurs. J'ai vu qu'il y avait un tuto la dessus (parce que les pointeurs je ne suis pas encore au top)

Pour donner quelques explications supplémentaires, je peux effectivement coder avec OpenGL, je sais le faire mais je souhaite développer un moteur utilisant de vieilles technologies pour plusieurs raisons (bien sûr si je n'y arrive pas je me rabattrai sur OpenGL) :

1 - au vu de mon âge (45 ans) j'ai connu les premiers jeux (style Wolfenstein mais beaucoup d'autres aussi) et je suis un peu nostalgique. Les jeux de cette époque utilisaient le ray-casting (méthode inventée par id Software qui est une version simplifiée du ray-tracing) et non les techniques de rasterisation employées par directx et opengl. Evidemment il faut savoir que les jeux de l'époque en question avaient une résolution de 320 x 200 pixels (et que la plupart du temps il n'utilisaient pas tout l'écran notamment à cause du HUD placé en bas)!

2 - la conception des niveaux est très simple : tu les monte en 2D et le moteur les joue en 3D :-) Il n'y a pas besoin de passer des dizaines d'heures sur un éditeur de niveaux parce que je suis tout seul pour tout faire, et pendant mon temps de loisir en plus.

3 - les monstres et les armes sont des sprites, ainsi que différents objets du décor, c'est moin bien tu vas dire ? Moi je préfère avoir de bons sprites qu'une mauvaise modélisation 3D, parce que je doute d'arriver à créer de beaux modèles. Tandis qu'avec les sprites ... pour les armes par ex ce sera ma main avec de vraies armes (photographies au numérique avec des réglage et des conditions particulières) alors forcément ça cause plus.

4 - Je ne peux pas concurencer toutes ces sociétés qui sortent des jeux magnifiques à longueur d'années. Recréer de l'ancien remis au gout du jour avec une bonne qualité, ça c'est nouveau et ça m'intéresse. Je l'ai déjà fait il y a plusieurs années en fabricant un gros casse-briques (140 niveaux quand même) qui est disponible sur www.dubois21.com (c'est le site d'un copain parce que je n'ai pas de site à moi); Ce jeu s'appelle Pictoïd et est disponible dans la rubtique téléchargement.

J'ai écrit ces quelques lignes pour expliquer aux personnes qui ont eu la gentillesse de me répondre (et aux autres aussi) ce que je cherche à faire, c'est la moindre des choses

md21
Commenter la réponse de md21
md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention - 17 août 2010 à 19:00
0
Merci
Merci Cirec, je viens de voir ton post, j'essaie dès demain et je te tiens au courant

md21
Commenter la réponse de md21
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 18 août 2010 à 12:38
0
Merci
@Caribensila:

déjà on ne part pas sur la même base de code ...
ton code ne fait qu'agir sur les pixels du bitmap
mais Md21 a dit qu'il avait besoin de Matrices où Tableaux pour stocker des données afin de les utiliser dans le code en les affectant aux pixels du Bitmap.
Donc j'ai repris son deuxième code d'exemple et je l'ai modifié de façon à ce que le code continue d'utiliser le principe Matrice/Tableau (sauf que c'est devenu un Pointeur) et du coup le code tourne 3 fois plus vite que la version de départ

Sinon ATTENTION aux idées reçues !!!!
En général, comme tu le sais, le pf32bit donne de meilleurs résultats car le proc est fait pour calculer avec des DWords...
je viens de faire l'essai ... en passant de pf24bit à pf32bit le Frame Rate est à nouveau divisé par 3 !!!!!



[hr]@+Cirec
[hr]
Commenter la réponse de Cirec
md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention - 18 août 2010 à 14:24
0
Merci
Bonjour

@Caribensila

effectivement, Cirec a raison, il me faut des matrices sous forme de tableaux à deux dimensions sinon je suis coincé.
Ton dernier code tourne à 60 img/s, mais, si je recalcule un X,Y du style (pour savoir sur quelle ligne ou quelle colonne je suis) :
  For I := 1 to (Img.Width * img.Height) do  // A CHANGE !
    Begin
      Y := I Div Img.Width;
      X := I - (Y * Img.Width);
    ...

je retombe à 15 img/s

@Cirec

le framerate se trouve effectivement multiplié par 3, j'ai obtenu 37 img/s (3 x 13 = 39)
Cependant, je crois qu'il y a un petit problème que je n'arrive pas à régler :
j'ai fait un test pour mettre un rectangle en incrustation (d'ailleurs si j'ai bien compris, ton code traite obligatoirement des lignes entières, je ne peux donc pas interagir sur une petite zone au milieu de la matrice pour implanter un élément de décor par ex autrement que par une condition if ? - je pense à une double boucle de petite taille ne touchant qu'une partie des pixels et décalée d'une valeur Xo, Yo ?).
Voici donc mon rectangle vert en incrustation, avec une condition if :
            with pRGBTriple(MATRIX_CurrentScan)^ do
              begin
                if (X > 600) And (X < 800) And (Y > 450) And (Y < 734) then
                  Begin
                    rgbtRed := 62;
                    rgbtGreen := 145;
                    rgbtBlue := 23;
                  end
                else
                  Begin
                    Couleur := Random(256);
                    rgbtRed := Couleur;
                    rgbtGreen := Couleur;
                    rgbtBlue := Couleur;
                  end;
              end;

et bien pour les Y ça à l'air OK, mais pour les X cela donne une bande floue qui se balade et se mélange aux pixels gris et je reconnais que je ne maîtrise pas assez ce code pour comprendre pourquoi.
Je ne comprends pas non plus pourquoi on est partout en pRGBTriple, et pour l'affichage en pRGBQuad juste 1 fois (ça c'est de la curiosité) ?

merci à tous les deux pour votre travail

md21
Commenter la réponse de md21
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 18 août 2010 à 15:24
0
Merci
tu avais tout simplement inversé X et Y pour la "matrice"
voici le code complet :
[hr]procedure TMainFiche.Button2Click(Sender: TObject);

[b]var
  /bScan0: Integer; //Valeur, en Integer, de la 1ère adresse de ScanLine.
  MemLineSize: Integer; //Taille d'une ligne de pixels en mémoire (en octets).
  BytesPerPix: Integer; //Format des pixels (en octets).
  CurrentScan: Integer;
  X, Y, P: Integer;
  Depart: TDateTime;
  couleur: Byte;
  V_SCR_MATRIX: pRGBTriple;
  MATRIX_Scan0: Integer; //Valeur, en Integer, de la 1ère adresse de ScanLine.
  MATRIX_CurrentScan: Integer;
[b]begin
  /bScan0 :=  Integer(Img.ScanLine[0]);  //Pointe sur la 1ère ligne du Bitmap.
  //MemLineSize sera le plus souvent <0 permettant ainsi de décrémenter l'adresse du pointeur de ligne-mémoire (Y).
  MemLineSize : = Integer(Img.ScanLine[1]) - Scan0;
  //BytesPerPix permettra d'incrémenter l'adresse du pointeur pRGBTriple en fonction de sa position dans la ligne (X).
  BytesPerPix :=  Abs(MemLineSize  div  Img.Width);

  V_SCR_MATRIX : = AllocMem(Img.Width * Img.Height * BytesPerPix);

  MATRIX_Scan0 :=  Integer(V_SCR_MATRIX);

  Depart := Now;
  [b]try
    for /bP := 0  to  99 [b]do
    begin
      /bMATRIX_CurrentScan : = MATRIX_Scan0;
       // ****** ici tu avais inversé X et Y ********
      for Y : =  0  to  1049 [b]do
        for /bX : =  0 to 1679 [b]do
        begin
          with /bpRGBTriple(MATRIX_CurrentScan)^ [b]do
          if /b(X > 600) and (X < 800) and (Y > 450) and (Y < 734) [b]then
            begin
              /brgbtRed :=  62;
              rgbtGreen := 145;
              rgbtBlue := 23;
            [b]end
          else
          begin
            /bCouleur := Random(256);
            rgbtRed := Couleur;
            rgbtGreen := Couleur;
            rgbtBlue := Couleur;
           end ;
          Inc(MATRIX_CurrentScan, BytesPerPix);
        end;

      MATRIX_CurrentScan : = MATRIX_Scan0;
      for Y :=  0  to  1049 [b]do
      begin
        /b // on se place en début de ligne
        CurrentScan : = Scan0 + Y * MemLineSize;
        for X :=  0  to  1679 [b]do
          with /bpRGBTriple(CurrentScan)^ [b]do
          begin
            /brgbtRed : = pRGBTriple(MATRIX_CurrentScan)^.rgbtRed;
            rgbtGreen :=  pRGBTriple(MATRIX_CurrentScan)^.rgbtGreen;
            rgbtBlue := pRGBTriple(MATRIX_CurrentScan)^.rgbtBlue;
              {on incrémente de BytesPerPixel pour passer à la valeur suivante}
            Inc(CurrentScan, BytesPerPix);
            Inc(MATRIX_CurrentScan, BytesPerPix);
           end ;
      end;
      MainFiche.Canvas.Draw(0, 0, Img);
    end;
  [b]finally
    /bReallocMem(V_SCR_MATRIX, 0);
  end;

  MainFiche.Caption : = FormatFloat('0.0', 1000 / (MilliSecondsBetween(Depart,
    Now) / 100)) + ' images par secondes';
end;
[hr]

pour le RGBTriple & RGBQuad dans ce contexte ça n'a aucune importance c'est juste un "trantypage" :

on a une adresse de pointeur "CurrentScan" que l'on demande: pRGBTriple(CurrentScan)^ où pRGBQuad(CurrentScan)^ Delphi fait très bien et en toute transparence le "transtypage"

mais dans un souci de clarté et de cohérence (bitmap à pf24bit) j'ai tout remis en "pRGBTriple"


[hr]@+Cirec
[hr]
Commenter la réponse de Cirec
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 18 août 2010 à 15:36
0
Merci
Ah! Je comprends mieux...

C'est pas md21 qui a inversé, Cirec.
C'est ton 1er code qui créait une matrice plus haute que large...
Ca n'avait pas d'importance pour les gris, mais ça ne pouvait pas représenter le BMP...

( Ca me semblait bien zarbi, mais j'ai cru que j'avais manqué qq chose... Désolé. )
Commenter la réponse de Caribensila
Cirec 4231 Messages postés vendredi 23 juillet 2004Date d'inscription 3 août 2018 Dernière intervention - 18 août 2010 à 15:48
0
Merci
[quote=Caribensila]C'est pas md21 qui a inversé, Cirec.
C'est ton 1er code qui créait une matrice plus haute que large... /quote

si si ..
je constate que tu ne regardes toujours pas le bon code d'exemple que Md21 a donné

dans son tout premier message il y a 2 exemples de code ... je suis parti du deuxième et j'ai pas fait attention que X et Y y étaient inversés ... comme tu le dis "Ca n'avait pas d'importance pour les gris"
c'est en voulant ajouter un rectangle vert que le problème est devenu visible


[hr]@+Cirec
[hr]
Commenter la réponse de Cirec
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 18 août 2010 à 15:59
0
Merci
C'est vrai. Je ne suis pas 'rentré' dans le code de md21

Mais peu importe...
Comme on dit : « le diable se cache dans les détails »
Et c'est particulièrement vrai en développement.
Commenter la réponse de Caribensila
md21 32 Messages postés mercredi 10 janvier 2007Date d'inscription 1 septembre 2015 Dernière intervention - 18 août 2010 à 17:08
0
Merci
@Cirec

hélas, je n'ai pas inversé les X et les Y

Lors des calculs on est en ray-casting, c'est à dire qu'il faut décrire des bandes verticales à l'écran
donc For X := 0 to ...
For Y := 0 to ...
chaque bande verticale correspond à un rayon qui a une longueur définie par un algorytme de collision avec les murs ou un élément du décor. Ensuite, après une correction "sphérique" qui sert à enlever les déformations provoquées par ce procédé, tu obtiens la hauteur (en pixels) du mur ou de l'élément qui correspond.
Une bande verticale est donc généralement constituée tout d'abord d'un morceau de plafond (sauf si on est à ciel ouvert), puis d'un mur, et enfin d'un morceau de sol. Puis tu ajoutes les textures et les effets de lumière sur ces différents éléments. Ensuite il y a encore d'autres phases de calcul pour intégrer le HUD, etc ... c'est pourquoi je voudrais tout afficher en une seule fois à la fin pour gagner du temp au moyen d'une deuxième double boucle. Si j'inverse les X et les Y dans la partie calculs je vais me retrouver avec le sol vertical à gauche de l'écran et un écran plus haut que large.

lors de l'affichage, comme j'avais bien compris que Scanline est très rapide (d'ailleurs encore plus que l'api bitblt que j'ai beaucoup utilisé), il faut décrire des bandes horizontales et non verticales
donc For Y := 0 to
For X := 0 to

Malheureusement pour moi je n'avais pas inversé les X et les Y, c'était volontaire et obligatoire. Je n'avais pas encore pensé à ce problème avec ta solution avec les pointeurs parce qu'avec les matrices de nombres il n'y en avait pas. Je ne sais pas si il est possible de faire quelque chose, ton code est vraiment très rapide et me convenait bien.

md21
Commenter la réponse de md21

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.