Monsteret Mike
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.
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)
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
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.
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);
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.