Défi Fonction 2D d'éclairage super optimisé

Programmation d'applications complexes
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Défi Fonction 2D d'éclairage super optimisé

Message par Thyphoon »

Si certain d'entre vous aime se prendre la tête voici un petit defi. ça fait 2 semaines que je bosse dessus et j'ai pas encore réussi a faire ce que j'avais prévu.

Je prépare un Jeu 2D vu de dessus ou des personnes se promène avec une lampe de poche. bien entendu on ne voit que ce qui est éclairé par la lampe de poche.
Image
chaque lampe est determiné par cette fonction:

Light(x.l,y.l,Angle.l,Anglef.l,Puissance.l,Couleur.l)

x et y sont les coordonnées de la source lumineuse
Angle = direction de la lumière
Anglef = A l'angle du faisseau lumineux (largeur)
Puissance = A la puissance du faisseau lumineux
Couleur = la couleur de la lampe.

Le decor est formé de 2 images
Image 1:il s'agit du décore vu de dessus
Image 2:un masque de l'image en niveau de gris
ou la couleur 0 correspont au sol et 255 a une hauteur de 2m50

Il peut y avoir plusieur lampe en même temps et de couleur différente.
La lumière est diffusé en fonction de la hauteur du sol (bump mapping) (a 2m50 la lumière ne vas pas plus loin)

voici ce que ça devrait donné (PLus ou moins)

Le but est que ça tourne le plus rapidement possible 50im/sec si possible :P

voilà..

De mon côté j'ai juste le faisceau lumineux avec ce qu'il éclaire...mais sans bumpmapping et je ne suis qu'a une image par seconde :(
Si vous faites mieux ...
Anonyme

Message par Anonyme »

Un truc tout bête, pourquoi n'utilise tu pas les sprites3D avec leur effets ?
ou alors , si j'ai bien compris ton MP tu désire faire ce genre de chose :

Image

C'est ni plus ni moins qu'un lancer de rayon2D à partir de la source qui s'arrete quand il touche un objet à la meme hauteur.

Tout dabord , moi si j'étais amener à codé ce genre de chose, je ferais de la manière suivante pour commencer :

1 Tableau contenant les couleurs de l'image de fond
1 qui Contient la Source lumineuse ( à l'aide d'un bitmap par exemple)
1 Tableau qui servira de Buffer final

ensuite je ferais une procedure, qui Additionne la couleur de fond à la couleur de la source lumineuse , je le ferais de pixel en pixel. en partant de la source, jusqu'a la portée de la lampe, ou si collision avec un objet.

C'est super lent , mais le principe est là, je te conseille donc de stocké l'image de fond avec les objets de collisions dans un tableau à la place de point() pour un gain de temps cpu.

Code : Tout sélectionner

InitSprite()
InitKeyboard()
OpenScreen(800,600,32,"")




Global LightX.f=400
Global LightY.f=300
Global LightA.f=0



Procedure DessineCone(Angle.l)
Shared X.f,Y.f,Color.l
StartDrawing(ScreenOutput())

Circle(400,400,10,RGB(255,255,255))


For i = -Angle/2 To Angle/2

For p = 0 To 250
   X = LightX + P * Cos( (i+LightA) *2*3.1415/360)
   Y = LightY + P * Sin((i+LightA) *2*3.1415/360)
Color = Point(x,y)
If Color<>$FFFFFF : Plot(X,Y,RGB(238, 237, 17)) : Else  : Goto nextAngle: :EndIf

Next p    
nextAngle:  
Next i
StopDrawing()
EndProcedure




