Ein Flugzeugballerspiel - Ich brauch mal ein wenig Profihilfe



  • Ich verstehe, nunja...
    Gehen wir mal davon aus, dass ich jetzt "transparente" Schüsse machen möchte...
    meinst du, es wird am Ende mit GDI noch schnell genug laufen, dass man es noch ordentlich spielen kann, schließlich geht man ja JEDEN Pixel durch, das ist doch später dann ziemlich langsam, wenn ich mehrere transparente Objekte einbauen möchte. Du hast mir auf jeden Fall sehr viel geholfen, den Code zu optimieren und es am Ende richtig flüssig zu bekommen, ohne dich hätte ich sichrlich jetzt immernoch sowas wie eine Diasshow, danke hellihjb^^

    http://home.arcor.de/bigdeak/TCall678_5.zip <--- Hier ist nun noch eine Version, dein letzter Post mit dem Alphavorschlag ist da noch nicht drin, aber ich werde mich denke mal danach richten.
    Übrigens, starte mal Project4.exe (Wegen der Gewichtung, die ich meine) und Project6.exe (Hier hab ich mithilfe dieser Sache einfach mal einen kleinen Grafischen Effekt eingebaut, was man von aktuelleren Spielen kennt :D)

    Und Anfangs dachte ich noch, dass es unmöglich sei, ohne OpenGL oder DirectX AlphaBlending zu programmieren, welches schnell genug sei...



  • Im Grunde genommen ist die API egal, denn du kannst ja alles im RAM machen, und brauchst dann nur einen Call, um alles auf den Screen zu bringen. Ob das schnell genug sein kann, hängt am Ende grösstenteils von der gewählten Auflösung und Farbtiefe ab. Es gibt jedenfalls eins paar fixe Softwarerenderer auf dem Markt. f'`8k

    Autocogito

    Gruß, TGGC (\-/ has leading)



  • meinst du, es wird am Ende mit GDI noch schnell genug laufen, dass man es noch ordentlich spielen kann
    schließlich geht man ja JEDEN Pixel durch, das ist doch später dann ziemlich langsam

    der offensichtliche vorteil von direct3d und opengl ist natuerlich, dass das ganze rendering hardware-beschleunigt wird, waehrend du dich bei gdi (oder vergleichbaren apis wie directdraw oder sdl) auf cpu-geschwindigkeit verlassen musst.
    am besten du machst mal ein paar benchmarks, wieviele objekte du per gdi in hinreichender framerate darstellen kannst.
    nun ist gdi auch fuer gaenzlich andere anwendungen gedacht, darum sollte man noch herauszufinden, in wie weit Graphics::TBitmap::Scanline() evtl overhead kostet.

    grundsaetzlich solltest du bei deinen farboperationen immer ueberlegen ob es ueberhaupt notwenig ist, in einzelnen rgb-komponenten zu zerlegen:

    // aus unit1.h:
      Rgb1 = ptr[cc];
      Rgb2 = ptrM[cc];
    
      B=(B*64>>7)+(B2*192>>7)>>1;
      G=(G*64>>7)+(G2*192>>7)>>1;
      R=(R*64>>7)+(R2*192>>7)>>1;
    =>
      // rgb1*0.25 + rgb2*0.75
      B=(B>>2)+(B2*192>>8);
      G=(G>>2)+(G2*192>>8);
      R=(R>>2)+(R2*192>>8);
    =>
      // rgb1*0.25 + rgb2*0.5 + rgb2*0.25
      Rgb= (Rgb1>>2&0x3f3f3f) + (Rgb2>>1&0x7f7f7f) + (Rgb2>>2&0x3f3f3f);
    

    in diesem fall kannst du mit einem shift alle 3 rgb-komponenten "dividieren", wobei die unteren bits der "hoeheren" komponente in die oberen bits der darunterliegenden komponente verschoben werden. die kannst du einfach mit einer bitmaske entfernen in der die entsprechenden bits 0 sind.
    das ergebnis wird leicht abweichend sein weil 2x gerundet wird.



  • Damit wird es dann wieder schwieriger und aufwendiger, dynmaische Durchsichtigbarkeit einzubauen (Siehe Project4.exe). Ich habe das mal geändert, wie du es da vorgeschlagen hast, und zwar so:

    for (ff=0;ff<370;ff=ff+qual)
    {
     ptr = (unsigned int*)Buffer->ScanLine[ff];
     ptrM =(unsigned int*)BufferM->ScanLine[ff];
     for (cc=0;cc<500;cc=cc+1)
     {
      ptr[cc]=(ptr[cc]>>1&0x7f7f7f)+(ptrM[cc]>>1&0x7f7f7f);
      //Alles in eine Zeile gekürzt
     }
    }
    

    Auf diese Weise ist es insgesamt nochmal ein Stück schneller, ja...
    Allerdings gehts damit nur statisch, und kaum noch dynamisch, wenn du mir jetzt noch nen schnellen Vorschlag machst, wie ich die Durchsichtigkeit mit dieser Lösungsart noch dynmaischer mache, ohne großen Aufwand, wäre das sehr toll ^^



  • Allerdings gehts damit nur statisch, wenn du mir jetzt noch nen schnellen Vorschlag machst,
    wie ich die Durchsichtigkeit noch dynmaischer mache, wäre das sehr toll

    das:

    B=(B*64>>7)+(B2*192>>7)>>1;
    

    ...halte ich fuer sehr statisch 🙂
    du muesstest also erstmal spezifizieren, welche konstanten eigentlich variablen sein sollten und in welchem wertebereich sich diese bewegen.



  • Arf, den Wert hab ich nur statisch angegeben, aber eigentlich sollte es so gehen:

    B=(B*64>>7)+(B2*192>>7)>>1;
    =>
     B=(B*(128-alpha)>>7)+(B2*(128+alpha)>>7)>>1;
    


  • B=(B*alpha>>7)+(B2*(255-alpha)>>7)>>1;
    

    um das ganze performanter zu loesen, kann man sich mmx zunutze machen.
    leider kann man den optimizer nur selten dazu ueberreden, solche konstruktionen selbststaendig zu erzeugen.
    und weil mir gerade langweilig war 😉 zeige ich mal wie das aussehen koennte:

    void alphaBlendScanline(unsigned int *dst, unsigned int *src, int alpha, int len)
    {
       if (alpha<0) return; // komplett durchsichtig: es gibt nichts zu tun
       if (alpha>255) alpha=255; // evtl spezialfall-behandlung: komplett undurchsichtig -> kein blending
       unsigned int a1= alpha * 0x01010101; // = (alpha<<24)|(alpha<<16)|(alpha<<8)|(alpha);
       unsigned int a2= ~a1; // 255-alpha in jeder komponente
    
       _asm {
          mov       edi,dst   // addresse dst
          mov       esi,src   // addresse src
          mov       ecx,len   // loop-counter
    
          pxor      mm7,mm7   // 0
    
          movd      mm2,a1    // 32bit alpha in 64bit-mmx-register laden: 0|0|0|0|a|a|a|a (8x8bit)
          punpcklbw mm2,mm7   // 8x8bit in 4x16bit mit 0 erweitern: 0|0|0|0|a|a|a|a -> 0.a|0.a|0.a|0.a
    
          movd      mm3,a2    // 255-alpha: 0|0|0|0|a|a|a|a
          punpcklbw mm3,mm7   //  0.a|0.a|0.a|0.a
    
       blendloop:
          movd      mm0,[edi] // mm0= *dst => 32bit farbe in 64bit-mmx-register lesen: 0|0|0|0|a|r|g|b (8x8bit)
          punpcklbw mm0, mm7  // 8x8bit in 4x16bit erweitern: mm0= 0.a|0.r|0.g|0.b
          pmullw    mm0, mm3  // 4x 16bit multiply: mm0= a*a2|r*a2|g*a2|b*a2
    
          movd      mm1,[esi] // das gleiche nochmal mit src[0]
          punpcklbw mm1, mm7  
          pmullw    mm1, mm2  // multiply mit a1
    
          paddusw   mm0, mm1  // mm0 += mm1 => 4x 16bit addition: a1+a2|r1+r2|g1+g2|b1+b2
          psrlw     mm0, 8    // mm0= 0.a|0.r|0.g|0.b (4x >>8)
          packuswb  mm0, mm7  // mm0= 0|0|0|0|a|r|g|b (8x8bit)
    
          movd      [edi], mm0 // *dst= mm0  32bit-farbe schreiben
          add       edi,4      // dst++
          add       esi,4      // src++
          dec       ecx        // len--
          jnz       blendloop  // while (len>0)
          emms                 // mmx-ende: fpu-register wieder herstellen
       };
    }
    
    for (ff=0;ff<370;ff=ff+qual)
    {
     ptr = (unsigned int*)Buffer->ScanLine[ff];
     ptrM =(unsigned int*)BufferM->ScanLine[ff];
     alphaBlendScanline(ptr, ptrM, alpha, 500);
    }
    

    ...was hier passiert:
    mmx bietet 64bit register und die moeglichkeit in einem taktzyklus 4 16bit-zahlen (die "nebeneinander" in ein register passen) parallel zu multiplizieren.
    zu diesem zwecke liesst man also eine 32bit farbe im format 4x8bit a|r|g|b (oder andersrum; is auch egal) in ein (64bit-)mmx register.
    dabei bleiben die oberen 32bit unbenutzt: 8x8bit x|x|x|x|a|r|g|b
    das erweitert man so, dass 4 16bit-komponenten entstehen: 0|a| 0|r| 0|g| 0|b
    warum? weil mmx nur 16bit-komponenten multiplizieren kann; klingt komisch - is' aber so.
    gleichzeitig bringt man die transparenz-konstante in das gleiche format: 0|a| 0|a| 0|a| 0|a
    und multipliziert die beiden mmx-register; wir erhalten also 4 16bit-komponenten in denen jeweils 2 bytes multipliziert sind.
    durch paralleles shiften fallen jeweils die unteren 8bit jeder komponente wieder raus und das ganze kann wieder zurueck ins 32bit-pixelformat konvertiert werden.

    oversized. aber 3x schneller 😉



  • uff, das ist assembler, ach du dicke Kanone, ich werde die Funktion mal verwenden, ich editiere dann mal hier rein, was ich davon halte ^^

    Unfassbar, es funktioniert, ich danke dir nochmals...
    ich werde diese Funktion nutzen, wenn du nichts dagegen hast, ich habe zwar noch kein Plan von Assembler, aber das scheint echt mal gut zu sein.
    Es läuft jetzt recht schnell, vielen Dank ^^

    Es wäre jetzt noch toll, wenn du mir diese Assembler Funktion so erweitern könntest, dass man das mit diesen 32 Bit Bildern machen kann und da für jeden Pixel das Alpha ausließt, das wäre sehr schön ^^

    Zusätzlich könntest du noch einfügen, dass man den Bereich auf dem Bildschirm, wo das Bild angezeigt wird, auch selber als Parameter übergeben kann (Zwar kann man die Y Position locker einstellen, aber... die X Position ist immer ganz links bis zum Bereich, den man als letzten Parameter der Funktion übergibt) XD

    Übrigens, ich habe dich mal im MSN Messenger geaddet, wäre nett, wenn du da mal online kämst, dann könntest du mir direkter helfen ^^



  • jaja, gibt man den kleinen finger... 😉
    - aber so langweilig ist mir nun auch wieder nicht :p

    wenn du mir diese Funktion so erweitern könntest, dass man für jeden Pixel das Alpha ausließt

    es wird dich aber nicht weiterbringen, wenn du es nicht selbst versuchst...

    dass man den Bereich wo das Bild angezeigt wird als Parameter übergeben kann

    das kannst du leicht selber machen in dem du als ziel-pointer (dst) jeweils die entsprechende scanline plus horizontaler pixelposition uebergibst.



  • Bigdeak schrieb:

    Übrigens, ich habe dich mal im MSN Messenger geaddet, wäre nett, wenn du da mal online kämst, dann könntest du mir direkter helfen ^^

    *invisible aktivier* 😎

    Gruß, TGGC (\-/ has leading)



  • Nagut, dann versuch ich es selber, aber eines noch:

    movd      mm0,[edi] // mm0= *dst => 32bit farbe in 64bit-mmx-register lesen: 0|0|0|0|a|r|g|b (8x8bit)
    

    Es wäre sehr nett, wenn du mir noch sagen könntest, wie ich jetzt aus diesem [edi] Register den Byte für a in den mm5 Register bewege (a aus [edi] in mm0 0|0|0|0|a|a|a|a), damit ich die einzelnen Alphawerten der Pixel mit gewichten kann.
    Ich habe mich wirklich in den Code reingedacht und soweit alles verstanden (obwohl ich wirklich nur Theorie in Assembler gemacht habe).

    Ich brauch praktisch hier nur den Befehl, um aus einen Register einen einzelnen Byte rauszulesen und diesen Byte in ein Byte eines anderen Registers abzulegen...

    Ich habe auch auf der Seite geguckt und nicht den entsprechenden Befehl gefunden >_<


Anmelden zum Antworten