Teksturer
Vi må kunne:
- ta deler av en bitmap og legge på en flate.
- strekke en bitmap og krympe en bitmap slik at den passer til flaten.
- gjenta en bitmap flere ganger på en flate.
- legge en bitmap på en overflate som ikke er plan
- ta inn fra fil rimelig standard bildeformater som kan tjene som bitmaps
Vi ser litt på disse problemene i denne modulen.
Tekstur
Å bruke teksturer i OpenGL er ifølge Mark Kilgard, i boka: OpenGL Programming for the X window System, en operasjon i 4 steg:
- Skru på (enable) teksturer
glEnable(GL_TEXTURE_2D);
- Spesifisere teksturen
glTexImage2D(...);
- Spesifisere hvordan teksturen skal legges på overflaten
glTexEnvi(...);
- Tegne ut ved å oppgi både geometriske koordinater på overflaten
og teksturkoordinater
glBegin(GL_TRIANGLES); glTexCoord(...); glVertex3f(..) glTexCoord(...); glVertex3f(..) glTexCoord(...); glVertex3f(..) glEnd();
Spesifikasjon av teksturer
Det finnes en rekke funksjoner og parametre for å styre utleggingen av teksturer på ulike geometriske former, og det finnes en rekke muligheter for å kombinere teksturer med bakgrunnsfarge. Det er ikke ambisjonene her å gå gjennom alle mulige kombinajoner og effektene av dem. Vi vil konsentrere oss om de viktigste prinsippene. Full kontroll over teksturer og effekter er et typisk tema for prøving og feiling på basis av OpenGL-dokumenstasjonen.
Når det gjelder å skru på tekstur, glEnable(), og å opprette teksturer, glTexImage2D(), så er det greitt å forstå dette fra OpenGL-dokumentasjonen, og vi går ikke nærmere inn på dette her.
La oss se litt nærmere på spesifikasjon av hvordan teksturen skal legges ut.
Merk at beskrivelsen nedenfor må betraktes som en første iterasjon av forståelse. Mulighetene er langt fler enn de som er beskrevet nedenfor.
Viktige rutiner er:
glTexParameteri(GL_TEXTURE_2D,...,...); glTexEnvi(GL_TEXTURE_ENV,...,...);
glTexParameter
Det er spesielt to egenskaper vi kan styre ved hjelp av glTexParameteri():
Bestemmelse av hvorvidt teksturen skal låses til geometrien (clamp) eller om teksturen skal gjentas over flaten (repeat).
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT) glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP)
Merk at GL_TEXTURE_WRAP_S angir en retning i teksturen. Alternativet i en 2D tekstur er GL_TEXTURE_WRAP_T.
-
Bestemme hvordan en pixel skal rendres dersom et teksturelement dekker mindre enn, eller mer enn en pixel.
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST) glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
der et alternativ til GL_NEAREST er GL_LINEAR. Merk at ved glTexParameter kan vi altså også kontrollere vilken mipmap vi ønsker å bruke. Mipmaps er alternative teksturer som kan brukes når vi trenger mer eller mindre detaljrikdom, f.eks. basert på avstand.
glTexEnv
Ved hjelp av glTexEnvi() kan vi kontrollere hvordan teksturen skal forholde seg til den bakgrunnen den legges på.
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL)
legger normalt teksturen på uten å ta hensyn til bakgrunnsmaterialet.
Alternativene:
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE) glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_BLEND)
har effekter som er detaljert beskrevet i openGL-dokumentasjonen. Hensikten er å muliggjøre påvirkning fra bakgrunnen på teksturen (eller omvendt).
Geometri
La oss først se på noen enkle mekanismer for å styre ut en tekstur på et rektangel. Vi har følgende utgangspunkt:
Merk at når vi angir størrelsen på teksturmappen til å være 1 i begge retninger, så har ikke det noe med antall bildelementer (pixler i bitmapen) å gjøre. Vi sier pr definisjon at bitmapen som beskriver teksturen har denne dimensjonen. Den aktuelle bitmapen som i dette tilfellet er en .bmp fil er 64 x 64 pixler stor. Dimensjonene på rektangelet bestemmer vi helt fritt.
Merk at OpenGL vil kun ha bitmaps som er 2n x 2m store. n trenger ikke være lik m.
La oss betrakte noen tegninger og koden for å framstille dem. Vi fokuserer bare på geometrien.
- Bilde 1 er framstilt slik:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); .... glTexImage2D(..); glBegin(GL_POLYGON); glTexCoord2f(0.0f,0.0f); glVertex3f(-6.5f,-6.5f,0.1f); glTexCoord2f(1.0f,0.0f); glVertex3f(6.5f,-6.5f,0.1f); glTexCoord2f(1.0f,1.0f); glVertex3f(6.5f,6.5f,0.1f); glTexCoord2f(0.0f,1.0f); glVertex3f(-6.5f,6.5f,0.1f); glEnd();
- Bilde 2 er framstilt slik:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); .... glTexImage2D(..); glBegin(GL_POLYGON); glTexCoord2f(0.0f,0.0f); glVertex3f(-6.5f,-6.5f,0.1f); glTexCoord2f(3.0f,0.0f); glVertex3f(6.5f,-6.5f,0.1f); glTexCoord2f(3.0f,3.0f); glVertex3f(6.5f,6.5f,0.1f); glTexCoord2f(0.0f,3.0f); glVertex3f(-6.5f,6.5f,0.1f); glEnd();
- Den eneste endringen mellom framstillingen av bilde 2 og bilde 3 er den første linja:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); ...
- I bilde 4 er de to første linjene:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); ....
Vi ser altså at vi kan kople hjørner i geometrien, glVertex3f(), med koordinater i teksturen, glTexCoord2f(). Dette er greitt så lenge vi kan "håndkode" scenen med kontropll over alle hjørner. Det bli verre når vi vil bruke Bezier-flater og OpenGLs funskjonsbibliotek for dette.
Det finnes en løsning på dette som er utformet slik at vi kan lage en mapping av teksturen på samme måte som vi mapper geometrien. Koden nedenfor legger ut en tekstur på en Bezierflate:
... // define ctrlpoints for beziersuface GLfloat ctrlpoints[4][4][3] = // [v][u][xyz] { ... }; ... glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); glEnable(GL_TEXTURE_2D); glEnable(GL_MAP2_VERTEX_3); glEnable(GL_MAP2_TEXTURE_COORD_1); glEnable(GL_MAP2_TEXTURE_COORD_2); glTexImage2D(GL_TEXTURE_2D,0,3, m_theImage.GetWidth(), m_theImage.GetHeight(), 0,GL_RGB,GL_UNSIGNED_BYTE, m_theImage.GetBitsPtr()); // map geometri glMap2f(GL_MAP2_VERTEX_3, 0.0f,1.0f,3,4,0.0f,1.0f,12,4, & ctrlpoints[0][0][0]); // map texture GLfloat txpts[2][2][2]={{{0.0, 0.0}, {1.0, 0.0}}, {{0.0, 1.0}, {1.0, 1.0}}}; glMap2f(GL_MAP2_TEXTURE_COORD_2,0,1,2,2,0,1,4,2, & txpts[0][0][0]); glMapGrid2f(20,0.0f,1.0f,20,0.0f, 1.0f); glEvalMesh2(GL_FILL, 0, 20, 0, 20);