Modelagem de Objetos
Para desenhar objetos tridimensionais em um espaço plano, é necessário primeiro obter o modelo do objeto nos eixos coordenados X, Y e Z. Isso significa que cada ponto na superfície do objeto deve ser definido por coordenadas específicas. Idealmente, seria necessário definir infinitos pontos na superfície do objeto para manter a qualidade durante o zoom. Na prática, a modelagem 3D é feita utilizando malhas compostas por polígonos. Quanto mais pontos extremos um polígono possui, mais detalhada é a malha e mais realista o modelo se torna. No entanto, calcular e renderizar modelos e gráficos 3D assim requer mais recursos computacionais.
Gráficos de computação antigos precisavam funcionar em placas gráficas mais fracas, o que levou à divisão de polígonos em triângulos há muito tempo. Triângulos podem descrever com precisão a posição de pequenas superfícies e calcular parâmetros relacionados, como iluminação e reflexão de luz. Uma coleção desses pequenos triângulos pode criar imagens tridimensionais realistas de objetos. A partir de agora, polígonos e triângulos serão tratados como sinônimos, pois é mais fácil visualizar triângulos do que polígonos com N vértices.
A criação de um modelo tridimensional de um objeto é feita definindo as coodrenadas de cada vértice do triângulo. Dessa forma, mesmo que o objeto se mova ou a posição do observador mude, é possível calcular as coordenadas de cada ponto do objeto. Portanto, lidamos com vértices, as arestas que os conectam e as superfícies formadas por essas arestas. Se a posição do triângulo for conhecida, podemos usar as leis da álgebra linear para criar normais de superfície (uma normal é um vetor perpendicular à superfície). Assim, podemos calcular como a superfície é iluminada e como a luz é refletida por ela.
Criação de Modelos
Vamos escrever um programa simples para criar um cubo. Usaremos a classe CCanvas3D da biblioteca de gráficos 3D.
A classe CCanvas3DWindow para renderizar a janela 3D possui membros e métodos mínimos. Gradualmente adicionaremos novos métodos e explicaremos os conceitos de gráficos 3D implementados nas funções DirectX.
//+------------------------------------------------------------------+
//| Classe de formulário de aplicação |
//+------------------------------------------------------------------+
class CCanvas3DWindow
{
protected:
CCanvas3D m_canvas; // Canvas
//--- Dimensões do canvas
int m_width;
int m_height;
//--- Cubo
CDXBox m_box;
public:
CCanvas3DWindow(void) {}
~CCanvas3DWindow(void) {m_box.Shutdown();}
//--- Criação da cena
virtual bool Create(const int width,const height){}
//--- Cálculo da cena
void Redraw(){}
//--- Resposta a eventos do gráfico
void OnChartChange(void) {}
};
A criação da cena começa com a criação do canvas. Em seguida, definimos os seguintes parâmetros para a matriz de projeção:
- Visão de 30 graus (M_PI/6), de onde observamos a cena 3D
- Proporção (taxa de largura para altura)
- Distância do plano de corte próximo (0.1f) ao distante (100.f)
Isso significa que apenas partes do objeto que estão entre duas paredes fictícias (0.1f e 100.f) serão renderizadas na matriz de projeção. Além disso, o objeto deve estar dentro do ângulo de visão horizontal de 30 graus. Observe que distâncias e todas as coordenadas em gráficos de computação são fictícias. O importante é a relação relativa entre distâncias e tamanhos, não os valores absolutos.
//+------------------------------------------------------------------+
//| Método de criação |
//+------------------------------------------------------------------+
virtual bool Create(const int width,const int height)
{
//--- Salvar dimensões do canvas
m_width = width;
m_height = height;
//--- Criar canvas para renderização de cena 3D
ResetLastError();
if(!m_canvas.CreateBitmapLabel("Exemplo 3D",0,0,m_width,m_height,COLOR_FORMAT_ARGB_NORMALIZE))
{
Print("Erro ao criar canvas: ",GetLastError());
return(false);
}
//--- Definir parâmetros da matriz de projeção - visão, proporção, distância dos planos de corte próximos e distantes
m_canvas.ProjectionMatrixSet((float)M_PI/6,(float)m_width/m_height,0.1f,100.0f);
//--- Criar cubo - passando coordenadas de dois vértices opostos, gerenciador de recursos e parâmetros de cena
if(!m_box.Create(m_canvas.DXDispatcher(),m_canvas.InputScene(),DXVector3(-1.0,-1.0,5.0),DXVector3(1.0,1.0,7.0)))
{
m_canvas.Destroy();
return(false);
}
//--- Adicionar cubo à cena
m_canvas.ObjectAdd(&m_box);
//--- Atualizar cena
Redraw();
//--- Sucesso
return(true);
}
Após a criação da matriz de projeção, podemos prosseguir com a construção do objeto 3D - um cubo baseado na classe CDXBox. Para criar um cubo, basta especificar dois vértices opostos do cubo. Ao observar a criação do cubo no modo de depuração, você pode ver o que acontece na função DXComputeBox(): todos os vértices do cubo são criados (suas coordenadas são gravadas na matriz "vértices") e as arestas do cubo são divididas em triângulos, listadas e salvas na matriz "índices". O cubo tem um total de 8 vértices, 6 faces divididas em 12 triângulos, esses índices de vértices do triângulo são listados como 36 índices.
Embora o cubo tenha apenas 8 vértices, 24 vértices são criados para descrevê-los porque é necessário especificar um conjunto separado de vértices para cada normal das 6 faces. A direção da normal afeta o cálculo da iluminação de cada face. A ordem de listagem dos vértices do triângulo nos índices determina as bordas visíveis do triângulo. O código em DXUtils.mqh mostra a ordem de preenchimento de vértices e índices:
//--- Normais para as faces inferiores
for(int i=20; i<24; i++)
vertices[i].normal=DXVector4(0.0,-1.0,0.0,0.0);
No mesmo código, as coordenadas de textura são definidas para o mapeamento de textura de cada face:
//--- Coordenadas de textura
for(int i=0; i<faces i="" vertices=""></faces>
Cada um dos 4 vértices da face tem 4 coordenadas de mapeamento de textura definidas. Isso significa que cada face do cubo, ao renderizar a textura, mapeará uma pequena estrutura. Claro, isso só é necessário quando uma textura é definida.