Tekstur
Grunnleggende
Først og fremst trenger vi i gl4java en Teksture objekt som brukes til å laste teksturene inn i. Det er tre forskjellige klasser vi kan bruke, alle disse står bedre forklart i gl4java API'en.
TextureLoader texLoader = new AWTTextureLoader(this, gl, glu);
Brukes til å laste ".gif", ".jpg" og mulige andre bildeformater
TextureLoader texLoader = new TGATextureLoader(gl, glu);
Brukes til å laste ".tga" bildeformatet.
TextureLoader texLoader = new PngTextureLoader(gl, glu);
Brukes til å laste ".png" bildeformatet.
Så trenger vi en array av integer for å lagre en referanse til teksturen:
int[] texName = {0};
Deretter trenger vi en integer variabel for hver tekstur som blir lastet til å lagre referansene i:
int texture = 0; int texture2 = 0; osv..
Metoden for å laste teksturen:
public int text(String fil) { //Leser teksturen fra fil og //inn i texture loader objektet texLoader.readTexture(fil); //Genererer referanse til tekstur gl.glGenTextures(1,texName); //Binner tekstur gl.glBindTexture(GL_TEXTURE_2D,texName[0]); //Velger hvordan tekstur skal bli gjenntatt gl.glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_LINEAR); gl.glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_LINEAR); //Velger hvordan et pixel skal rendres dersom et teksturelement //dekker mer eller mindre en en pixel gl.glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); gl.glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); //Skalerer tekstur elemntet texLoader.texImage2DNonScaled(true); //Returnerer referanse til tekstur return texName[0]; }
Alternativene til GL_LINEAR i metodene:
gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR); gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_LINEAR);
Er GL_CLAMP eller GL_REPEAT, se modulen Teksturerfor mer detaljer.
Alternativet til GL_LINEAR i metodene:
gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
Er GL_NEAREST, se også her modulen Teksturer for mer detaljer.
Så kaller vi på metoden for å laste teksturen:
texture = text("mintekstur.gif");
Deretter må vi slå på teksturer med metoden:
gl.glEnable(GL_TEXTURE_2D);
Og så binde teksturen:
gl.glBindTexture(GL_TEXTURE_2D, texture);
Og så velge hvordan materialet på overflaten skal blandes med teksturen:
GL_DECAL - bakgrunnen har ingen påvirkning, ingen lyseffekter.
GL_MODULATE - bakgrunnsmaterialet skinner igjennom og lyssetning får innvirkning. Hvis materialet er hvitt får det ingen fargeeffekter
på teksturen.
GL_BLEND - teksturfargene blandes med materialfargene
gl.glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
Og så tegne polygon med tekstur:
glBegin(GL_POLYGON); glTexCoord2f(0.0f,0.0f); glVertex3f(0.0f,0.0f,0.0f); glTexCoord2f(1.0f,0.0f); glVertex3f(1.0f,0.0f,0.0f); glTexCoord2f(1.0f,1.0f); glVertex3f(1.0f,1.0f,0.0f); glTexCoord2f(0.0f,1.0f); glVertex3f(0.0f,1.0f,0.0f); glEnd();
Og så slå av tekstur igjen:
gl.glDisable(GL_TEXTURE_2D);
Koordinater
Når en skal legge teksturer på en flate er man nødt til å ha koordinatene til den delen av en tekstur en vil legge på et gitt polygon. Et høydekart er bygd opp av veldig mange polygoner (TRIANGLES eller QUADS), så hvis man vil legge en tekstur på flaten er man nødt til og ha en metode som automatisk finner og lagrer alle teksturkoordinatene i punktene x og y. |
I dette eksempelet vil en tekstur bli lagt over hele overflaten, og vil dekke
alle polygoner i hele høydekartet.
Tankegangen er at en tekstur vil bli delt opp i like mange små deler som
antall polygoner i høydekartet. For hver (x,y) koordinat vil det bli lagret en
hopp koordinat mellom 0 og 1.0 (se Teksturer) Siden avstanden (stride) mellom
punktene i høydekartet er konstant, vil teksturkordinatene øke med en
"hopp" for hver x i bredden, og en "hopp" for hver y i
lengden.
"Hopp" tilsvarer 1/SIZE (hvis flaten er kvadratisk, hvis ikke må
det være en hopp verdi for bredde og en for lengde). Dvs. at "Hopp" *
"SIZE" = 1, slik at teksturen dekker akkurat flaten , og ikke blir
klippet eller gjentatt (repeated). (se Teksturer).
Koden som jeg bruker er en enkel utvidelse av koden for å sette opp et høydekart.
/* Teksturkoordinater */ public class HeightField { private int SIZE; //bredde i av området(kvadratisk) //Array som innholder innformasjon //om teksturkooridnater i x og y retning private float[][][] teksturkoor = new float[SIZE][SIZE][2]; //klippe, klippe bort kode for høydekart //Metode for å sette opp en SIZEx * SIZEy flate //stride er mellomrom mellom punkter private void setData(float stride, float teksrep) { //klippe, klippe bort kode for høydekart float hopp = (1.0f/(float)SIZE)*teksrep; float u = 0.0f; float v = 0.0f; //Alle x-koordinater for (int breddeX = 0; breddeX < SIZE-1; breddeX++) { //Alle y-koordinater for en x-verdi for (int breddeY = 0; breddeY < SIZE-1; breddeY++) { //klippe, klippe bort kode for høydekart //Tekstur x koordinat teksturkoor[breddeX][breddeY][0] = u; //Tekstur y koordinat teksturkoor[breddeX][breddeY][1] = v; u += hopp; } v += hopp; u = 0.0f; } } }
teksrep er en variabel for å angi hvor mange ganger teksturen skal bli gjentatt på overflaten. Jo høyere verdi desto flere ganger blir teksturen gjentatt. Hvis teksrep = 1.0 blir teksturen strekt over hele overflaten.
I tillegg trengs det en metode for og få tak i tak i teksturkoordinatene fra andre klasser.
public float[][][] getTeksturKoor() { return teksturkoor; }
Generering av egne teksturer
Til nå har vi bare lagt på en tekstur over hele landskapet uten og brydd oss om høydeforskjellene i landskapet. I denne delen skal vi lage en tekstur som har forskjellige farger avhengig av om det er en fjelltopp eller om det er nede i en dal. En tekstur er kun en array bestående av mange tall eller bit-verdier hvor hver verdi representere en RGBA-farge. En RGBA-farge er har en verdi for rød-, en for grønn-, en for blå- og en for alpha- fargen. Det betyr at for hver pixel må en ha fire verdier, så en trenger en array som er 4 ganger så stor som det antall piksler man skal dekke. |
For å generere en tekstur med høydekartet trenger vi en array som er 4*SIZE av høydekartet. Så går vi gjennom hele høydekartet og legger til en farge i teksturen avhengig av høydeverdiene i høydekartet.
HeightField hf; float[] texture; //Konstruktør public Texture(HeightField h, float heightdiff, float s, float f) { hf = h; //tekstur array texture = new float[hf.getSize()*hf.getSize()*4]; makeTexture(heightdiff, s, f); } public void makeTexture(float hd, float prosentsno, float prosentfjell) { int v = 0; //hvor høyt oppe skal snø begynne float psno = hd * prosentsno; //hvor langt ned skal fjell gå float pfjell = hd * prosentfjell; for (int i = 0; i < hf.getSize(); i++) { for (int j = 0; j < hf.getSize(); j++) { //Hvis høyde større snøgrense if (hf.getHeight(i,j) > (hd - psno)) { //Setter farge på snø(hvit) texture[v] = 1.0f; texture[v+1] = 1.0f; texture[v+2] = 1.0f; texture[v+3] = 1.0f; } //hvis høyde mindre en snøgrense og større en fjellgrense else if ((hf.getHeight(i,j) < (hd - psno)) && (hf.getHeight(i,j) > (hd - (psno+pfjell)))) { //Setter farge på fjell(gulaktig) texture[v] = 0.9f; texture[v+1] = 0.9f; texture[v+2] = 0.5f; texture[v+3] = 1.0f; } //Hvis ikke, bakkenivå else { //Setter farge på bakke(grønnaktig, varierende) texture[v] = 0.0f; texture[v+1] = (float)Math.random()*0.8f; texture[v+2] = 0.0f; texture[v+3] = 1.0f; } v += 4; } } } //få tekstur array public float[] getTexture() { return texture; }
heightdiff - største høyde i høydekartet
prosentsno - hvor høy opp grensa for snø skal ligge, verider mellom 0.0 og 1.0
(100%)
prosentfjell - hvor langt ned fjelltekstur skal gå, verdi mellom 0.0 og 1.0
minus prosentsno
Denne teknikken for å lage tekstur er kun ment som en introduksjon til det og lage egne teksturer. Resultat blir ganske enkelt da det kun benyttes enkle farger. For å få bedre og penere resultater må teknikken utvides til å bruke teksturer fra bilder, og en må lage overganger (blending) fra en tekstur til en annen. Dette skal jeg ikke gå mer inn på her.
For å laste teksturen må en bruke en litt annen metode en den som bruktes til å laste bildeteksturer fra fil på overflaten.
public int groundtext() { gl.glGenTextures(1,texName); gl.glBindTexture(GL_TEXTURE_2D,texName[0]); gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //Her leses verdiene fra teksturarrayen gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SIZE, SIZE, 0, GL_RGBA, GL_FLOAT,madeTex); return texName[0]; }
madetex er den tekstur arrayen som ble laget ut i fra høydene i høydekartet.