Esse é um projeto da matéria de Algebra Linear e Teoria da Informação do Insper para o curso de Ciência da Computação.
Neste projeto, utilizamos a biblioteca pygame
, usando a função pygame.draw.line
para desenhar as linhas na tela. Ademais, as transformações foram implementadas manualmente.
O projeto consiste em fazer a implementação da projeção de um mundo 3D em uma tela 2D usando o algoritmo da pinhole camera. No qual o objetivo deste projeto é fazer uma projeção em tempo real de um cubo em wireframe que gira em todas as direções das três dimensões.
O modelo matemático utilizado para o desenvolvimento do projeto foi baseado no experimento da câmera pinhole, que consiste em mostrar de maneira simples como as imagens são formadas. Geometricamente, nós conseguimos representar esse experimento em um plano cartesiano
Já que estamos tratando de um plano de duas dimensões, o vetor de coordenadas é do tipo:
Na imagem, conseguimos visualizar o objeto na coordenada
- A distância entre a imagem projetada e o pinhole é
$d$ (distância focal), então podemos concluir que$x_p=-d$ .
Através de semelhança de triângulos, podemos encontrar a razão entre as coordenadas
Logo, podemos substituir
Como queremos fazer transformações matriciais para a projeção, nós precisamos converter as coordenadas dos pontos em coordenadas homogêneas. Para isso, basta adicionar uma variável auxiliar, no exemplo vamos utilizar a variável
Nós sabemos que, para chegar em uma matriz de transformação linear:
Portanto, através da utilização da variável auxiliar e da dedução da multiplicação matricial, podemos escrever a matriz de projeção do espaço bi-dimensional para o unidimensional como:
Aplicar essa teoria em um plano tridimensional não é muito diferente, precisamos definir o pinhole no ponto
A nova matriz de projeção do plano de três dimensões para o bi-dimensional é dada por:
E a nova matriz resultante da multiplicação matricial é:
-Primeiramente nós definimos um array através da biblioteca numpy representando todos os pontos do cubo, onde cada coluna é um vértice do cubo e a linhas representam as dimensões x,y,z de cada vértice, respectivamente. O array representa o cubo nas três dimensões com arestas paralelas aos eixos x, y e z, cujos vértices estão localizados nos seguintes pontos:
-
(-100, -100, -100): canto inferior esquerdo da face frontal.
-
(100, -100, -100): canto inferior direito da face frontal.
-
(100, 100, -100): canto superior direito da face frontal.
-
(-100, 100, -100): canto superior esquerdo da face frontal.
-
(-100, -100, 100): canto inferior esquerdo da face traseira.
-
(100, -100, 100): canto inferior direito da face traseira.
-
(100, 100, 100): canto superior direito da face traseira.
-
(-100, 100, 100): canto superior esquerdo da face traseira.
ou seja, a matriz:
-
Após isso, criamos uma matriz de projeção pinhole que depende da distância focal
$d$ (já foi mostrada anteriormente). -
Definimos as matrizes de rotação para cada dimensão
$x,y,z$ :
- É definida uma matriz de rotação total, que é o resultado da multiplicação das três matrizes de rotação, essa matriz é incrementada através de multiplicação matricial dependendo do input do usuário (se ele quer que o cubo gire no eixo x,y ou z):
# Matriz de rotação total
r = x @ y @ z
Caso o usuário queira que o cubo rotacione no eixo x, a matriz rotação será incrementada da seguinte forma:
r = r @ x
Além disso, caso o usuário queira que o cubo rotacione no eixo y, a matriz rotação será incrementada da seguinte forma:
r = r @ y
E por fim, caso o usuário queira que o cubo rotacione no eixo z, a matriz rotação será incrementada da seguinte forma:
r = r @ x
- É definida uma matriz de translação no eixo z, para que a visualização do cubo seja "de fora" da câmera, para isso, o cubo é transladado uma distância
$d$ no eixo z.
- É definida uma matriz de translação que translada o cubo para o centro original, do tipo:
Após todos esse passos, concluimos a implementação definindo uma matriz de transformação total e fazemos uma multiplicação matricial entre essa matriz resultante e o vetor de coordenadas do cubo.
# Matriz de transformação total do cubo
M = Tc @ pinhole @ Tz @ r
# Cubo com as transformações aplicadas
cubo_final = M @ cubo
Observação:
Essa sequencia de multiplicação matricial para chegar na matriz de transformação total é feita da maneira que, o cubo seja primeiramente rotacionado em algum ou todos os eixos, lembrando que para isso ele deve estar na coordendada
Por fim, é chamado o método pygame.draw.line() para desenhar as 12 arestas do cubo:
- Para desenhar cada linha, as coordenadas dos dois vértices que a linha conecta são divididas pelas suas coordenadas homogêneas (o último elemento da coluna correspondente) para obter as coordenadas normalizadas em relação ao plano de projeção. Essas coordenadas normalizadas são então usadas como argumentos para a função pygame.draw.line() para desenhar a linha.
# Cria linhas que ligam os pontos do cubo, transforma o XpWp em Xp e/ou YpWp em Yp
pygame.draw.line(screen, cor, (cubo_final[0, 0]/cubo_final[3, 0], cubo_final[1, 0]/cubo_final[3, 0]), (cubo_final[0, 1]/cubo_final[3, 1], cubo_final[1, 1]/cubo_final[3, 1]), 3)
O projeto foi feito em Python e utiliza as bibliotecas pygame
e numpy
.
https://github.com/WeeeverAlex/thecube
pip install -r requirements.txt
python cubo.py
O projeto possui as seguintes funcionalidades:
- Rotação do cubo em torno do eixo x caso o usuário aperte a tecla
x
. - Rotação do cubo em torno do eixo y caso o usuário aperte a tecla
y
. - Rotação do cubo em torno do eixo z caso o usuário aperte a tecla
z
. - Rotação do cubo nos três eixos caso o usuário aperte a tecla
r
. - Aumento da distância focal caso o usuário aperte a tecla
s
. - Diminuição da distância focal caso o usuário aperte a tecla
w
.