Čeština / English
Login

Aplikace / 3D vektorová grafika

Autor: Josef Strnadel ()

Abstrakt: Aplikace demonstrující možnosti tvorby vektorových objektů ve 3D a jejich zobrazení.

Update: 20090414

1. Popis aplikace

Aplikace demonstrující možnosti tvorby vektorových objektů ve 3D a vykreslování 3D scény v režimech wireframe (drátěný model) nebo solid (vyplněné plochy) pomocí rasterizéru trojuhelníků implementovaného v FPGA. K dispozici jsou základní funkce pro transformace (posun, rotace, změna měřítka) vrcholů, hran, povrchů, objektů a celé scény.

graphic_3d_screen.png

Obrázek 1.1: Ukázka z aplikace

Zdrojové kódy aplikace je možné nalézt v SVN.

2. Knihovna pro manipulaci s 3D objekty

Součástí projektu je knihovna umožňující popis, zobrazení a manipulaci s 3D objekty.

2.1. Základní datové struktury

Před popisem tvorby vektorových objektů ve 3D se nejprve seznámíme se základními datovými strukturami, které pro tento účel budeme nezbytně potřebovat. První důležitou datovou strukturou je struktura Vertex. Pomocí ní můžeme vytvořit bod (jeden z vrcholů budoucího vektorového objektu) ve 3D, a to na souřadnicích [x, y, z]. 3D body mohou být řetězeny do jednosměrně vázaného lineárního seznamu, který je budován dynamicky za běhu aplikace.

struct Vertex
{
    double x;
    double y;
    double z;
    struct Vertex *next;
};
3d_axis.png

Obrázek 2.1: Ilustrace k 3D bodu a souřadnicovému systému

Ze dvou 3D bodů je možno vytvořit hranu reprezentovanou strukturou Edge. I hrany jsou řetězeny do seznamu.

struct Edge
{
    struct Vertex *v1;
    struct Vertex *v2;
    struct Edge *next;
};
3d_edge.png

Obrázek 2.2: Ilustrace k hraně

Máme-li vytvořeny hrany, můžeme jimi (formou obecného polygonu) ohraničit plochu (tj. stěnu budoucího objektu).

Struktura SkinAttrib zatím využita není, ale je připravena pro případné budoucí přiřazení vlastností každé ploše. Mezi tyto vlastnosti patří např. barva, průsvitnost, tvrdost atp.

struct SkinAttrib
{
    bool phong;
    bool light;
    struct Color *color;
    struct Color *reflect;
    struct Color *filter;
    struct Color *specular;
    long hardness;
    long roughness;
    long shininess;
    long brightness;
};

Plochu můžeme vytvořit pomocí následující struktury (Surface). Významný je zejména ukazatel *edgelist ukazující na začátek seznamu hran tvořících plochu.

struct Surface
{
    struct Edge *edgelist;
    enum SkinType skintype;
    struct SkinAttrib *skinattrib;
    struct Surface *next;
};
3d_surface.png

Obrázek 2.3: Ilustrace k povrchu

Máme-li vytvořenu alespoň jednu plochu, můžeme vytvořit objekt a to tak, že ve struktuře Object uložíme informaci o plochách tvořících objekt.

struct Object
{
    struct Surface *surfacelist;
    struct Object *next;
};

Pomocí vytvořených objektů můžeme vytvořit scénu - objekty tvořící scénu zřetězíme do lineárního seznamu a ukazatel na začátek tohoto seznamu uložíme do *objectlist struktury Scene.

struct Scene
{
    struct Object *objectlist;
};

2.2. Tvorba objektů a scény

Před vytvořením objektů a scény je potřeba deklarovat prostor pro plánované vrcholy, hrany, povrchy, objekty a scénu. Triviální řešení viz níže.

struct Vertex *v1 = NULL;

struct Edge *e1 = NULL;

struct Surface *s1 = NULL;

struct Object *o1 = NULL;

struct Scene *scene = NULL;

2.3. Funkce pro vykreslení scény

Je-li scéna vytvořena, je možno si ji nechat vykreslit. K tomu slouží funkce render. Parametry této funkce jsou ukazatel na strukturu Scene, dále plocha VGA obrazu, která bude využita k vykreslení scény, umístění kamery na ose Z (v bodě [0, 0, d], kamera je zaměřena do bodu [0, 0, 0]), informace o typu vykreslování ploch (drátěný/plný) a o potřebě vykreslení hranic plohy pro vykreslování.

