2013-04-05 13 views
11

Ho "hacking" uno dei miei giochi preferiti da quando ero più giovane, e sono riuscito a essere in grado di intercettare chiamate OpenGL e iniettare codice C++ utilizzando un wrapper opengl32.dll. Sono stato in grado di trovare una serie di fantastici usi per questo, ma il mio obiettivo attuale è quello di implementare gli shaders glsl in questo vecchio gioco per renderlo un po 'più moderno. Il gioco è stato originariamente rilasciato alla fine degli anni '90, quindi il suo uso di OpenGL e del suo codice di rendering è limitato/primitivo/obsoleto.Iniezione di un frame buffer

Sono stato in grado di produrre vertex e frammenti di shader iniettando il codice per inizializzare e utilizzare i programmi glsl shader, ma il mio problema consiste nel produrre un framebuffer/renderizzare la mia scena su una texture da inviare al mio framment shader come campionatore . Per quanto ne so, la consistenza che produco è solitamente tutta nera.

È difficile trovare risorse che siano rilevanti per quello che sto facendo ed esaminare le chiamate open source (ho cercato di determinare dove inserire il codice leggendo una traccia opengl di un singolo frame di gameplay), quindi non sono stato in grado di avvolgere la mia mente su quello che sto facendo male.

Ho provato a mettere il codice in molti luoghi, ma al momento, sto vincolante il framebuffer dopo la partita chiama wglSwapBuffers() e io sono non vincolante immediatamente prima della prossima glFlush() chiamata viene effettuata, ma non sono sicuro se l'installazione di più finestre, operazioni con matrici o qualsiasi altra cosa creata tra questi tempi è in qualche modo un problema con il framebuffer.

Questo è il codice per inizializzare il framebuffer:

void initFB() 
{ 
    /* Texture */ 
    glActiveTexture(GL_TEXTURE0); 
    glGenTextures(1, &fbo_texture); 
    glBindTexture(GL_TEXTURE_2D, fbo_texture); 

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 

    glBindTexture(GL_TEXTURE_2D, 0); 

    /* Depth buffer */ 
    glGenRenderbuffers(1, &fbo_depth); 
    glBindRenderbuffer(GL_RENDERBUFFER, fbo_depth); 
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 1920, 1080); 
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo_depth); 
    glBindRenderbuffer(GL_RENDERBUFFER, 0); 

    /* Framebuffer to link everything together */ 
    glGenFramebuffers(1, &fbo); 
    glBindFramebuffer(GL_FRAMEBUFFER, fbo); 

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture, 0); 
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo_depth); 

    GLenum status; 
    if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) { 
    fprintf(stderr, "glCheckFramebufferStatus: error %p", status); 
    } 

    glBindFramebuffer(GL_FRAMEBUFFER, 0); 
} 

Questo è il codice chiamato dopo wglSwapBuffers:

GLenum fboBuff[] = { GL_COLOR_ATTACHMENT0 }; 
glBindFramebuffer(GL_FRAMEBUFFER, fbo); 
glDrawBuffers(1, fboBuff); 
glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT); 

Questo è il codice chiamato prima glFlush():

glBindFramebuffer(GL_FRAMEBUFFER, 0); 
glPopAttrib(); // Restore our glEnable and glViewport states 

... Draw an Overlay containing with fbo texture binded ... 

Here è la traccia gl di un singolo fotogramma.

Sono quasi 800 linee, ma un occhio esperto dovrebbe essere in grado di vedere quali chiamate vengono fatte quando. Ho rimosso diverse centinaia di righe di chiamate di disegno che non capisco.

+0

Forse provare ad aggiornare la texture tramite glReadPixels invece solo per vedere se il resto del codice è corretto? –

risposta

0

Come ho capito, stai cercando di aggiornare il lato grafico di un vecchio gioco, ma stai riscontrando limitazioni nel codice originale che ti impediscono di riadattare le parti di cui hai bisogno.

Anche se non ho la relativa esperienza di codifica del gioco, quando avevo un'applicazione che stavo cercando di aggiornare, vorrei smontare l'applicazione per studiarne la struttura e applicare in modo selettivo le patch in memoria per reindirizzare determinate funzioni e segmenti di codice alle sostituzioni che ho fornito.

Vorrei precaricare una libreria condivisa che sovrascriva alcune parti della memoria del programma con istruzioni di salto/chiamata condizionale/incondizionata e NOP in modo selettivo in modo tale che il mio codice diventasse parte integrante del programma. Sicuramente non era elegante in alcun modo, ma mi permetteva di aggirare velocemente le carenze nel codice originale.

2

Non sono completamente sicuro di cosa vuoi fare. Esegui il gioco per renderti fbo? Renderli in cima? Per iniziare, prova ad agganciare tutte le chiamate wgl e gl. http://research.microsoft.com/en-us/projects/detours/ è un buon punto di partenza.

