Respostas:
Sim, você pode adicionar um vetor no caso de tradução. O motivo para usar uma matriz se resume a ter uma maneira uniforme de lidar com diferentes transformações combinadas.
Por exemplo, a rotação geralmente é feita usando uma matriz (verifique o comentário no @MickLH para outras formas de lidar com as rotações), para lidar com várias transformações (rotação / translação / dimensionamento / projeção ... etc) de maneira uniforme, você precisa codificá-los em uma matriz.
Bem, tecnicamente falando; uma transformação está mapeando um ponto / vetor para outro ponto / vetor.
p` = T(p);
onde p` é o ponto transformado e T (p) é a função de transformação.
Como não usamos uma matriz, precisamos fazer isso para combinar várias transformações:
p1 = T (p);
p final = M (p1);
Uma matriz não só pode combinar vários tipos de transformações em uma única matriz (por exemplo, afim, linear, projetiva).
Usar uma matriz nos dá a oportunidade de combinar cadeias de transformações e depois multiplicá-las em lote. Isso economiza uma tonelada de ciclos geralmente pela GPU (obrigado a @ChristianRau por apontar isso).
T final = T * R * P; // traduzir rotacionar projeto
p final = T final * p;
Também é bom ressaltar que as GPUs e até algumas CPUs são otimizadas para operações vetoriais; CPUs usando SIMD e GPUs sendo processadores paralelos controlados por dados, por isso, o uso de matrizes se encaixa perfeitamente na aceleração de hardware (na verdade, as GPUs foram projetadas para se ajustar às operações de matriz / vetor).
Se tudo o que você vai fazer é se mover ao longo de um único eixo e nunca aplicar nenhuma outra transformação, então o que você está sugerindo é bom.
O verdadeiro poder do uso de uma matriz é que você pode concatenar facilmente uma série de operações complexas e aplicar a mesma série de operações a vários objetos.
A maioria dos casos não é tão simples e, se você girar seu objeto primeiro, e quiser transformar os eixos locais em vez dos eixos mundiais, descobrirá que não pode simplesmente adicionar 10 a um dos números e fazer com que funcione corretamente .
Para responder sucintamente à pergunta "por que", é porque uma matriz 4x4 pode descrever operações de rotação, tradução e dimensionamento de uma só vez. Ser capaz de descrever qualquer um deles de maneira consistente simplifica muitas coisas.
Diferentes tipos de transformações podem ser mais simplesmente representados com diferentes operações matemáticas. Como você observa, a tradução pode ser feita apenas adicionando. Escala uniforme multiplicando por um escalar. Mas uma matriz 4x4 adequadamente criada pode fazer qualquer coisa. Portanto, o uso consistente de 4x4 torna o código e as interfaces muito mais simples. Você paga alguma complexidade para entender esses 4x4, mas muitas coisas ficam mais fáceis e rápidas por causa disso.
a razão para usar uma matriz 4x4 é para que a operação seja uma transformação linear . este é um exemplo de coordenadas homogêneas . O mesmo é feito no caso 2d (usando uma matriz 3x3). A razão para o uso de coordenadas homogêneas é para que todas as 3 formas geométricas de transformação possam ser feitas usando uma operação; caso contrário, seria necessário fazer uma multiplicação da matriz 3x3 e uma adição da matriz 3x3 (para a tradução). esse link do cegprakash é útil.
As traduções não podem ser representadas por matrizes 3D
Um argumento simples é que a tradução pode pegar o vetor de origem:
0
0
0
longe da origem, diga x = 1
:
1
0
0
Mas isso exigiria uma matriz tal que:
| a b c | |0| |1|
| d e f | * |0| = |0|
| g h i | |0| |0|
Mas isso é impossível.
Outro argumento é o teorema da Decomposição de Valor Singular , que diz que toda matriz pode ser composta com duas operações de rotação e uma escala. Não há traduções lá.
Por que matrizes podem ser usadas?
Muitos objetos modelados (por exemplo, um chassi de carro) ou parte de objetos modelados (por exemplo, um pneu de carro, uma roda motriz) são sólidos: as distâncias entre os vértices nunca mudam.
As únicas transformações que queremos fazer nelas são rotações e traduções.
A multiplicação de matrizes pode codificar rotações e traduções.
Matrizes de rotação possuem fórmulas explícitas, por exemplo: uma matriz de rotação 2D para ângulo a
é da forma:
cos(a) -sin(a)
sin(a) cos(a)
Existem fórmulas análogas para 3D , mas observe que as rotações 3D usam 3 parâmetros em vez de apenas 1 .
As traduções são menos triviais e serão discutidas mais tarde. Eles são a razão pela qual precisamos de matrizes 4D.
Por que é legal usar matrizes?
Porque a composição de múltiplas matrizes pode ser pré-calculada por multiplicação de matrizes .
Por exemplo, se vamos traduzir mil vetores v
do chassi de nosso carro com matriz T
e depois girar com matriz R
, em vez de fazer:
v2 = T * v
e depois:
v3 = R * v2
para cada vetor, podemos pré-calcular:
RT = R * T
e faça apenas uma multiplicação para cada vértice:
v3 = RT * v
Ainda melhor: se queremos colocar os vértices do pneu e da roda motriz em relação ao carro, apenas multiplicamos a matriz anterior RT
pela matriz em relação ao próprio carro.
Isso naturalmente leva à manutenção de uma pilha de matrizes:
Como a adição de uma dimensão resolve o problema
Vamos considerar o caso de 1D a 2D, que é mais fácil de visualizar.
Uma matriz em 1D é apenas um número e, como vimos em 3D, não pode fazer uma tradução, apenas um dimensionamento.
Mas se adicionarmos a dimensão extra como:
| 1 dx | * |x| = | x + dx |
| 0 1 | |1| | 1 |
e depois esquecemos a nova dimensão extra, obtemos:
x + dx
como queríamos.
Essa transformação 2D é tão importante que tem um nome: transformação de cisalhamento .
É legal visualizar essa transformação:
Observe como todas as linhas horizontais (fixas y
) são apenas traduzidas.
Por acaso, pegamos a linha y = 1
como nossa nova linha 1D e a traduzimos em uma matriz 2D.
As coisas são análogas em 3D, com matrizes de cisalhamento 4D da forma:
| 1 0 0 dx | | x | | x + dx |
| 0 1 0 dy | * | y | = | y + dy |
| 0 0 1 dz | | z | | z + dz |
| 0 0 0 1 | | 1 | | 1 |
E nossas antigas rotações / redimensionamentos 3D agora estão em forma:
| a b c 0 |
| d e f 0 |
| g h i 0 |
| 0 0 0 1 |
Também vale a pena assistir a este tutorial em vídeo de Jamie King .
Espaço afim
Espaço afim é o espaço gerado por todas as nossas transformações lineares em 3D (multiplicações de matrizes) junto com o cisalhamento 4D (traduções em 3D).
Se multiplicarmos uma matriz de cisalhamento e uma transformação linear 3D, sempre obtemos algo da forma:
| a b c dx |
| d e f dy |
| g h i dz |
| 0 0 0 1 |
Essa é a transformação afim mais geral possível, que faz a rotação / redimensionamento e tradução 3D.
Uma propriedade importante é que, se multiplicarmos 2 matrizes afins:
| a b c dx | | a2 b2 c2 dx2 |
| d e f dy | * | d2 e2 f2 dy2 |
| g h i dz | | g2 h2 i2 dz2 |
| 0 0 0 1 | | 0 0 0 1 |
sempre temos outra matriz afim de forma:
| a3 b3 c3 (dx + dx2) |
| d3 e3 f3 (dy + dy2) |
| g3 h3 i3 (dz + dz2) |
| 0 0 0 1 |
Os matemáticos chamam esse fechamento de propriedade e são necessários para definir um espaço.
Para nós, isso significa que podemos continuar fazendo multiplicações de matrizes para calcular as transformações finais felizes, e é por isso que usamos matrizes usadas em primeiro lugar, sem nunca obter transformações lineares 4D mais gerais que não são afins.
Projeção de Frustum
Mas espere, há mais uma transformação importante que fazemos o tempo todo: glFrustum
que faz com que um objeto 2x mais adiante pareça 2x menor.
Primeiro, obtenha alguma intuição sobre glOrtho
vsglFrustum
em: https://stackoverflow.com/questions/2571402/explain-the-usage-of-glortho/36046924#36046924
glOrtho
pode ser feito apenas com traduções + dimensionamento, mas como podemos implementar glFrustum
com matrizes?
Suponha que:
z = -1
tem um quadrado de comprimento 2z = -2
Se ao menos permitíssemos 4 vetores mais gerais do tipo:
(x, y, z, w)
com w != 0
, e além disso, identificamos todos (x, y, z, w)
com (x/w, y/w, z/w, 1)
, então uma transformação de frustum com a matriz seria:
| 1 0 0 0 | | x | | x | | x / -z |
| 0 1 0 0 | * | y | = | y | identified to | y / -z |
| 0 0 1 0 | | z | | z | | -1 |
| 0 0 -1 0 | | w | | -z | | 0 |
Se jogamos fora z
e w
no final, obtemos:
x_proj = x / -z
y_proj = y / -z
que é exatamente o que queríamos! Podemos verificar isso para alguns valores, por exemplo:
z == -1
, exatamente no plano que estamos projetando, x_proj == x
ey_proj == y
.z == -2
, then x_proj = x/2
: os objetos têm metade do tamanho.Observe como a glFrustum
transformação não é de forma afim: ela não pode ser implementada apenas com rotações e traduções.
O "truque" matemático de adicionar w
e dividir por ele é chamado de coordenadas homogêneas
Consulte também: pergunta relacionada ao Stack Overflow: https://stackoverflow.com/questions/2465116/understanding-opengl-matrices
Veja este vídeo para entender os conceitos de modelo, visão e projeção.
As matrizes 4x4 não são usadas apenas para traduzir um objeto 3D. Mas também para vários outros fins.
Veja isso para entender como os vértices do mundo são representados como matrizes 4D e como eles são transformados.