void render(struct Scene *sc,
            unsigned int xwmin, unsigned int ywmin, unsigned int xwmax, unsigned int ywmax,
            double d, bool wireframe, bool drawborder);

2.4. Funkce pro transformaci objektů a scény

Nad vytvořenými objekty i scénou jako celkem je možné provádět všechny typické operace. První skupinou operací jsou operace nad vrcholy. Vrcholy je možno posouvat (translate3Dv), měnit jejich měřítko (scale3Dv), rotovat kolem souřadnicových os, popř. rotovat kolem obecné osy (rotate3Dv) zadané dvěma body [x1, y1, y1], [x2, y2, z2] na ní ležícími.

void translate3Dv(struct Vertex *v, double tx, double ty, double tz);
void scale3Dv(struct Vertex *v, double xf, double yf, double zf, double sx, double sy, double sz);
void rotate3DXv(struct Vertex *v, double angle);
void rotate3DYv(struct Vertex *v, double angle);
void rotate3DZv(struct Vertex *v, double angle);
void rotate3Dv(struct Vertex *v, double x1, double y1, double z1, double x2, double y2, double z2, double angle);

Obdobné funkce existují pro hrany,

void translate3De(struct Edge *e, double tx, double ty, double tz);
void scale3De(struct Edge *e, double sx, double sy, double sz);
void rotate3DXe(struct Edge *e, double angle);
void rotate3DYe(struct Edge *e, double angle);
void rotate3DZe(struct Edge *e, double angle);
void rotate3De(struct Edge *e, double x1, double y1, double z1, double x2, double y2, double z2, double angle);

pro povrchy,

void translate3Dp(struct Surface *s, double tx, double ty, double tz);
void scale3Dp(struct Surface *s, double sx, double sy, double sz);
void rotate3DXp(struct Surface *s, double angle);
void rotate3DYp(struct Surface *s, double angle);
void rotate3DZp(struct Surface *s, double angle);
void rotate3Dp(struct Surface *s, double x1, double y1, double z1, double x2, double y2, double z2, double angle);

pro objekty,

void translate3Do(struct Object *o, double tx, double ty, double tz);
void scale3Do(struct Object *o, double sx, double sy, double sz);
void rotate3DXo(struct Object *o, double angle);
void rotate3DYo(struct Object *o, double angle);
void rotate3DZo(struct Object *o, double angle);
void rotate3Do(struct Object *o, double x1, double y1, double z1, double x2, double y2, double z2, double angle);

a pro celou scénu, tj. všechny objekty jí náležící.

void translate3Ds(struct Scene *s, double tx, double ty, double tz);
void scale3Ds(struct Scene *s, double sx, double sy, double sz);
void rotate3DXs(struct Scene *s, double angle);
void rotate3DYs(struct Scene *s, double angle);
void rotate3DZs(struct Scene *s, double angle);
void rotate3Ds(struct Scene *s, double x1, double y1, double z1, double x2, double y2, double z2, double angle);

3. Zobrazení krychle krok za krokem

Demo aplikace demonstruje základní funkčnost 3D modelu, a to nejprve pomocí rotující krychle vykreslované v drátěném (wireframe) modelu a poté pomocí krychle vykreslované ve vyplňovaném (solid) modelu. Krychle má 8 vrchlolů s následujícími souřadnicemi:

v5->x = -40;   v5->y = 40;   v5->z = 40;
v6->x = 40;  v6->y = 40;  v6->z = 40;
v7->x = 40;  v7->y = -40; v7->z = 40;
v8->x = -40;  v8->y = -40; v8->z = 40;
v9->x = -40;   v9->y = 40;   v9->z = -40;
v10->x = 40;  v10->y = 40;  v10->z = -40;
v11->x = 40;  v11->y = -40; v11->z = -40;
v12->x = -40;  v12->y = -40; v12->z = -40;

Pomocí vrcholů vytvoříme hrany, pomocí nichž poté vytvoříme plochy. Každá plocha (strana) krychle je tvořena čtvercem a k definici čtverce stačí 4 hrany. Jelikož se jedná o krychli, nemusíme vytvářet všech 6 stran, ale k vykteslení krychle nám postačí strany 4.

e1 = (struct Edge *)malloc(sizeof(struct Edge));
e2 = e1->next = (struct Edge *)malloc(sizeof(struct Edge));
e3 = e2->next = (struct Edge *)malloc(sizeof(struct Edge));
e4 = e3->next = (struct Edge *)malloc(sizeof(struct Edge));
e4->next = NULL;

