Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SDL/OpenGL] Zmiana kolorów tekstury

Ostatnio zmodyfikowano 2013-05-25 12:30
Autor Wiadomość
Piter
Temat założony przez niniejszego użytkownika
[SDL/OpenGL] Zmiana kolorów tekstury
» 2013-05-18 00:22:37
Witam!

Tworzę w C++ klon pewnej starej gry znanej z 8-bitowców pt. "Boulder Dash". Jest to prosta gra 2D, w której ludzik chodzi po planszy (dwuwymiarowej tablicy) i musi zebrać określoną liczbę diamentów. Poszczególne pola tejże planszy odzwierciedlane są na ekranie poprzez sprite'y - właściwie w moim przypadku tekstury.

Oryginalna gra miała ubogą grafikę - wykorzystywanych było w sumie sześć czy siedem kolorów - ale dla urozmaicenia dla każdej planszy kolory te były zmieniane. Pisząc mój program chciałem się trzymać tej zasady. Zdefiniowałem zestaw takich obrazków, które ładuję w programie do tablicy o nazwie sprites i wyświetlam na ekranie w najprostszy znany mi sposób przy użyciu funkcji (pisząc w skrócie) glBegin(GL_QUADS) poprzedzonej przez glBindTexture.

Po lekturze dostępnych w necie materiałów wymyśliłem też sposób na zmianę tych kolorów.

Mam strukturę, którą nazwałem rgb oraz tablicę dla wartości siedmiu kolorów, które są zmienne. Mówiąc inaczej - wartości RGB dla siedmiu kolorów, które zapisuję w tablicy wzorkol są wartościami, które chciałbym, aby przyjęły moje tekstury dla danej planszy gry.

C/C++
struct rgb {
    unsigned char r;
    unsigned char g;
    unsigned char b;
};

rgb wzorkol[ 7 ];

Samą zmianę kolorów realizują mi dwie funkcje: UpdateTextures oraz SetKolor. Pierwsza z nich iteracyjnie leci przez całą tablicę tekstur i dla każdej kolejnej tekstury sprawdza teksel po tekselu czy jego wartości RGB odpowiadają tym, które znajdują się we wzorcu wzorkol. Jeśli tak, to wywoływana jest funkcja SetKolor, która zmienia wartości RGB dla każdego konkretnego teksela. Kanał alpha jest tutaj nieużywany.

C/C++
void cl_BDImage::setKolor( GLuint ID, GLint x, GLint y, uint8_t r, uint8_t g, uint8_t b, uint8_t a ) {
    uint8_t data[ 3 ];
    data[ 0 ] = r;
    data[ 1 ] = g;
    data[ 2 ] = b;
   
    //data[3] = a;
    glBindTexture( GL_TEXTURE_2D, ID );
    glTexSubImage2D( GL_TEXTURE_2D,
    0,
    x,
    y,
    1,
    1,
    GL_RGB,
    GL_UNSIGNED_BYTE,
    data );
}

void cl_BDImage::UpdateTextures( int wzorkol_id, unsigned char new_r, unsigned char new_g, unsigned char new_b ) {
   
    for( int i = 1; i < max_img; i++ ) {
       
        unsigned char * pData = NULL;
        GLint uW, uH, uC, uF;
       
        glBindTexture( GL_TEXTURE_2D, sprites[ i ] );
        glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, & uW );
        glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, & uH );
        glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_COMPONENTS, & uC );
        glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, & uF );
        pData = new unsigned char[ uW * uH * uC ];
       
        glGetTexImage( GL_TEXTURE_2D, 0, uF, GL_UNSIGNED_BYTE, pData );
       
        for( int y = 0; y < uH; y++ ) {
            for( int x = 0; x < uW; x++ ) {
                rgb teksel;
                int wektor = y * uW * 4 + x * 4;
               
                teksel.r = pData[ wektor ];
                teksel.g = pData[ wektor + 1 ];
                teksel.b = pData[ wektor + 2 ];
               
                if( teksel.r == wzorkol[ wzorkol_id ].r && teksel.g == wzorkol[ wzorkol_id ].g && teksel.b == wzorkol[ wzorkol_id ].b ) {
                   
                    setKolor( sprites[ i ], x, y, new_r, new_g, new_b, 255 );
                }
            }
        }
        delete[] pData;
    }
   
    return;
}

