INTRODUCTION

Original Author: Damiano Vitulli

Translation by: Click here

Aluksi pitää selittää peruskonsepti... mikä on 3d-moottori? Vastaus on suhteellisen yksinkertainen: 3d-moottori on kokonaisuus, joka koostuu rakenteista, funktioista ja algoritmeistä, joita käytetään näyttämään, monien muunnoksien ja laskutoimituksien jälkeen, 3-ulotteisia esineitä 2-ulotteisella näytöllä.

3d-moottorin pääosat ovat:

  1. Esineiden rakenteellisen datan hakeminen.
  2. Muunnokset, jotta esineet saadaan sijoitetuksi maailmaan.
  3. Kuvan mallinnus 2-ulotteiselle näytölle.

Tämän takia olemme täällä, joten eikun töihin!

Tässä oppitunnissa opimme 3d-kappaleen piirtämisen kannalta olennaiset asiat.

LEIKKAUSPISTEET

Oletetaan, että sinulla on esine, jonka haluat saada näkyviin 2d-näytöllä. Tehdäksesi tämän, on olennaista kerätä tietoa sen rakenteesta. Kuinka sen voi tehdä? Aluksi meidän täytyy määritellä kappaleen avainpisteet: sivujen leikkauskohdat. Jokainen piste määritellään kolmen koordinaattiarvon perusteella (x,y,z). Jokainen koordinaatti pitää olla joko FLOAT- tai DOUBLE-muuttuja, tämä siksi, että haluamme parhaan mahdollisen näyttötarkkuuden mallinnuksessa. Tätä tapaa käytetään jokaisessa 3d-moottorissa, mutta se ei ole virheetön. Tosiasiassa se menettää tarkkuutensa, kun numero on liian suuri. Tämä rajoittaa simulaatioavaruuden kokoa. Avaruussimulaattorissa pitäisi pystyä liikkumaan loputtoman avaruuden halki, joten tämä ongelma tulee väkisinkin vastaan. Määrittääksemme pisteen C-kielessä, käytämme rakennetta, joka koostuu kolmesta muuttujasta (x,y,z)

typedef struct{
   float x,y,z;
}vertex_type;

On hyvin tärkeää muistaa, että kaikki laskutoimitukset, jotka vaikuttavat kappaleen liikutteluun ja kääntelyyn, tehdään pisteiden perusteella, sillä ne määrittelevät kappaleen rakenteen. Pisteiden jälkeeen seuraavaksi tärkein rakenne on monikulmio.

MONIKULMIOT

Monikulmiot ovat kappaleen seinät, ja ne koostuvat useasta pisteestä. Useimmissa 3d-moottoreissa monikulmiot koostuvat kolmesta pisteestä, joten käytämme samaa sääntöä. Käyttäen vertex_type -rakennetta, voimme määrittää monikulmion, joka koostuu 3 pisteestä:

typedef struct{
   vertex_type a,b,c;
}polygon_type;

Määritämme myös polygon_type -taulukkomuuttujan, johon tallennetaan kaikki monikulmiot, joista kappale koostuu.

#define MAX_POLYGONS 2000
polygon_type polygon[MAX_POLYGONS]; 

Mutta katsohan tarkkaan! Määrittelyssämme jokainen monikulmio koostuu 3 pisteestä, mutta näitä ei ole jaettu usean monikulmioiden kesken. Jos palaamme vähän taakepäin, näemme, että kappaleen jokainen monikulmio jakaa sivunsa, ja myös pisteensä, jonkin toisen monikulmion kanssa. Olemme siis tehneet virheen! Se ei oikeastaan ole virhe, mutta se kasvattaa pisteiden määrän huomattavasti tarpeellista määrää suuremmaksi. Totesimme jo, että moottori käyttää pisteitä suurimmassa osassa laskutoimituksiaan, joten oikeasti tarvimme toisen tavan määritellä monikulmiot. Voisimme luoda listan pisteistä, joka pitää sisällään kappaleen kaikki pisteet. Sen jälkeen luodaksemme monikulmiot, käytämme eräänlaista viittausjärjestelmää viitataksemme listan pisteisiin. Luomme nyt vertex_type -taulukkomuuttujan, joka pitää sisällään enintään MAX_VERTICES pistettä.

#define MAX_VERTICES 2000
vertex_type vertex[MAX_VERTICES];

Monikulmiorakenne ei sisällä enää pisteitä vaan kolme numeroa, jotka viittaavat pistelistan kolmeen elementtiin. Tällä tavalla useampi monikulmio voi käyttää samaa pistettä kulmanaan. Tällä tavoin saamme optimoitua moottorimme rakennetta huomattavasti.

typedef struct{
   int a,b,c;
}polygon_type;

KAPPALE

Tässä kohdassa siivoamme hieman ja järjestämme edelliset määrittelyt rakenteeseen nimeltä obj_type.

typedef struct{
   vertex_type vertex[MAX_VERTICES];
   polygon_type polygon[MAX_POLYGONS];
}obj_type,*obj_type_ptr;

Tämä on vain perusmääritelmä. Myöhemmin tulemme lisäämään uusia kenttiä, jotka kertovat kappaleen paikan, tilan ja käännökset.

Nyt voimme luoda kappaleen ja täyttää listan pisteistä.