Repeat
ExamineKeyboard() : ClearScreen(RGB(0,0,0))
DessineCone(45)
LightA+1
FlipBuffers()
Until KeyboardPushed(#PB_Key_Escape)
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

Cpl.Bator a écrit :Un truc tout bête, pourquoi n'utilise tu pas les sprites3D avec leur effets ?
ou alors , si j'ai bien compris ton MP tu désire faire ce genre de chose :

Image
Tu as tout compris. Dans ma version j'utilise déjà les tableaux. mais bon rien que le fait de faire ImageToTable(1,Image()) puis TableToImage(1,Image()) apres avoir fait les modifications dansle tableau je passe de 50 FPS a 1 FPS...Gloups..... Bref c'est trop lent pour utiliser ça dans un jeu ...Si tu as une idée miracle pour optimisé ça. :P
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

Cpl.Bator a écrit :Un truc tout bête, pourquoi n'utilise tu pas les sprites3D avec leur effets ?
ou alors , si j'ai bien compris ton MP tu désire faire ce genre de chose :

Image
Tu as tout compris. Dans ma version j'utilise déjà les tableaux. mais bon rien que le fait de faire ImageToTable(1,Image()) puis TableToImage(1,Image()) apres avoir fait les modifications dansle tableau je passe de 50 FPS a 1 FPS...Gloups..... Bref c'est trop lent pour utiliser ça dans un jeu ...Si tu as une idée miracle pour optimisé ça. :P
Anonyme

Message par Anonyme »

Pour l'optimisation il n'y a pas de secret, le hardware ! pour cela il faut programmer en cg pour avoir accès au shaders & cie, file ton code, je vais regarder de plus près.

@++
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

Cpl.Bator a écrit :Pour l'optimisation il n'y a pas de secret, le hardware ! pour cela il faut programmer en cg pour avoir accès au shaders & cie, file ton code, je vais regarder de plus près.

@++
C'est sympa...je fait un peu de nettoyage car a force de faire des tests ça ne ressemble plus a rien et je le :D poste ici
Fred
Site Admin
Messages : 2808
Inscription : mer. 21/janv./2004 11:03

Message par Fred »

Tu peux pas garder tout le temps le tableau en memoire ? Par exemple un tableau de bytes qui contient juste les obstacles du decors.
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

un truc un peu comme ca ?
vu.exe

j'avais codé une autre version avec des Sprite3D mais j'ai perdu les sources dans le crash de mon dur. Le principe avec les Sprite3D c'était que j'avais un sprite tout noir qui me servait d'ombrage plus ou moins intense en fonction de la valeur "Alpha"

je dois pouvoir le retrouver, je l'avais fait tester par d'autres personnes mais bon pour les sources c'est mort...

Dri
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

Fred a écrit :Tu peux pas garder tout le temps le tableau en memoire ? Par exemple un tableau de bytes qui contient juste les obstacles du decors.
Je voulais pouvoir avoir un sol qui ne soit pas uniforme si j'utilise des bytes alors il faut que je marche par case...passe ou passe pas. Mais peut être que j'y arriverait !
Dr. Dri a écrit : un truc un peu comme ca ?
vu.exe

j'avais codé une autre version avec des Sprite3D mais j'ai perdu les sources dans le crash de mon dur. Le principe avec les Sprite3D c'était que j'avais un sprite tout noir qui me servait d'ombrage plus ou moins intense en fonction de la valeur "Alpha"

je dois pouvoir le retrouver, je l'avais fait tester par d'autres personnes mais bon pour les sources c'est mort...
.
Oui c'est un peu prêt ça ! J'ai pas essayer pour les Sprite3D mais si tu retrouve quelques choses n'hesite pas...peut être que ça me donner des idées :P


Bon voici le code que j'ai fait...ça marche super lentement...et je suis pas vraiment satisfait du rendu...mais si vous savez comment optimiser tout ça et améliorer le rendu je suis preneur. Dans mon idée de depart je voulais en même temps faire un bump-mapping pour améliorer l'effet d'optique mais je crois queje peu abandonner. voilà
Il vous faut juste une image.png en 800x600
ce n'est peut être pas assez documenté. si vous avez des questions n'hesitez pas

Code : Tout sélectionner

InitSprite() 
InitKeyboard() 
OpenScreen(800,600,32,"") 
UsePNGImageDecoder()
LoadImage(1,"image.png")
CreateImage(2,800,600,32)
Global Dim Image(800,600)
Global Dim LightMask(800,600)
Global Dim Mask(800,600)
Global Dim ImageFinal(800,600)

Procedure ImageToTable(Image, Table) ; Retourne 1 si l'image a été chargée dans le tableau ImageID=#Image : Table=@Tableau(), ex : Dim Tableau(ImageWidth(), ImageHeight()) -> @Tableau() 
If Image>=0 And Table 
ImageID = ImageID (Image) 
Hdc = CreateCompatibleDC_ ( GetDC_ ( ImageID )) 
If HDC 
bmi.BITMAPINFO 
bm.BITMAP 
GetObject_ ( ImageID , SizeOf(BITMAP), @bm.BITMAP) 
bmi\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER) 
bmi\bmiheader\biWidth = bm\bmWidth 
bmi\bmiheader\biHeight = bm\bmHeight 
bmi\bmiheader\biPlanes = 1 
bmi\bmiheader\biBitCount = 32 
bmi\bmiheader\biCompression = #BI_RGB 
HList = AllocateMemory (bm\bmWidth*bm\bmHeight*4) 
GetDIBits_ (hDC, ImageID ,0,bm\bmHeight,HList,bmi, #DIB_RGB_COLORS ) 

For nn = 0 To bm\bmWidth - 1 
For n = 0 To bm\bmHeight - 1 
s = HList + nn * 4 + (bm\bmHeight - 1 - n) * bm\bmWidth * 4 
d = Table + n * 4 + nn * bm\bmHeight * 4 
CopyMemory (s + 2, d, 1) 
CopyMemory (s + 1, d + 1, 1) 
CopyMemory (s, d + 2, 1) 
Next n 
Table+4 
Next nn 
FreeMemory (HList) 
Else 
ProcedureReturn 
EndIf 
ProcedureReturn 1 
EndIf 
EndProcedure 

Procedure TableToImage(Image, Table) ; Crée une image à partir du tableau Image=#Image, Table=@Tableau(), ex : Dim Tableau(ImageWidth(),ImageHeight()) -> @Tableau() 
If IsImage (Image) And Table 
ImageID = ImageID (Image) 
bm.BITMAP 
GetObject_ ( ImageID , SizeOf(BITMAP), @bm.BITMAP) 
bmi.BITMAPINFO 
bmi\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER) 
bmi\bmiheader\biWidth = bm\bmWidth 
bmi\bmiheader\biHeight = bm\bmHeight 
bmi\bmiheader\biPlanes = 1 
bmi\bmiheader\biBitCount = 32 
bmi\bmiheader\biCompression = #BI_RGB 
pixel= AllocateMemory (bm\bmHeight*bm\bmWidth*4) 
For nn = 0 To bm\bmwidth - 1 
For n = 0 To bm\bmheight - 1 
s = Table + n * 4 + nn * bm\bmHeight * 4 
d = pixel + nn * 4 + (bm\bmHeight - 1 - n) * bm\bmWidth * 4 
CopyMemory (s, d + 2, 1) 
CopyMemory (s + 1, d + 1, 1) 
CopyMemory (s + 2, d, 1) 
Next 
table + 4 
Next 
HDC= StartDrawing ( ImageOutput (Image)) 
SetDIBits_ (HDC, ImageID ,0, ImageHeight (Image), pixel, bmi, #DIB_RGB_COLORS ) 
StopDrawing () 
FreeMemory (pixel) 
ProcedureReturn 1 
EndIf 
EndProcedure 

Procedure Light(LightX.l,LightY.l,Angle.l,Anglef.l,Puissance.l,Color.l) 
StartDrawing(ScreenOutput()) 
  For i = -Anglef To Anglef 
    For p = 0 To Puissance 
      X = LightX + P * Cos( (i/2+Angle) *2*3.1415/360) 
      Y = LightY + P * Sin((i/2+Angle) *2*3.1415/360) 
      lightlevel=((Puissance-p)*255/Puissance+(255*(Anglef/2-Abs(i))/Anglef/2))/2;ça donne la forme de la luminosité
      ;lightlevel=(Puissance-p)*255/Puissance
      red=lightlevel*Red(Color)/255
      green=lightlevel*Green(Color)/255
      blue=lightlevel*Blue(Color)/255
      ;LightMask(X,Y)=RGB(red,green,blue)
      If X>0 And X<800 And Y>0 And Y<600
        If LightMask(X,Y)>1
          LightMask(X,Y)=RGB((Red(LightMask(X,Y))+lightlevel),(Green(LightMask(X,Y))+lightlevel),(Blue(LightMask(X,Y))+lightlevel))
          If LightMask(X,Y)>RGB(255,255,255):LightMask(X,Y)=RGB(255,255,255):EndIf
        Else
          LightMask(X,Y)=RGB(lightlevel,lightlevel,lightlevel)
        EndIf
      EndIf
      ;On verfie maintenant si quelques choses est sur le chemin du rayon
      Color = Mask(X,Y) 
      If Color>1: Goto nextAngle: :EndIf ; si c'est le cas on arrête le rayon et on passe au suivant
    Next p    
    nextAngle:  
  Next i 
StopDrawing() 
EndProcedure 

;Fusion des masks sur le buffer
Procedure Display()
For zx=0 To 800
  For zy=0 To 600
    Light=LightMask(zx,zy);luminosité et couleur de la pixel
    If Light>1
      col=Image(zx,zy); couleur du decore
      red=Int(Red(col)*Red(Light)/255)
      green=Int(Green(col)*Red(Light)/255)
      blue=Int(Blue(col)*Red(Light)/255)
      col=RGB(red,green,blue)  
    
      ImageFinal(zx,zy)=col
      LightMask(zx,zy)=0
    Else 
      ImageFinal(zx,zy)=0 
    EndIf
  Next
Next

EndProcedure

ImageToTable(1,Image()) ; Le decor


Repeat 
ExamineKeyboard() :
Light(320,300,A,30,280,RGB(255,255,255))
Light(400,250,-A,60,150,RGB(255,255,255))
Display()
A=A+10
If A>360:A=0:EndIf 
TableToImage(2,ImageFinal())

  If Val(FormatDate("%ss", Date()))=sek
    FPS+1
  Else
    FPS$=Str(FPS)
    FPS=0
  EndIf
  sek=Val(FormatDate("%ss", Date()))
StartDrawing(ScreenOutput())
DrawImage(ImageID(2), 0, 0) 
 DrawingMode(1)
    FrontColor(RGB(255,255,255))
    DrawText(320,1,"FPS: "+FPS$)
  StopDrawing()
FlipBuffers() 
Until KeyboardPushed(#PB_Key_Escape)
Anonyme

Message par Anonyme »

Difficile de faire plus optimiser...
Mais! il est possible de gagner quelques FPS.

Tes procèdures TableToImage() & ImageToTable() bouffe trop de temps CPU, tu devrais dans c'est cas là , allèger son procéssus, je m'explique :

Au lieu de mettre à jour tous le tableau, pourquoi ne pas mettre à jour seulement la zone concernée? fait la meme chose pour display() tu devrais avoir un gain considérable.

@++
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

J'ai essayer d'optimiser mais je ne gagne rien... :( doit bien y avoir une solution ....je continue a chercher... Si quelqu'un me trouve une solution je suis prenneur !! :)
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Message par djes »

Je vois bien une solution rapide (sans trop se casser la tête avec du lancer de rayon).

Il faudrait redessiner le masque de la lampe. Au départ celui-ci consiste en un quart de cercle rempli de blanc.

Tu redessines dedans le masque des objets qui cachent la lumière. Pour chacun de ces objets tu dessines un polygo noir à partir des points du contour qui sont visibles par la lampe (on part du plus proche et on fait les autres dans l'ordre chrono pour savoir si un contour ne les cache pas) jusqu'à une distance respectable (petit calcul position_bord-position_lampe*n).

Ensuite tu te sers du masque "normalement" pour afficher ta lampe.

Ca devrait marcher pas trop mal ;)
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

djes a écrit :Je vois bien une solution rapide (sans trop se casser la tête avec du lancer de rayon).

Il faudrait redessiner le masque de la lampe. Au départ celui-ci consiste en un quart de cercle rempli de blanc.

Tu redessines dedans le masque des objets qui cachent la lumière. Pour chacun de ces objets tu dessines un polygo noir à partir des points du contour qui sont visibles par la lampe (on part du plus proche et on fait les autres dans l'ordre chrono pour savoir si un contour ne les cache pas) jusqu'à une distance respectable (petit calcul position_bord-position_lampe*n).

Ensuite tu te sers du masque "normalement" pour afficher ta lampe.

Ca devrait marcher pas trop mal ;)
j'avais déjà pensé a ça...mais comment tu trace ton quart de cercle rempli de blanc rapidement ? d'ailleur normalement il ne doit pas être tout blanc il doit y avoir différente nuance...(plus ou moins lumineux selon la distance du point lumineux)

Je crois que j'ai peut être trouvé un truc..mais j'ai un bug testé le

l'idée est d'avoir en global l'adresse mémoire de l'ecran et des différentes images. ensuite je fais un pokel pour tracer et un peekl pour prendre la couleur...
j'ai remarqué d'ailleur que si je place le poke dans une autre procedure ça divise par 2 les fps....
mais je dois avoir un petit bugs car il y a un bug de rendu.
a mon avis il s'agit de cette ligne
PokeL(*pixel+DrawingBufferPitch()*y+DrawingBufferPixelFormat()*x,RGB(255,0,255))

Code : Tout sélectionner

InitSprite() 
InitKeyboard() 
OpenScreen(800,600,32,"") 
UsePNGImageDecoder() 
;LoadImage(1,"image.png") 
CreateImage(2,800,600,16) 

Structure Surface
  pixel.l
EndStructure
StartDrawing(ScreenOutput())
Global *pixel.Surface=DrawingBuffer()
StopDrawing()

#Rad=2*3.1415/360
Procedure Light(LightX.l,LightY.l,Angle.l,Anglef.l,Puissance.l,Color.l) 
;StartDrawing(ScreenOutput()) 
  For i = -Anglef*2 To Anglef*2 Step 2
    For p = 0 To Puissance 
      a.f=(i/4+Angle) 
      X = LightX + P * Cos(a*#Rad)
      Y = LightY + P * Sin(a*#Rad)
      ;lightlevel=((Puissance-p)*255/Puissance+(255*(Anglef/2-Abs(i))/Anglef/2))/2;ça donne la forme de la luminosité 
      ;If X>0 And X<800 And Y>0 And Y<600 
      PokeL(*pixel+DrawingBufferPitch()*y+DrawingBufferPixelFormat()*x,RGB(255,0,255))
          ;Plot(X,Y,RGB(255,255,255))
      ;EndIf 
      ;On verfie maintenant si quelques choses est sur le chemin du rayon 
      ;Color = Mask(X,Y) 
      ;If Color>1: Goto nextAngle: :EndIf ; si c'est le cas on arrête le rayon et on passe au suivant 
    Next p    
    nextAngle:  
  Next i 
;StopDrawing() 
EndProcedure 


Repeat 
ClearScreen(0)
ExamineKeyboard() :
Light(320,300,A,30,280,RGB(255,255,255)) 
Light(400,250,-A,60,150,RGB(255,255,255)) 
A=A+1
If A>360:A=A-360:EndIf
  If Val(FormatDate("%ss", Date()))=sek 
    FPS+1 
  Else 
    FPS$=Str(FPS) 
    FPS=0 
  EndIf 
  sek=Val(FormatDate("%ss", Date())) 
StartDrawing(ScreenOutput()) 

 DrawingMode(1) 
    FrontColor(RGB(255,255,255)) 
    DrawText(320,1,"FPS: "+FPS$) 
  StopDrawing() 
FlipBuffers() 
Until KeyboardPushed(#PB_Key_Escape)
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Message par djes »

Ben ton masque tu le fais avec un logiciel de dessin, genre photoshop, avec un dégradé central (et une couche alpha), et tu le charges tout simplement.
Des peeks, des pokes et tout ce que tu veux, ça sera toujours bcp plus lent.
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Message par Thyphoon »

djes a écrit :Ben ton masque tu le fais avec un logiciel de dessin, genre photoshop, avec un dégradé central (et une couche alpha), et tu le charges tout simplement.
Des peeks, des pokes et tout ce que tu veux, ça sera toujours bcp plus lent.
Tu as surement raison, mais comment fais tu si mon faisceau lumineu passe devant un poteau...a la limite j'ai le mask du poteau mais ça ne fera qu'un trou dans le faisceau. Tu vois ce que je veux dire ?
Répondre