Ma leggendo di nuovo il tuo post, immagino che tu lo sappia già.Sarebbe utile sapere cosa vuoi fare.

  • rendering una sovrapposizione: SwapBuffers Hook, fare la tua roba lì
  • Acquisizione: Anche in questo caso, SwapBuffers gancio
  • di rendering per consistenza e facendo effetti di post: Hook BindFramebuffer. Ma c'è un problema: il frame buffer 0 è il default all'avvio, quindi il codice che non lo usa non chiamerà BindFramebuffer per iniziare, ma solo per riportarlo a 0. Prova ad agganciare glClear o qualunque altra chiamata arrivi prima.

Sentitevi liberi di me con più dettagli. Sembra un divertente progetto educativo che mi piacerebbe aiutare :)

Ho appena letto di nuovo il tuo post prima di pubblicare e il tuo problema potrebbe essere molto più semplice. Come programma Windows, il tuo target presume che fbo 0 sia vincolato quando inizia a mostrare un frame. Fallo e basta Prima che sia fatto, passerà a fbo 0. Non fare la cosa push/pop. Assicurati che il tuo fbo abbia le stesse dimensioni (anche su Windows più grande va bene) che si aspetta. Devi però agganciare BindFramebuffer. Ogni volta che il tuo gioco chiama bind 0, devi invece vincolare il tuo. Ma non c'è bisogno di fare altro come toccare la finestra, il gioco deve averne già cura. Quando finalmente esegui il rendering nel tuo swapBuffers, assicurati di ripristinare qualsiasi cosa tu cambi.

Inoltre, non hai davvero bisogno della chiamata DrawBuffers. Mi ha confuso la talpa e probabilmente anche il tuo obiettivo. :) Il tuo obiettivo fa il codice GL 1.2, stai facendo 3 ... 4 ...

1

Dalla mia esperienza, le cose che stai cercando di fare sono del tutto possibili con un approccio di intercettazione GL per un'applicazione GL1.x senza shader .

Come ho trucco rapido, si può solo dimenticare il FBO per ora e basta usare glCopyTexSubImage() quando mai l'applicazione originale scambia i buffer, per copiare il contenuto visualizzato in una texture e il testo le funzioni di post-processing.

Alcune cose che si dovrebbe prendere cura di, quando si fa un tale approccio iniezione GL:

  • ci potrebbero essere diversi contesti GL (anche se questo è più probabile per le applicazioni CAD di giochi tipici)
  • vecchia GL ha permesso all'utente di gestire direttamente l'ID oggetto - nessuna chiamata alle funzioni glGen* dove necessario (le applicazioni piacevoli le eseguivano ancora). Se tu stesso crei nuovi oggetti, l'app originale non saprà e potrebbe riutilizzare quegli ID. Se vuoi farlo bene, dovresti rimappare in modo trasparente gli ID oggetto (possibilmente usando una tabella hash) - il che è molto lavoro, dato che molte funzioni GL sono coinvolte. Un altro attacco malvagio sarebbe usare da soli alcuni ID oggetto improbabili.
  • GL è una macchina a stati, quindi dovresti stare molto attento a ripristinare lo stato che il gioco si aspetta (il più possibile). Mano OTOH, dovresti anche essere preparato che l'applicazione originale possa avere qualsiasi stato GL impostato quando hai la modifica per intercettarlo, quindi potresti dover resettare un sacco di cose.

Posso assicurarvi che è effettivamente possibile iniettare il rendering in texture in qualche vecchia applicazione GL, come ho fatto io stesso. Ha funzionato molto bene con Quake3 e altri famosi giochi dei tempi. Non vedo un errore evidente con il tuo approccio attuale. Poche cose però: dovresti anche intercettare lo glDrawBuffer() e ritrasmettere qualsiasi disegno che l'app farà a GL_BACK (assumendo qui l'applicazione a doppio buffering) al tuo allegato a colori. Il log che hai postato non ha traccia di questa funzione, quindi il gioco potrebbe averlo impostato una volta all'avvio.

Inoltre non capisco perché si usi glFlush. Non è un buon marcatore per qualcosa in particolare. Se il gioco utilizza il doppio buffering, SwapBuffers sarà la migliore approssimazione di un "frame" che puoi ottenere. È sufficiente inserire le proprie creazioni di oggetti oggetto init direttamente dopo la creazione del contesto (è possibile intercettare wglCreateCintext() e anche gli amici) e già impostato il rendering sull'FBO. Su SwapBuffers, basta disegnare il contenuto della texture usando gli shader sul vero back buffer, fare il vero buffer swap e ripristinare il rendering sull'impostazione dell'FBO.

Non so se ne siete già a conoscenza o no, ma il progetto open source Chromium offre un buon framework per l'intercettazione OpenGL. Mentre il focus di questo potrebbe essere sul rendering cluster distribuito, funzionerà anche in setup alocal e ti permetterà di scrivere SPU (unità di elaborazione stream), dove puoi semplicemente specificare una funzione C per qualsiasi funzione GL che vuoi agganciare . Il progetto è obsoleto e non in sviluppo attivo da 5 anni circa, perché il tipo di manipolazione del flusso GL è stato progettato per non funzionare con la pipeline programmabile più moderna, ma funzionerà ancora bene con il buon vecchio GL1. x pipeline con funzione fissa ...

Problemi correlati