e5 = (struct Edge *)malloc(sizeof(struct Edge));
e6 = e5->next = (struct Edge *)malloc(sizeof(struct Edge));
e7 = e6->next = (struct Edge *)malloc(sizeof(struct Edge));
e8 = e7->next = (struct Edge *)malloc(sizeof(struct Edge));
e8->next = NULL;

e9 = (struct Edge *)malloc(sizeof(struct Edge));
e10 = e9->next = (struct Edge *)malloc(sizeof(struct Edge));
e11 = e10->next = (struct Edge *)malloc(sizeof(struct Edge));
e12 = e11->next = (struct Edge *)malloc(sizeof(struct Edge));
e12->next=NULL;

e13 = (struct Edge *)malloc(sizeof(struct Edge));
e14 = e13->next = (struct Edge *)malloc(sizeof(struct Edge));
e15 = e14->next = (struct Edge *)malloc(sizeof(struct Edge));
e16 = e15->next = (struct Edge *)malloc(sizeof(struct Edge));
e16->next = NULL;

Každou hranu vytvoříme pomocí dvou bodů.

e1->v1 = v6;   e1->v2 = v10;  //  e1 = (v6, v10)
e2->v1 = v10;   e2->v2 = v11;  //  e2 = (v10, v11)
e3->v1 = v11;   e3->v2 = v7;  //  e3 = (v11, v7)
e4->v1 = v7;   e4->v2 = v6;  //  e4 = (v7, v6)

e5->v1 = v5;   e5->v2 = v6;  //  e5 = (v5, v6)
e6->v1 = v6;   e6->v2 = v7;  //  e6 = (v6, v7)
e7->v1 = v7;   e7->v2 = v8;  //  e7 = (v7, v8)
e8->v1 = v8;   e8->v2 = v5;  //  e8 = (v8, v5)

e9->v1 = v5;   e9->v2 = v9;  //  e9 = (v5, v9)
e10->v1 = v9;   e10->v2 = v12;  //  e10 = (v9, v12)
e11->v1 = v12;   e11->v2 = v8;  //  e11 = (v12, v8)
e12->v1 = v8;   e12->v2 = v5;  //  e12 = (v8, v5)

e13->v1 = v9;   e13->v2 = v10;  //  e13 = (v9, v10)
e14->v1 = v10;   e14->v2 = v11; //  e14 = (v10, v11)
e15->v1 = v11;   e15->v2 = v12; //  e15 = (v11, v12)
e16->v1 = v12;   e16->v2 = v9;  //  e16 = (v12, v9)

Každé straně krychle přiřadíme seznam jejích hran, strany krychle (povrchy) provážeme do seznamu.

s1 = (struct Surface *)malloc(sizeof(struct Surface));
s2 = s1->next = (struct Surface *)malloc(sizeof(struct Surface));
s3 = s2->next = (struct Surface *)malloc(sizeof(struct Surface));
s4 = s3->next = (struct Surface *)malloc(sizeof(struct Surface));
s4->next = NULL;

s1->edgelist = e1;
s2->edgelist = e5;
s3->edgelist = e9;
s4->edgelist = e13;

Objekt (krychli) vytvoříme pomocí seznamu jeho ploch.

o2 = (struct Object *)malloc(sizeof(struct Object));
o2->surfacelist = s1;
o2->next = NULL;

Scénu vytvoříme pomocí seznamu jejích objektů (zde jen naše krychle).

scene = (struct Scene *)malloc(sizeof(struct Scene));
scene->objectlist = o2;

Demonstrační funkce nejprve rotuje drátěnou krychlí kolem os X, Y a poté pokračuje rotací plněné krychle kolem týchž os.

void demo()
{
  unsigned int i, j;
  bool border=0;
  bool wireframe=1;

  for(i=0; i<=40; i++)
  {
    rotate3DXo(o2, -3);
    rotate3DYo(o2, -6);
    render(scene, xwmin, ywmin, xwmax, ywmax, distance, wireframe, border); DelayMS(1);
  }

  wireframe=0;

  for(i=0; i<=40; i++)
  {
    rotate3DXo(o2, -3);
    rotate3DYo(o2, -6);
    render(scene, xwmin, ywmin, xwmax, ywmax, distance, wireframe, border); DelayMS(1);
  }
}

4. Zprovoznění aplikace

  1. přeložte aplikaci

  2. aktivujte propojku J6 pro povolení periferií PC

  3. naprogramujte MCU a FPGA a spusťte terminálový program. Zadáním příkazu help se zobrazí další možnosti.

Zobrazeno: 849x Naposledy: 15.11.2017 13:17:53