Eirin Østvold Blæstrud / Student 2002
Å tegne:>Monster

Monsteret Mike

Hva
Det grønne monsteret Mike ved hjelp av Bezierflater og teksturer

For å lage en figur slik som monsteret mitt tar man i bruk bezierflater og teksturer. Så utfordringene mine var å modellere bezierflatene, og legge teksturer på de. Teksturer på bezierflater var det største problemet, så det er det jeg skal ta for meg i denne modulen.

Originalmonsteret kan du se her

mike_front mike_back

Løsninger

Monsteret Mike består av mange bezierflater som er satt sammen og så lagt teksturer på. Jeg begynte med hodeformen, og fortsatte med de forskjellige kroppsdelene. Har lagt tekstur på alle flatene med unntak av horna, klørne, tennene og den mørke flaten inne i munnen.
Som et eksempel kan jeg vise kontrollpunktene for å lage en "håndflate". Når den skal tegnes ut, tegner jeg den først en gang, så roterer jeg og tegner den en gang til for å få en ordentlig hand.

int un = 4;
int vn = 4;

float z1 = -0.3f;
float z2 = 0.0f;
float z3 = 0.5f;
float z4 = 0.7f;

float y0 = 0.0f;
float y1 = -0.5f;
float y2 = 0.5f;

float liste [] = {
   -0.55f,y1,z1, -0.5f,y1,z1,
   0.0f,y1,z1, 0.5f,y1,z1,

   -0.45f,y1,z2, -0.4f,y0,z2,
   0.4f, y0,z2, 1.0f,y1,z2,

   -0.4f,y1,z3, -0.25f,y0,z3,
   0.25f,y0,z3, 0.5f,y1,z3,

   -0.25f,y1,z4, 0.0f,y1,z4,
   0.25f,y1,z4, 0.3f,y1,z4,
};

float listeAndre [] = {
   -0.55f,y2,z1, -0.5f,y2,z1,
   0.0f,y2,z1, 0.5f,y2,z1,

   -0.45f,y2,z2, -0.4f,y0,z2,
   0.4f,y0,z2, 1.0f,y2,z2,

   -0.4f,y2,z3, -0.25f,y0,z3,
   0.25f,y0,z3, 0.5f,y2,z3,

   -0.25f,y2,z4, 0.0f,y2,z4,
   0.25f,y2,z4, 0.3f,y2,z4,
};

Dette er vel stort sett sånn jeg har lagd monsteret. Bare mange forskjellige bezierflater som jeg flytter rundt og setter sammen i Monster klassen min.
Har brukt glutSolidSphere noen steder for å slippe luft mellom bezierflater osv. For eksempel i fingertuppene ligger det en liten kule slik at man ikke kan se at fingeren er hul.

Teksturer på bezierflater

Så over til den største utfordringen min. :) Deler opp problemet i tre. Følg denne oppskriften, og du skal ende opp med en flate med tekstur på.

Bezierflaten

For å legge teksturer på en bezierflate må man ha en bezierflate. :) Jeg bruker øyet fra monsteret som eksempel. Koden er ikke helt lik den i Monster.java. Hvis du har en frame med et GLCanvas klart er det bare å lime inn denne koden og kalle metoden for å få fram flaten.

bezier


public void drawEye() {

  //Har en egen klasse der jeg har definert forskjellige farger
  myColor.whiteBrown();

  int un = 4;
  int vn = 4;

  //Kontrollpunktene som bestemmer hvordan bezierflaten skal se ut
  float liste[] =  {
    -1.5f,0.0f,-1.0f, -1.0f,0.0f,-2.0f, //vn 1
    1.0f,0.0f,-2.0f, 1.6f,0.0f,-1.0f
    ,
    -2.0f,0.0f,-1.0f, -1.0f,-1.0f,-1.0f, //vn 2
    1.0f,-1.0f,-1.0f, 2.0f,0.0f,-1.0f
    ,
    -2.0f,0.0f,1.0f, -1.0f,-1.0f,1.0f, //vn 3
    1.0f,-1.0f,1.0f, 2.0f,0.0f,1.0f
    ,
    -1.5f,0.0f,1.0f, -1.0f,0.0f,2.0f, // vn 4
    1.0f,0.0f,2.0f, 1.5f,0.0f,1.0f,
  };

  gl.glEnable(gl.GL_AUTO_NORMAL);

  //Gjør klar bezierflaten
  gl.glMap2f(gl.GL_MAP2_VERTEX_3,0.0f,1.0f,3,un,
                        0.0f,1.0f,3*un,vn,liste);

  gl.glEnable(GL_MAP2_VERTEX_3);

  gl.glFrontFace(gl.GL_CW);
  gl.glMapGrid2f(40,0.0f,1.0f,40,0.0f, 1.0f);
  gl.glEvalMesh2(gl.GL_FILL, 0, 40, 0, 40);   //Tegner ut

}

Prøv denne koden først, slik at du er sikker på at du har en fungerende, rund bezierflate. Hvis du ikke har z opp i ditt koordinatsystem, kan du godt bytte om på y og z koordinatene i eksempelet, slik at du ikke får en liggende flate. Du kan ikke bruke noe annet enn GL_FILL på bezierflaten under en tekstur, hvis du for eksempel bruker GL_LINE blir teksturen rar.

Teksturen

