Fredrik Danielsen / Student 2002
Å tegne:>Terreng>Tekstur

Tekstur

Hva
rock
Grunnlegende om teksturer i openGL og gl4java. Hvordan få tak i teksturkoordinatene til et høydekart.

Denne modulen tar sikte på og gi en kjapp introduksjon til teksturer i openGL og gl4java. Deretter gås det gjennom en teknikk for å mappe teksturer til flater. Endelig drøftes generering av teksturer ut fra gråtone bilder.

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. tekstkoor

I dette eksempelet vil en tekstur bli lagt over hele overflaten, og vil dekke alle polygoner i hele høydekartet.
hopp

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.
hoppdemo
"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.

madetex

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.

Referanser
  • Terreng.java
  • HeightField.java
  • Texture.java
Vedlikehold
  • Skrevet av Fredrik Danielsen, student 2002
  • Redaksjonell tilpassing,Børre Stenseth juni 2002
(Velkommen) Å tegne:>Terreng>Tekstur (Displaylister)