Teenpä tässä tälläistä pientä 3D-hommelia ja eteeni hyppäsi täysin käsittämätön ongelma. Piirtäessäni aaltoja tekstuurimatriisi (kai) sekoaa ja se aiheuttaa taivaaseen täydellisen disco-efektin. Pilvet päättävät myös pillastua, ja ne muuttuvat pitkiksi viivoiksi. Paikansin ongelman veden aalto -piirtolistan luontiin. Ohessa koodia ja niiden vaikutukset.
//Piirto listaan glNewList(Vesi_aalto_lista,GL_COMPILE); glBindTexture(GL_TEXTURE_2D, Vesi_texture); //glHint(GL_FOG_HINT, GL_NICEST); glDrawArrays(GL_QUAD_STRIP, 0, AaltoVertekseja); //glHint(GL_FOG_HINT, GL_FASTEST); glDrawArrays(GL_QUAD_STRIP, AaltoVertekseja, AaltoVertekseja); glDrawArrays(GL_QUAD_STRIP, 2*AaltoVertekseja, AaltoVertekseja); glEndList();
Yllä oleva koodi toimii oikein. Kuva
Jos muutan sen muotoon:
//Piirto listaan glNewList(Vesi_aalto_lista,GL_COMPILE); glBindTexture(GL_TEXTURE_2D, Vesi_texture); glHint(GL_FOG_HINT, GL_NICEST); glDrawArrays(GL_QUAD_STRIP, 0, AaltoVertekseja); glHint(GL_FOG_HINT, GL_FASTEST); glDrawArrays(GL_QUAD_STRIP, AaltoVertekseja, AaltoVertekseja); glDrawArrays(GL_QUAD_STRIP, 2*AaltoVertekseja, AaltoVertekseja); glEndList();
Saadaan aikaan hieno disco-efekti, joka ei ole tavoite. Kuva
Disco myös katoaa ja tavoiteltu taivas palautuu jos kommentoin kaksi alinta glDrawArrays-komentoa.
//Piirto listaan glNewList(Vesi_aalto_lista,GL_COMPILE); glBindTexture(GL_TEXTURE_2D, Vesi_texture); glHint(GL_FOG_HINT, GL_NICEST); glDrawArrays(GL_QUAD_STRIP, 0, AaltoVertekseja); glHint(GL_FOG_HINT, GL_FASTEST); //glDrawArrays(GL_QUAD_STRIP, AaltoVertekseja, AaltoVertekseja); //glDrawArrays(GL_QUAD_STRIP, 2*AaltoVertekseja, AaltoVertekseja); glEndList();
Onhan se disco "kaunis", mutta taivaasta se ei käy. Onko tähän ongelmaan mitään kunnollista selitystä tai ratkaisua, koska en haluaisi törmätä tähän ongelmaan enää uudestaan?
Ongelma on todettu kahdella eri koneella, joten näytönohjaimessa vika ei kai voi olla.
Tässä vielä yksi kuva tästä efektistä.
Mitä ajat takaa tolla?
glDrawArrays(GL_QUAD_STRIP, AaltoVertekseja, AaltoVertekseja); glDrawArrays(GL_QUAD_STRIP, 2*AaltoVertekseja, AaltoVertekseja);
Onko sulla määritelty glVertexPointer jne... tai glInterleavedArrays ennen noita.
Vaikuttais siltä että luet muistista ohi sun vertex arrayn. Miksi edes käytät listaa arrayn piirtoon?
Mitä yrität tolla glHint(GL_FOG_HINT, xxxx) jutulla?
glVertexPointer on viittaa taulukkoon joka on 9*AaltoVertekseja kokoinen. Itse en usko lukevani yli taulukosta, sillä veden aallot piirtyvät aivan oikein.
float VerteksiArr[AaltoVertekseja*3 *3]; //Tässä kohtaa täytän VerteksiArr glVertexPointer (3, GL_FLOAT, 0,VerteksiArr);
Käytän listaa, koska sitä on helppo kutsua, eikä minun tarvitse pitää VerteksiArr-taulukkoa muistissa. glHint-komennolla pyrin saamaan paremman sumun (pixel-perfect) tuolle ensimmäiselle glDrawArrays-komennolle (se piirtää itse aallot). Nuo kaksi alempaa glDrawArrays-komentoa ovat vähemmän tärkeitä (aaltojen reunat), joten niille riittää vertex-perfect sumu.
Tutkiessani miksi vain taivas muuttuu discoksi sain selville että disco-efekti muodostuu vain ja ainastaan vain jos GL_FOG on disabloitu. Miksi sumun disabloiminen vaikuttaa näin? Vain poistamalla glDisable(GL_FOG) -komennon ennen taivaan piirtoa sain taivaan toimimaan. Olen asettanut sumun alkamaan 300 yksikön päästä, jolloin sillä ei pitäisi mitään vaikutusta taivaan kupuun(taivas on puolipallo, jonka säde on 1 yksikkö), korkeitaan vain pilviin. Vaikutus silti näkyy taivaassa.
Testasin disabloida sumun (glDisable(GL_FOG)) ohjelman toisesta osasta, joka hoitaa ajoneuvojen piirron (nuolia, jotka liikkuvat viivoja pitkin). Tuloksena ajoneuvojen tekstuurin vääristymisen. Ennen Jälkeen
Jostain syystä sumun disabloiminen aiheuttaa tekstuurin vääristymisen, jos piirrän veden aallot eri laatuisilla sumuilla. Ilmeisesti ainoa ratkaisu on pirtää taivas (ja kaikki muukin) sumun kanssa, jos haluan piirtää osan vedestä laadukkaammalla sumulla. Tai piirtää vesi heikommalla sumulla, jotta voin piirtä taivaan ilman sumua.
Miten määrittelet veden ja taivaan texture koordinaatit?
Veden textuurikordinaatit lasketaan suoraan jakamalla vekteksin x- ja z-arvo 120:llä. Eli siis:
//Lasketaan texcoordit for (int i=0;i<AaltoVertekseja*3;i++){ TekstuuriArr[2*i+0] = VerteksiArr[3*i+0] / 120.0f; TekstuuriArr[2*i+1] = VerteksiArr[3*i+2] / 120.0f; }
Piirron yhteydessä tekstuurimatriisia muutetaan vastaavasti ennen listan kutsumista. Näin veden tekstuuri näyttää pysyvän paikoillaan kameran liikkuessa. Siis että ei liiku mukana.
//Siirretään kameran kohtaan glTranslatef(CAM_X,0,CAM_Z); //Tekstuurimatriisin siirto glMatrixMode(GL_TEXTURE); //inv120 on 120:n vastaluku (n*inv120 = n/120) glTranslatef(CAM_X*inv120,CAM_Z*inv120,0); glMatrixMode(GL_MODELVIEW); //Piirto //Tekstuurimatriisin palautus glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW);
Taivaan (puolipallo) tekstuurikordinaateissa x on aina 0 ja y lasketaan jakamalla puolipallon nykyinen kerros kerrosten lukumäärällä.
//Tämä lasketaan jokaiselle puolipallon pisteelle texCoordit[addIndex][0] = 0; texCoordit[addIndex][1] = y/float(vaakaTasoja)+0.01f;
Jokaisen pisteen texcoordin X on 0 tarkoituksella. Muutan teksuurimatriisia ennen piirtoa seuraavasti:
glMatrixMode(GL_TEXTURE); glLoadIdentity(); //aika->timeOfDay on arvo väliltä 0-1 glTranslatef(aika->timeOfDay,0,0);
Näin saan helposti kellonajan mukaan muuttuvan taivaan. Olen ottanut mallia tuolta.
Outoa on että taivas toimii loistavasti jos pidän GL_FOG:in päällä taivaan piirron aikana, vaikka sen ei pitäisi vaikuttaa mitenkään tekstuurimatriisiin.
ainakin sulla on [AaltoVertekseja*3 *3]; vertexiä joita vastaan on altoVertekseja*3 texture koordinaattia. oletko duplikoinut saman texcoord setin jokaiselle kolmelle Aaltoverteksejä quadstripille? (siis annatko niille saman texcoordpointterin). pistätkö texture koordinaatit arraylle glTexCoordPointerilla samaan aikaan kun laitat verteksit glVertexPointerilla? kokemuksesta voin sanoa että jos noi pointterit vertexarraylle ei ole oikein asetettu niin diskoahan se on.
tässä muutama hihasta vedetty pikaparannusehdotus.
//Siirretään kameran kohtaan TÄHÄN OLIS HYVÄ LAITTAA glLoadIdentity(); glTranslatef(CAM_X,0,CAM_Z); //Tekstuurimatriisin siirto glMatrixMode(GL_TEXTURE); TÄHÄN OLIS HYVÄ LAITTAA glLoadIdentity(); //inv120 on 120:n vastaluku (n*inv120 = n/120) > tollanen "optimointi" on tarpeeton nykykoneilla. sekottaa vaan koodin ymmärrystä glTranslatef(CAM_X * (1.f/120.f),CAM_Z * (1.f/120.f) ,0); // >1.f/120.f kääntyy vakioksi, ja on selkeämpi lukea *snip*
muutenkin suosittelisin käyttämään gluLookAt funktiota kameran translaatiossa. olisi muutenkin selkeämpää jos käyttäisit jotain Vector3-structia
struct Vector3 { float x; float y; float z; };
noiden float arrayiden sijaan vertexdatassa. se ajaa täsmälleen saman asian kuin float array ja on helpompi lukea.
muuten mikä näytönohjain sulla on käytössä? tämä voi olla ajuribugigin. itse on ole kyllä koskaan käyttänyt tuota glHint funkkaria fogin ohjaukseen. (epäilen sen hyötyä nykykorteilla...)
//inv120 on 120:n vastaluku (n*inv120 = n/120) > tollanen "optimointi" on tarpeeton nykykoneilla. sekottaa vaan koodin ymmärrystä glTranslatef(CAM_X * (1.f/120.f),CAM_Z * (1.f/120.f) ,0); // >1.f/120.f kääntyy vakioksi, ja on selkeämpi lukea
Ja mitä vikaa on laittaa vain suoraan jakolaskuna tuo? Minusta se olisi vielä selkeämpi, ja jos tuollainen optimointi todella johonkin vaikuttaa, eiköhän kääntäjä sen hoida. Minulla näyttivät kerto- ja jakolaskut olevan yhtä nopeita, mitä nyt pari sataa miljoonaa toimitusta laitoin laskemaan.
Tekstuuri kordinaatit on taulukossa joka on AaltoVertekseja*6 kokoinen, ja viittaan siihen vain kerran glTexCoordPointerilla.
float TekstuuriArr[AaltoVertekseja*2*3]; //Tässä lasketaan vekteksit //Lasketaan texcoordit for (int i=0;i<AaltoVertekseja*3;i++){ TekstuuriArr[2*i+0] = VerteksiArr[3*i+0] / 120.0f; TekstuuriArr[2*i+1] = VerteksiArr[3*i+2] / 120.0f; } glTexCoordPointer(2, GL_FLOAT, 0, TekstuuriArr);
Ongelmasta lisää:
Tein testin jossa poistin veden piirron kokonaan ja korvasin sen seuraavalla koodilla:
void Testaus(){ glHint(GL_FOG_HINT,GL_NICEST); glCallList(testi_lista); glHint(GL_FOG_HINT,GL_FASTEST); }
testi_lista sisältää 24 trianglea ja 84 quadsia, kummatkin piirretty glDrawArrays-komennolla, niille on määrätty glTexCoordPointer ja glNormalPointer.
Tulos: Taivas vääristyi osittain. Tähdet (teksturoitu quad) ja kuu (myös teksturoitu quad) vääristyivät. Pilvet eivät vääristyneet.
Löysin uuden tavan ratkaista ongelma.
Jos kutsun ennen tähtien piirtoa (teksuurimatriisille) glLoadIdentity, tähdet vääristyvät. Mutta jos kutsun glLoadIdentityn jälkeen glTranslatef(0,0,0) tähdet toimivat. Sama toimii kuun kanssa.
Siis koodina:
glMatrixMode(GL_TEXTURE); glLoadIdentity(); //glTranslatef(0,0,0); tahdet->Draw(); glTranslatef(aika->timeOfDay,0,0); puolipallo->Draw(); glLoadIdentity(); //glTranslatef(0,0,0); aurinko->Draw(); glLoadIdentity(); glTranslatef(aika->timeOfDay*pilvet->nopeus,0,0); pilvet->Draw(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW);
Jos nuo glTranslatef(0,0,0) -kohdat ottaa käyttöön taivas piirtyy oikein. Tekstuurimatriisi ei muutu, mutta tulos muuttuu. Outoa.
Näytönohjain on ATI Radeon 9700 ja ajurit ovat uusimmat.
Edit. Tuo glTranslatef(0,0,0) toimi vain tuon void Testaus() -jutun kanssa.
Aihe on jo aika vanha, joten et voi enää vastata siihen.