INTRODUÇÃO

Original Author: Damiano Vitulli

Translation by: Click here

Em primeiro lugar, é necessário explicar o conceito básico… o que é uma engine 3D? A resposta é relativamente simples: uma engine 3D é um conjunto de estruturas, funções e algoritmos usados para visualizar, após muitos cálculos e transformações, objetos 3D em uma tela 2D.

As seções principais de uma engine 3D são:

  1. A aquisição dos dados dos objetos em estruturas.
  2. As transformações para posicionar os objetos no mundo.
  3. Apresentação da cena na tela 2D.

Eis porque nós estamos aqui... então vamos trabalhar!

Nesta lição, nós estudaremos como definir as estruturas principais necessárias para desenhar um objeto 3D.

OS VÉRTICES

Suponha que você tenha um objeto e quer mostrá-lo em uma tela 2D. A fim de fazer isto, é necessário obter informação sobre sua estrutura. Como nós podemos fazer isto?

Primeiramente, nós definimos alguns pontos chaves: os vértices do objeto. Cada vértice é composto de três coordenadas: x, y e z. Cada coordenada deve ser expressa através de uma variável FLOAT ou DOUBLE porque nós precisamos sempre da melhor definição para apresentar a cena. Esta solução é usada em cada engine 3D mas não está livre de falhas pelo fato de se perder precisão quando o valor numérico é muito elevado. Este fato limita o espaço no qual pode-se simular. Em um simulador de espaço, nós deveríamos ser capazes de nos mover-se através do espaço infinito e, portanto, nós enfrentaríamos este problema eventualmente. Para definir um vértice em C, nós usamos uma estrutura composta das três variáveis x, y e z.

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

É muito importante lembrar que todos os cálculos a respeito do posicionamento e da rotação de um objeto são aplicados aos vértices, uma vez que eles são as unidades que compõem a estrutura básica. A segunda estrutura, em termos de importância, é o polígono.

OS POLÍGONOS

Os polígonos são as faces do objeto e são compostos de N vértices. Na maioria das engines 3D os polígonos são compostos de 3 vértices e, portanto, nós usaremos esta regra também. Usando nossa estrutura vertex_type, nós podemos definir uma estrutura para um polígono que contenha 3 vértices como:

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

Nós também declararemos uma variável do tipo array para a nossa estrutura polygon_type que será preenchida com todos os polígonos que constituem o objeto.

#define MAX_POLYGONS 2000
polygon_type polygon[MAX_POLYGONS];  

Mas… atenção! Nossa definição atribui 3 vértices a cada polígono e estes vértices não são compartilhados com os outros polígonos. Na verdade, se nós refletirmos um pouco veremos que cada polígono de um objeto de fato compartilha seus lados e também, seus vértices, com outros polígonos. Assim, nós cometemos um erro! Bem, não é realmente um erro, mas nós aumentamos, consideravelmente, o número real de vértices na cena além do que é necessário. Nós já dissemos que a engine usará os vértices para realizar a maioria dos cálculos e, então, nós realmente deveríamos encontrar um outro método para definir os polígonos. Nós poderíamos criar uma lista de vértices contendo todos os vértices do objeto. Então, a fim de definir os polígonos, nós usaremos um tipo de esquema de referência para selecionar os vértices daquela lista.

Nós declaramos agora um array de variáveis do tipo vertex_type que conterá os MAX_VERTICES vértices do objeto.

#define MAX_VERTICES 2000
vertex_type vertex[MAX_VERTICES];

A estrutura do polígono não conterá mais os vértices mas somente 3 números que apontarão para 3 elementos da lista de vértices. Desta maneira, mais polígonos podem apontar para o mesmo vértice. Isto otimiza muito o projeto da engine.

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

O OBJETO

Com o intuito de deixar mais claro os conceitos mostrados vamos organizar as definições precedentes em uma estrutura que nós chamaremos de obj_type.

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

Há somente uma definição básica. No futuro, nós adicionaremos mais campos que identificarão a posição, a rotação e o estado do objeto. Neste momento nós vamos declarar a variável objeto e preencher a lista de vértices:

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

Agora o problema está em como subdividir nosso cubo em triângulos. A resposta é simples: cada face do cubo é um quadrado composto de dois triângulos adjacentes. Portanto, nosso cubo será composto de 12 polígonos (triângulos) e de 8 vértices.

Tut 3dengine cube.png

A lista de polígonos deve ser preenchida assim:

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

Você deve lembrar que, a fim de definir os polígonos corretamente, é necessário usar sempre o mesmo sentido (horário ou anti-horário) para todos os polígonos na cena. Nós veremos, no tutorial seguinte, como o sentido é usado para controlar o tempo que o polígono é visível ou não. Assim, preste muita atenção na maneira que você define os polígonos ou muitos deles não serão visíveis. Nós usaremos o método anti-horário (por exemplo, o primeiro polígono é definido por v0, v1, v4 ou v1, v4, v0 ou v4, v0, v1 e não v1, v0, v4 ou v0, v4, v1 ou v4, v1, v0).

OUTRA MANEIRA, MAIS ELEGANTE, DE DEFINIR NOSSO OBJETO

Em C/C++, nós podemos preencher a estrutura obj_type usando esta maneira, mais elegante:

obj_type cube = 
{
    {
        -10,-10, 10, // vértice v0
         10,-10, 10, // vértice v1
         10,-10,-10, // vértice v2
        -10,-10,-10, // vértice v3
        -10, 10, 10, // vértice v4
         10, 10, 10, // vértice v5
         10, 10,-10, // vértice v6 
        -10, 10,-10  // vértice v7
    },  
    {
        0, 1, 4, // polígono v0,v1,v4
        1, 5, 4, // polígono v1,v5,v4
        1, 2, 5, // polígono v1,v2,v5
        2, 6, 5, // polígono v2,v6,v5
        2, 3, 6, // polígono v2,v3,v6
        3, 7, 6, // polígono v3,v7,v6
        3, 0, 7, // polígono v3,v0,v7
        0, 4, 7, // polígono v0,v4,v7
        4, 5, 7, // polígono v4,v5,v7
        5, 6, 7, // polígono v5,v6,v7
        3, 2, 0, // polígono v3,v2,v0
        2, 1, 0, // polígono v2,v1,v0
    }
};


Você também pode ver que nós mudamos as coordenadas dos vértices de (0,1) para (10, -10). Isto é intencional porque, de fato, nós precisamos ter o centro do objeto em (0,0) (nós compreenderemos o porquê no próximo tutorial). Nosso objeto é também 20 vezes maior (apenas para controlar facilmente as coordenadas).

CONCLUSÕES

Isso é tudo para esta lição. Na seguinte, nós começaremos a usar OpenGL para mostrar nosso cubo na tela.

SOURCE CODE

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