|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
TUTORIAL 1: THE 3D ENGINE |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
INTRODUZIONE In queste lezioni spiegherò come realizzare un motore 3d in linguaggio C usando le librerie OpenGL. Queste lezioni sono rivolte a chi già possiede una conoscenza base del linguaggio C. In caso contrario vi consiglio di studiare prima il linguaggio C (sulla rete ci sono un infinità di corsi). Il motore che andremo a realizzare sarà strutturato per gestire ambienti aperti, caratteristica adatta per i simulatori spaziali (dopotutto ci troviamo a spacesimulator.net, no?) e non è escluso che in futuro aggiungerò anche sezioni per visualizzare terreni e tutorials più avanzati nei quali svelerò anche alcune soluzioni che ho implementato nel mio stesso motore. State certi però che procederò gradualmente dai concetti più elementari ai più complessi. E naturalmente per cominciare è necessario spiegare il concetto base... cos'è un motore 3d? Un motore 3d è un insieme di strutture, di funzioni e di algoritmi che consentono di visualizzare, dopo avere effettuato opportuni calcoli e trasformazioni, degli oggetti tridimensionali su uno schermo 2d. Le principali sezioni di un motore 3d sono: Noi siamo qui per questo e quindi mettiamoci subito a lavoro! In questa lezione vedremo come definire le strutture principali per disegnare un oggetto 3d.
I VERTICI Supponiamo di avere un oggetto e di volerlo rappresentare in uno schermo 2D. A tale scopo occorre ottenere informazioni riguardanti la sua struttura. Come facciamo? Definiamo dei punti chiave: i vertici, i quali identificano, appunto, i vertici dell'oggetto. Ogni vertice 3d è composto da tre coordinate cartesiane x,y,z. Ogni coordinata deve essere espressa tramite valori a virgola mobile a singola o a doppia precisione (float o double), questo perchè è necessario avere sempre la massima risoluzione disponibile per effettuare i calcoli sulla posizione e rotazione e per disegnare l'oggetto. Questa soluzione è l'unica consentita ma non è esente da difetti, tende infatti a perdere risoluzione con l'avanzare del valore numerico. Questo difetto è particolarmente sentito specialmente se si vuole progettare un simulatore spaziale, in ogni caso comunque è necessario limitare lo spazio in cui effettuare la simulazione. Per definire un vertice in linguaggio C usiamo una struttura vertex composta da tre variabili x,y,z. typedef
struct{ E' molto importante tenere a mente che tutti i calcoli che riguardano il posizionamento e la rotazione di un oggetto si applicano ai vertici, essendo la struttura base. La seconda struttura in ordine di importanza sono i poligoni.
I POLIGONI I poligoni sono le facce dell'oggetto e sono composti da N vertici. In quasi tutti i motori i poligoni sono composti da 3 vertici e quindi anche noi useremo questa convenzione. A questo punto possiamo pensare di definire una struttura poligono la quale conterrà 3 vertici: typedef
struct{ ed in seguito dichiariamo la variabile array polygons che conterrà tutti i poligoni che costituiranno l'oggetto: #define
MAX_POLYGONS 2000 Ma attenzione! La nostra definizione di poligono assegna ad ogni poligono 3 vertici unici e non condivisi con altri poligoni. In realtà se ci riflettete un attimo ogni poligono condivide i suoi lati, e quindi anche i suoi vertici, con altri poligoni. Quindi abbiamo commesso un errore. Beh, non è proprio un errore, ma abbiamo comunque aumentato in modo considerevole il numero di vertici presenti sulla scena, ben oltre il necessario. Come abbiamo già detto il motore userà i vertici per effettuare la maggior parte dei suoi calcoli e quindi dobbiamo assolutamente trovare un altro modo per definire i poligoni. A pensarci bene possiamo per prima cosa creare una lista di vertici che conterrà tutti i vertici dell'oggetto e poi... per definire i poligoni useremo una sorta di "referenziamento" per "puntare" ai vertici di quella lista. Dichiariamo adesso una variabile array di tipo vertex_type che conterrà MAX_VERTEX vertici. #define MAX_VERTICES 2000 La struttura poligono non conterrà più i vertici ma soltanto 3 numeri che identificheranno dei vertici corrispondenti alla lista dei vertici. In questo modo più poligoni possono puntare allo stesso vertice e ciò ottimizza notevolmente il design dell'engine. typedef
struct{
L' OGGETTO Facciamo un pò d'ordine ed organizziamo le precedenti definizioni in una struttura che chiameremo obj_type. typedef
struct{ Si tratta solo di una definizione di base, in futuro arricchiremo questa struttura con nuovi campi che identificheranno la posizione, la rotazione e lo stato dell'oggetto. Per definire un cubo occorrerà quindi riempire l'array di vertici in questo modo (coordinate da 0 a +1): obj_type obj; obj.vertex[0].x=0; obj.vertex[0].y=0; obj.vertex[0].z=0; //vertex v0obj.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 Il problema adesso è come suddividere il nostro cubo in triangoli. La risposta è semplice, ogni faccia del cubo è un quadrato che è composto da due triangoli adiacenti. Il nostro oggetto sarà composto quindi da 12 poligoni (triangoli) e da 8 vertici.
Riempiamo la lista di poligoni: obj.polygon[0].a=0;
obj.polygon[0].b=1; obj.polygon[0].c=4; //polygon v0,v1,v4 Una cosa molto importante da considerare è che per definire i poligoni bisogna usare sempre lo stesso verso orario o antiorario per tutti i poligoni sulla scena. Come vedremo più avanti il verso è usato per vedere se il poligono è visibile o meno. Quindi attenzione a non sbagliarsi altrimenti alcuni poligoni non saranno visibili. Noi useremo la convenzione antioraria (ad esempio per il primo poligono potremmo avere 0,1,4 oppure 1,4,0 oppure 4,0,1 e non 1,0,4 e nemmeno 0,4,1 e nemmeno 4,1,0).
CONCLUSIONI Per questa lezione è tutto, nella prossima inizieremo ad usare OpenGL ed integreremo quanto visto finora per disegnare il cubo sullo schermo. Se avete qualche dubbio o critica non esitate a scriveremi a info@spacesimulator.net |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
©2000-2003 Damiano Vitulli. All Rights Reserved. No Portion Of This Site May Be Reproduced Without Permission. Best viewed at 1024x768. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||