obj_type obj;
obj.vertex[0].x=0;  obj.vertex[0].y=0;  obj.vertex[0].z=0; //vertex v0
obj.vertex[1].x=1;  obj.vertex[1].y=0;  obj.vertex[1].z=0; //vertex v1
obj.vertex[2].x=1;  obj.vertex[2].y=0;  obj.vertex[2].z=1; //vertex v2
obj.vertex[3].x=0;  obj.vertex[3].y=0;  obj.vertex[3].z=1; //vertex v3
obj.vertex[4].x=0;  obj.vertex[4].y=1;  obj.vertex[4].z=0; //vertex v4
obj.vertex[5].x=1;  obj.vertex[5].y=1;  obj.vertex[5].z=0; //vertex v5
obj.vertex[6].x=1;  obj.vertex[6].y=1;  obj.vertex[6].z=1; //vertex v6
obj.vertex[7].x=0;  obj.vertex[7].y=1;  obj.vertex[7].z=1; //vertex v7

Nyt ongelmana on kuution sivujen jakaminen kolmioiksi. Ratkaisu on helppo: Jokainen kuution sivu on neliö, joka koostuu kahdesta vierekkäisestä kolmiosta. Täten kuutiomme koostuu 12 monikulmiosta (kolmiosta) ja 8 pisteestä.

Tut 3dengine cube.png

The list of polygons must be filled like so:

obj.polygon[0].a=0;  obj.polygon[0].b=1;  obj.polygon[0].c=4;  //polygon v0,v1,v4
obj.polygon[1].a=1;  obj.polygon[1].b=5;  obj.polygon[1].c=4;  //polygon v1,v5,v4
obj.polygon[2].a=1;  obj.polygon[2].b=2;  obj.polygon[2].c=5;  //polygon v1,v2,v5
obj.polygon[3].a=2;  obj.polygon[3].b=6;  obj.polygon[3].c=5;  //polygon v2,v6,v5
obj.polygon[4].a=2;  obj.polygon[4].b=3;  obj.polygon[4].c=6;  //polygon v2,v3,v6
obj.polygon[5].a=3;  obj.polygon[5].b=7;  obj.polygon[5].c=6;  //polygon v3,v7,v6
obj.polygon[6].a=3;  obj.polygon[6].b=0;  obj.polygon[6].c=7;  //polygon v3,v0,v7
obj.polygon[7].a=0;  obj.polygon[7].b=4;  obj.polygon[7].c=7;  //polygon v0,v4,v7
obj.polygon[8].a=4;  obj.polygon[8].b=5;  obj.polygon[8].c=7;  //polygon v4,v5,v7
obj.polygon[9].a=5;  obj.polygon[9].b=6;  obj.polygon[9].c=7;  //polygon v5,v6,v7
obj.polygon[10].a=3; obj.polygon[10].b=2; obj.polygon[10].c=0; //polygon v3,v2,v0
obj.polygon[11].a=2; obj.polygon[11].b=1; obj.polygon[11].c=0; //polygon v2,v1,v0

Muista, että määrittääksesi monikulmiot oikein, on aina käytettävä samaa myötäpäivään tai vastapäivään kulkevaa kiertosuuntaa jokaiselle monikulmiolle. Seuraavassa ohjeessa tulee ilmi, että tätä kiertosuuntaa käytetään määrittelemään, onko monikulmio näkyvissä vai ei. Kannattaa siis kiinnittää huomiota siihen, miten määrittelet monikulmiot tai moni niistä ei tule näkyviin. Ohjeissa määritämme monikulmiot vastapäivään (esimerkiksi ensimmäinen monikulmio määrittellään näin: v0,v1,v4 tai v1,v4,v0 tai v4,v0,v1; eikä näin: v1,v0,v4; v0,v4,v1 tai v4,v1,v0).

TOINEN, ELEGANTIMPI TAPA MÄÄRITELLÄ KAPPALE

C ja C++ -kielissä voimme täyttää obj_type -rakenteen tällä, elegantimmalla tavalla:

obj_type cube = 
{
    {
        -10,-10, 10, //vertex v0
         10,-10, 10, //vertex v1
         10,-10,-10, //vertex v2
        -10,-10,-10, //vertex v3
        -10, 10, 10, //vertex v4
         10, 10, 10, //vertex v5
         10, 10,-10, //vertex v6 
        -10, 10,-10  //vertex v7
    },  
    {
        0, 1, 4, //polygon v0,v1,v4
        1, 5, 4, //polygon v1,v5,v4
        1, 2, 5, //polygon v1,v2,v5
        2, 6, 5, //polygon v2,v6,v5
        2, 3, 6, //polygon v2,v3,v6
        3, 7, 6, //polygon v3,v7,v6
        3, 0, 7, //polygon v3,v0,v7
        0, 4, 7, //polygon v0,v4,v7
        4, 5, 7, //polygon v4,v5,v7
        5, 6, 7, //polygon v5,v6,v7
        3, 2, 0, //polygon v3,v2,v0
        2, 1, 0, //polygon v2,v1,v0
    }
};

Voit huomata myös, että vaihdoimme pisteiden koordinaatit (0,1 -> 10,-10). Tämä on tarkoituksenmukaista. Haluamme kappaleen keskipisteen pisteeseen (0,0). Ymmärrämme seuraavassa ohjeessa, miksi. Kappaleemme on nyt 20 kertaa suurempi, jotta koordinaattien käsittely olisi helpompaa.

PÄÄTÖS

Siinä se. Seuraavassa ohjeessa alamme käyttää OpenGL:ää saadaksemme kuution näkymään.

SOURCE CODE

The Source Code of this lesson can be downloaded from the Tutorials Main Page