Jeg har valgt å bruke PngTextureLoader så derfor må vi ha .png bilder. Velg deg ut et bilde du vil bruke som tekstur. Hvis dette er på et annet format enn png, åpne Photoshop eller et annet lignende program, og lagre bildet som png.
Du må også passe på at bildet har riktig størelse. Det må være 64x64, 64x128, 256x256 osv. Større enn 256 anbefales ikke. 35x78 eller andre tilfeldige størrelser fungerer ikke.
En ting til som man må passe på er at bildet kun har et lag, hvis det har flere lag når du lagrer det, gir dette en rar effekt. I Photshop kan du "flatten image" for å ordne på dette.
Slik ser det ut hvis et bilde har flere lag. :) (Du kan bruke bildet under som tekstur uten å gjøre noe med det)

rar

Tekstur på bezierflaten

Så skal vi legge bildet på flaten. Først har jeg en metode som laster inn teksturen og gjør den klar.

public void textures() {

  //Laster inn bildet
  PngTextureLoader txtLoader = new PngTextureLoader(gl, glu);
  txtLoader.readTexture("rar_test.png");

  //Hvis det går greit å laste inn bildet så..
  if(txtLoader.isOk()) {

    //Genererer en tekstur
    gl.glGenTextures(1,texName);
    gl.glBindTexture(GL_TEXTURE_2D,texName[0]);

    //Legger teksturen utover hele flaten
    gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
    gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

    //Hvordan teksturen skal strekkes
    gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

    //Bytter ut fargen med teksturen
    gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    //Definerer teksturen utifra argumentene
    gl.glTexImage2D(GL_TEXTURE_2D,
         0,
         3,
         txtLoader.getImageWidth(),
         txtLoader.getImageHeight(),
         0,
         GL_RGB,
         GL_UNSIGNED_BYTE,
         txtLoader.getTexture());

  }
}

Tenkte jeg skulle si litt om glTexImage2D.

gl.glTexImage2D(GL_TEXTURE_2D,
  0,
  3,
  txtLoader.getImageWidth(),
  txtLoader.getImageHeight(),
  0,
  GL_RGB,
  GL_UNSIGNED_BYTE,
  txtLoader.getTexture());

GL_TEXTURE_2D forteller OpenGL at teksturen er en 2D tekstur.
0, sier at at du bare har en oppløsning av bildet. Det er vanlig å sette denne til null.
3 angir at vi bruker RGB.
txtLoader.getImageWidth(), bredden på teksturen
txtLoader.getImageHeight(), høyden på teksturen
0 angir at vi ikke har ramme, hvis du setter 1 her får du ramme.
GL_RGB angir at bildet vi bruker er laget av rød, grønne og blå data.
GL_UNSIGNED_BYTE angir data typen.
txtLoader.getTexture()), bildet som skal brukes som tekstur.
Ellers anbefaler jeg å ta en titt i kap 9 i red book, der blir alle metoder og argumenter godt forklart!
Etter at denne metoden er implementert må vi legge til litt kode i "bezier-metoden" vår.

public void drawEye() {

  //Gjøre klart til å bruke teksturer
  gl.glEnable(GL_TEXTURE_2D);

  .
  . //Koden fra istad!
  .

  //tekstur metoden vår!
  textures();

  //Koordinatene til teksturen
  //Skal legges en gang over hele flaten
  float textPoints[]={
    0.0f, 0.0f,
    1.0f, 0.0f,
    0.0f, 1.0f,
    1.0f, 1.0f,
  };

  //Legger ut teksturen
  gl.glMap2f(GL_MAP2_TEXTURE_COORD_2,
    0,1,2,2,
    0,1,4,2,textPoints);

  gl.glEnable(GL_MAP2_TEXTURE_COORD_2);

  //Avslutter teksturen
  gl.glDisable(GL_TEXTURE_2D);

}//Slutt på drawEye

eye
Det er viktig å enable og disable teksturene på riktig sted, ellers kan du få teksturer på flater du ikke vil ha det osv. Her har jeg gjort det på begynnelsen og slutten av metoden, men det er også ryddig å gjøre det rundt metodekallet. Hver sin smak!
Før jeg legger ut teksturene må de gjøres klare, defor kaller jeg på tekstur metoden. Deretter kommer en array med punktene til teksturen. Arrayen jeg har brukt legger teksturen utover hele flaten.
Hvis du vil repetere teksturen må du forandre på denne arrayen. For eksempel:

  float textPoints[]={
    0.0f, 0.0f,
    5.0f, 0.0f,
    0.0f, 5.0f,
    5.0f, 5.0f,
  };

Man må også forandre et argument ned i textures(). Sett til GL_REPEAT istedet for GL_CLAMP.

  gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
  gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

Resultat blir da dette for vårt eksempel.

repeat

Man kan velge å repetere bare den ene veien. Her setter jeg til 5 langs x, og 1 langs y.

  float textPoints[]={
    0.0f, 0.0f,
    5.0f, 0.0f,
    0.0f, 1.0f,
    5.0f, 1.0f,
  };

  gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
  gl.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

repeat_2

Tilslutt legger jeg ut teksturen med standard argumenter.
På denne måten kan du lage mange slags figurer. Først forme den med bezierflater og deretter legge på teksturer for å få den fin. :)

Utvidelser

Naturlig utvidelse for monsteret mitt er bevegelser. Særlig bevegelser i ansiktet hadde vært gøy og fått med. En finere tekstur hadde heller ikke vært å forakte. Photoshop kunnskapene mine er nok ikke gode nok.


Referanser
Monster.java
Head.java UnderArm.java Bein.java
Eye.java FotKlo.java Hand.java
Horn.java HornInn.java Klo.java
KloInn.java Arm.java Leggen.java
Munn.java MyColor.java Eyelid.java

Alle filene inklusive teksturer: alt.zip.

Vedlikehold
Skrevet av Eirin Østvold Blæstrud, student 2002
Redaksjonalle endringer juni 2002, Børre Stenseth
(Velkommen) Å tegne:>Monster (Terreng)