No i to, co jest powyżej działa poprawnie. Pożądany efekt osiągnąłem, ale jest to (jak mawiał mój nauczyciel) tzw. metoda radziecka - nieekonomiczna i wolna. Tym wolniejsza im więcej jest tekstur i czym są one większych rozmiarów.

Chciałbym to przyspieszyć i z tym problemem zwracam się do Was. Może ktoś będzie w stanie mi coś podsunąć - ja napisałem to najlepiej jak umiałem, ale podejrzewam, że można znacznie lepiej podejść do tematu. Będę wdzięczny za wskazówki - dopiero zaczynam swoją przygodę z programowaniem w SDL/OGL i liczę na wyrozumiałość. Jeśli coś, z tego co napisałem powyżej jest niejasne też proszę o sygnał - uzupełnię o wszystkie potrzebne informacje.

pozdrawiam,

Piotrek
P-83210
m4tx
» 2013-05-18 07:16:36
A gdyby tak użyć
glColor3f()
?

Innym, bardzo wydajnym rozwiązaniem będzie użycie fragment shadera.
P-83212
Piter
Temat założony przez niniejszego użytkownika
» 2013-05-18 11:49:40
glColor3f() raczej tutaj nie będzie mieć zastosowania - on zmienia bieżący kolor, a mi zależy tak naprawdę na podmianie koloru dla poszczególnych (wybranych) tekseli tekstury. Co do fragment shadera - nie wiem, musiałbyś rozjaśnić nieco temat,
P-83224
m4tx
» 2013-05-18 13:49:20
Shader to taka mała aplikacja wykonywana przez kartę graficzną podczas renderowania każdej klatki obrazu. W przypadku fragment shadera operujemy na pikselach - można więc zmieniać im kolor. W sprawie shaderów za wiele Ci niestety nie pomogę, bo w czystym OpenGL-u nigdy ich nie wykorzystywałem.

EDIT:
Tyle, że Ty mówisz, że potrzebujesz zmienić kolor tekselom - fragment shader zatem i tutaj może Ci, niestety nie pomóc...
P-83227
DejaVu
» 2013-05-22 22:35:01
Ja również jakiś czas temu dowiedziałem się na uczelni jak działają shadery - z tego co pamiętam to można nawet za pomocą nich zmieniać nie tylko barwę ale również pozycje wierzchołków. Dzięki temu można realizować sprzętowo np. animację trawy, na którą wpływa wiatr :)

/edit:
Natomiast co do Twojej zasady to myślę, że warto najpierw wczytać 'teksturę', a potem jakimś algorytmem podmieniać kolory na teksturze, po czym zmodyfikowaną teksturę używać do renderowania. Złożoność obliczeniowa jest w tym momencie żadna :)
P-83640
Piter
Temat założony przez niniejszego użytkownika
» 2013-05-25 11:53:12
No tak, z tymze tutaj operacja jest już wykonywana na wczytanych do pamięci teksturach... spójrz proszę na załączoną wyżej funkcję.

Swoją drogą, zauważyłem że na nowszym od mojego komputerze to moje rozwiązanie działa błyskawicznie. I wszystko fajnie, ale ja u siebie mam procesor Q9300 i ATI HD4850... więc biorąc pod uwagę efekt, który chcę osiągnąć w ogóle nie powinno to stanowić problemu ;)
P-83828
m4tx
» 2013-05-25 12:30:19
Ja również jakiś czas temu dowiedziałem się na uczelni jak działają shadery - z tego co pamiętam to można nawet za pomocą nich zmieniać nie tylko barwę ale również pozycje wierzchołków. Dzięki temu można realizować sprzętowo np. animację trawy, na którą wpływa wiatr :)
Od tego akurat są vertex shadery a nie fragment shadery :) Oprócz tego w (naj)nowszych wersjach OpenGL-a są także geometry shadery - one potrafią nawet dodawać nowe wierzchołki. Ale to już tak na boku.
P-83831
« 1 »
  Strona 1 z 1