xyz transformación rotación rotacion resueltos matriz matrices las identificar explicacion encontrar ejes ejercicios desplazamiento coordenadas cartesianas opengl math matrix 3d

opengl - transformación - ¿Cómo compongo una matriz de rotación con ángulos legibles desde cero?



rotacion de coordenadas cartesianas (1)

Lo único que siempre me ha impedido hacer programación en 3D es no entender cómo funcionan las matemáticas. Puedo seguir las matemáticas bien en el flujo de programación usando métodos y funciones, entonces todo es claro y lógico para mí, pero en notación matemática, simplemente no puedo sacar cara o cruz de él.

He estado leyendo sitios web, viendo videos de institutos que intentan explicar esto, pero todos usan notación matemática y simplemente me pierdo, mi mente no lo traducirá a algo comprensible. Podría tener un defecto allí.

Además, simplemente usar el código de alguien no es mi interés, quiero entender la mecánica detrás de él, la lógica. Me encantaría usar el código de otra persona, pero realmente quiero entender cómo funciona.

La pregunta

¿Puede explicarme en términos simples sin notación matemática, solo programar notación / funciones / psuedocódigo, cómo implementar una transformación matricial a lo largo de los 3 ejes?

Idealmente, lo que quiero es el material / comprensión para escribir un método / objeto donde pueda definir los ángulos de 3 ejes similares a glRotate para rotar la colección de quads / triángulos que tengo. (Estoy tratando de programar una rotación 3D de las formas de un cubo sin tener acceso a las funciones de OpenGL para hacerlo por mí porque esto se hace en una llamada de sorteo cada vez que algo cambia en la lista de visualización).

¿Qué he hecho?

Intenté hacer una función de transformación de 90 grados para dominar las matemáticas, pero fracasé por completo en hacer una matriz adecuada, que en teoría debería haber sido la más simple de hacer. Puede ver mi intento fallido en toda su gloria en http://jsfiddle.net/bLfg0tj8/5/

Vec3 = function(x,y,z) { this.x = x; this.y = y; this.z = z; } Matrix = function Matrix() { this.matrixPoints = new Array(); this.rotationPoint = new Vec3(0,0,0); this.rotationAngle = 90; } Matrix.prototype.addVector = function(vector) { this.matrixPoints.push(vector); } Matrix.prototype.setRotationPoint = function(vector) { this.rotationPoint = vector; } Matrix.prototype.setRotationAngle = function(angle) { this.rotationAngle = angle; } Matrix.prototype.populate = function() { translateToOrigin = [[1,0,0-this.rotationPoint.x], [0,1,0-this.rotationPoint.y], [0,0,0-this.rotationPoint.z]]; rotationMatrix = [[0,-1,0], [0,1,0], [0,0,1]]; translateEnd = [[1,0,this.rotationPoint.x], [0,1,this.rotationPoint.y], [0,0,this.rotationPoint.z]]; currentColumn = 0; currentRow = 0; this.combomatrix = this.mergeMatrices(this.mergeMatrices(translateEnd,rotationMatrix), translateToOrigin); } Matrix.prototype.transform = function() { newmatrix = new Array(); for(c = 0;c<this.matrixPoints.length;c++) { newmatrix.push(this.applyToVertex(this.matrixPoints[c])); } return newmatrix; } Matrix.prototype.applyToVertex = function(vertex) { ret = new Vec3(vertex.x,vertex.y,vertex.z); ret.x = ret.x + this.combomatrix[0][0] * vertex.x + this.combomatrix[0][1] * vertex.y + this.combomatrix[0][2] * vertex.z; ret.y = ret.y + this.combomatrix[1][0] * vertex.x + this.combomatrix[1][1] * vertex.y + this.combomatrix[1][2] * vertex.z; ret.z = ret.z + this.combomatrix[2][0] * vertex.x + this.combomatrix[2][1] * vertex.y + this.combomatrix[2][2] * vertex.z; return ret; } Matrix.prototype.mergeMatrices = function(lastStep, oneInFront) { step1 = [[0,0,0],[0,0,0],[0,0,0]]; step1[0][0] = lastStep[0][0] * oneInFront[0][0] + lastStep[0][1] * oneInFront[1][0] + lastStep[0][2] * oneInFront[2][0]; step1[0][1] = lastStep[0][0] * oneInFront[0][1] + lastStep[0][1] * oneInFront[1][1] + lastStep[0][2] * oneInFront[2][1]; step1[0][2] = lastStep[0][0] * oneInFront[0][2] + lastStep[0][1] * oneInFront[1][2] + lastStep[0][2] * oneInFront[2][2]; //============================================================ step1[1][0] = lastStep[1][0] * oneInFront[0][0] + lastStep[1][1] * oneInFront[1][0] + lastStep[1][2] * oneInFront[2][0]; step1[1][1] = lastStep[1][0] * oneInFront[0][1] + lastStep[1][1] * oneInFront[1][1] + lastStep[1][2] * oneInFront[2][1]; step1[1][2] = lastStep[1][0] * oneInFront[0][2] + lastStep[1][1] * oneInFront[1][2] + lastStep[1][2] * oneInFront[2][2]; //============================================================ step1[2][0] = lastStep[2][0] * oneInFront[0][0] + lastStep[2][1] * oneInFront[1][0] + lastStep[2][2] * oneInFront[2][0]; step1[2][1] = lastStep[2][0] * oneInFront[0][1] + lastStep[2][1] * oneInFront[1][1] + lastStep[2][2] * oneInFront[2][1]; step1[2][2] = lastStep[2][0] * oneInFront[0][2] + lastStep[2][1] * oneInFront[1][2] + lastStep[2][2] * oneInFront[2][2]; return step1; } Matrix.prototype.getCurrentMatrix = function() { return this.matrixPoints; } myvectors = [new Vec3(50,50,0), new Vec3(20,80,0), new Vec3(80, 80, 0)]; function drawVectors(vectors,color) { for(c=0;c<vectors.length;c++) { document.getElementById("whoa").innerHTML += ''<div style="color:''+color+'';position:absolute;left:''+vectors[c].x+''px; top:''+vectors[c].y+''px;z-index:''+vectors[c].z+'';">(''+c+'').</div>''; } } matrix = new Matrix(); for(c=0;c<myvectors.length;c++) { matrix.addVector(myvectors[c]); } matrix.setRotationPoint(new Vec3(50,70,0)); matrix.populate(); somematrix = matrix.transform(); drawVectors(matrix.getCurrentMatrix(),"lime"); // draw current matrix that was hand coded drawVectors([matrix.rotationPoint],''white''); // draw rotation point drawVectors(somematrix,"red"); // transformed matrix... somehow two points merge

<div id="whoa" style="position:relative;top:50px;left:150px;background-color:green;color:red;width:400px;height:300px;"> &nbsp; </div>

El texto verde es el triángulo original, el punto blanco el punto central, los puntos rojos la transformación fallida (creo, porque no está alineada alrededor del punto central). El tutorial en el que estaba pensaba cómo combinar matrices en una matriz combinada, pero supongo que me equivoqué en alguna parte.

Como dije, es realmente muy difícil para mí entender la notación matemática y hablar. Y no ayudar es que la mayoría de los maestros omiten partes de la explicación. Me tomó 2 horas solo para comprender cuándo multiplicar matrices, necesita agregar cada paso juntos en lugar de seguir multiplicando. Yay para explicaciones.

Un ejemplo práctico con lo que trabajo / quiero trabajar

Por ejemplo, tengo un cubo, cargado desde un archivo obj de frente de onda ubicado en el mundo en

x = 50 y = 100 z = 200

El cubo se dibuja usando quads y algunos mapas uv. No hay problemas aquí. Se representa maravillosamente con todas las texturas que se muestran correctamente.

Estas son las coordenadas de ubicación para cada "cara" del cubo que se dibuja usando un quad.

// Front face -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, // Back face -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // Top face -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, // Bottom face -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, // Right face 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, // Left face -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0

Entonces esto funciona muy bien. Pero, ¿qué pasa si quiero que este cubo gire 90 grados a lo largo del eje xy 45 grados alrededor del eje z? No puedo usar glRotate porque en el momento en que paso los datos al objeto tesselator no puedo hacer ninguna transformación de matriz a través de las funciones opengl porque solo está tomando los datos, en realidad no los representa per se.

La forma en que se almacenan los datos es la siguiente:

WaveFrontObject() | |-> Groups(String groupname) | |-> Faces() | |-> Vertex(float x, float y, float z)[] |-> Float UVmap[] corresponding to each vertex |-> drawFace() // Draws the face as a quad or triangle

Por lo tanto, cada una de las coordenadas anteriores que proporcioné se almacena como una cara del objeto de frente de onda en el "cubo" del grupo.

Cuando el cubo se agrega al tesselator, se traduce a las coordenadas correctas en el mundo y se vuelve normal.

Sin embargo, siempre hace lo mismo. Si quisiera que se renderizara en ángulo, tendría que hacer un objeto de frente de onda separado en este momento para poder hacerlo. En mi opinión, eso es una locura cuando se puede resolver con algunas matemáticas.

Necesario en la respuesta

  1. Explicación paso a paso de cómo construir una matriz de traducción y un intento de explicarme las matemáticas.
  2. Explicación sobre cómo aplicar la matriz de traducción a los cuádriceps / triángulos en las caras mientras se mantienen orientados alrededor del centro de su ubicación.

    x = 50.5 y = 100.5 z = 200.5

  3. Algún ejemplo / pseudocódigo para acompañar la explicación.

El lenguaje de programación utilizado para explicar no es realmente relevante mientras esté en la familia C

Por favor, trate de mantenerse alejado de la notación matemática / hablar. No sé qué es alfa beta, eso es, sí sé qué es el eje x, el eje y y el eje z. Sé cuáles son los ángulos, pero no sé los nombres que los matemáticos encuentran para ello.

Si desea utilizar nombres matemáticos, explíqueme cuáles son en el mundo / código 3D y cómo se forman / calculan.

Simplemente quiero hacer un método / objeto a lo largo de las líneas de

Matrix.transformVertices(vertices[], 90deg x, 45 deg y, 0 deg z);


Entonces, la pregunta realmente es comprender matrices de transformación homogéneas 4x4

bueno, sin las matemáticas detrás, lo único que queda es la representación / significado geométrico, que es mucho mejor para la abstracción / comprensión humana.

1. Entonces, ¿qué es la matriz 4x4?

Es la representación de algún sistema de coordenadas cartesianas y se compone de:

  1. 3 vectores básicos (uno para cada eje) rojo, verde, azul

    Entonces, si los vectores rojo, verde y azul son perpendiculares entre sí, entonces el sistema de coordenadas es ortogonal . Si también son vectores unitarios, entonces es ortonormal (como, por ejemplo, la matriz unitaria).

  2. punto de origen gris

  3. proyección y lado homogéneo (resto inferior sin marcar de la matriz)

    Esta parte solo está ahí para permitir la rotación y la traslación a la vez, por lo tanto, el punto utilizado debe ser homogéneo, lo que significa en forma (x,y,z,w=1) para los puntos y (x,y,z,w=0) para la dirección vectores Si fuera solo (x,y,z) entonces la matriz sería 3x3 y eso no es suficiente para la traducción. No utilizaré ninguna proyección que sea difícil de explicar geométricamente.

Este diseño es de notación OpenGL, también hay representación transpuesta (los vectores son filas, no columnas)

ahora cómo transformar cualquier punto hacia / desde este sistema de coordenadas:

g=M*l; l=Inverse(M)*g;

dónde:

  • M es matriz de transformación
  • l es M punto de sistema de coordenadas local (LCS)
  • g es un punto del sistema de coordenadas global (GCS)

para la versión transpuesta ( DirectX ) es:

l=M*g; g=Inverse(M)*l;

Esto se debe a que la matriz de rotación ortogonal transpuesta también es inversa de sí misma

  • Para obtener más información, vea la anatomía de la matriz de transformación y la tubería gráfica 3D

2. cómo visualizarlo

Sí, puede dibujar los números de la matriz, pero no tienen sentido a primera vista, especialmente si los números están cambiando, así que dibuje los vectores axises como en la imagen de arriba. Donde cada eje es una línea desde el origin hasta el origin + line_size*axis_vector

3. cómo construirlo

Simplemente calcule los vectores de eje y el origen y colóquelos dentro de la matriz. Para garantizar la ortogonalidad, explote el producto cruzado (pero tenga cuidado con el orden de los multiplicantes para usar la dirección correcta)

4. efectos

  • la rotación se realiza girando los ejes para que pueda calcular cada eje por ecuación de círculo paramétrico ...
  • el escalado se realiza multiplicando ejes por factor de escala
  • sesgar es simplemente usar ejes no perpendiculares

5. rotación

Para la mayoría de los casos se usa la rotación incremental. Hay dos tipos

  • rotación local M''=M*rotation_matrix gira alrededor de ejes de coordenadas locales como si controlaras el avión, el automóvil o el jugador ... La mayoría de los motores / juegos no los usan y lo falsifican con ángulos de Euler, lo cual es una solución barata (tienen muchas peculiaridades y problemas) porque la mayoría de las personas que usan OpenGL ni siquiera saben que esto es posible y más bien apilan la lista de llamadas glRotate/glTranslate ...

  • rotación global M''=Inverse(Inverse(M)*rotation_matrix) matrix_ M''=Inverse(Inverse(M)*rotation_matrix) gira alrededor de los ejes del sistema de coordenadas global.

donde rotation_matrix es cualquier matriz de transformación de rotación estándar.

Si tiene un diseño de matriz diferente (transpuesto), las rotaciones locales y globales se calculan al revés ...

También puede calcular su rotation_matrix desde 3 ángulos como:

rotation_matrix=rotation_around_x(ax)*rotation_around_y(ay)*rotation_around_z(az);

ver Wiki matrices de rotación: 3D Rx,Ry,Rz de Basic rotations son lo que necesitas. Como puede ver, en realidad son ecuaciones paramétricas de círculo unitario. El orden de multiplicación cambia la forma en que los ángulos convergen a la posición objetivo. Esto se llama ángulos de Euler y no lo uso (integro los cambios de paso en su lugar, que no tienen restricciones si se hace correctamente, sin mencionar que es más simple).

De todos modos, si lo necesita, puede convertir la matriz de transformación en ángulos de Euler con relativa facilidad.

  • ¿Hay alguna manera de calcular la rotación 3D en los ejes X e Y a partir de una matriz 4x4

6. glRotate

Si quieres glRotate entonces debes usar quaternions en su lugar porque ¡eso es rotación alrededor del eje, no en 3 ángulos! Hay una solución alternativa:

  1. crear matriz de transformación N para ese eje
  2. luego transforma tu matriz M en ella
  3. girar N por ángulo
  4. luego transforma M de N a coordenadas globales

O puede usar Rodrigues_rotation_formula lugar

Para transformar Matrix a / de Matrix en este caso, simplemente transforme los ejes como puntos y deje el origen como está, ¡pero el origen de N debe ser (0,0,0)! o los vectores transformados deben tener w=0 lugar.

7. uso

Las transformaciones son acumulativas, lo que significa:

  • p''=M1*M2*M3*M4*p; es lo mismo que M=M1*M2*M3*M4; p''=M*p M=M1*M2*M3*M4; p''=M*p

Entonces, si tiene muchos puntos para transformar, entonces precalcula todas las transformaciones en una matriz única y la usa solo. No es necesario multiplicar puntos por todas las matrices posteriores. OK ahora el concepto:

debes tener 3 sistemas de coordenadas:

  • cámara C
  • mundo (generalmente unidad de matriz)
  • objeto O (cada objeto tiene su propia matriz)

así que si tiene un cubo con 8 vértices p0,...,p7 entonces debe realizar la transformación en cada punto desde las coordenadas locales del objeto a las coordenadas locales de la cámara. Algunos gfx api hacen algo de eso, por lo que aplica solo lo que tiene que realmente necesita:

  • p(i)''=inverse(C)*unit*M*p(i);

las transformaciones son acumulativas y la matriz de unidades no cambia nada, así que:

  • Q=inverse(C)*M; p(i)''=Q*p(i);

así que antes de dibujar el cálculo Q para el objeto dibujado, tome cada punto p(i) del objeto y calcule el p(i)'' transformado y dibuje / use el transformado ... El p(i)'' está en el sistema de coordenadas de la cámara local (x, y de la pantalla) pero no hay una perspectiva allí, así que antes de dibujar también puede agregar cualquiera de las matrices de proyección y dividir por z cordinate al final ... La proyección también es acumulativa, por lo que también puede estar dentro de Q

[edit1] Ejemplo de C ++

//$$---- Form CPP ---- //--------------------------------------------------------------------------- // apart from math.h include you can ignore this machine generated VCL related code #include <vcl.h> #pragma hdrstop #include "win_main.h" #include <math.h> //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TMain *Main; // pointer to main window ... //--------------------------------------------------------------------------- // Here is the important stuff some math first //--------------------------------------------------------------------------- const double deg=M_PI/180.0; double divide(double x,double y); void matrix_mul (double *c,double *a,double *b); // c[16] = a[16] * b[16] void matrix_mul_vector(double *c,double *a,double *b); // c[ 4] = a[16] * b[ 4] void matrix_subdet (double *c,double *a); // c[16] = all subdets of a[16] double matrix_subdet ( double *a,int r,int s);// = subdet(r,s) of a[16] double matrix_det ( double *a); // = det of a[16] double matrix_det ( double *a,double *b); // = det of a[16] and subdets b[16] void matrix_inv (double *c,double *a); // c[16] = a[16] ^ -1 //--------------------------------------------------------------------------- double divide(double x,double y) { if (!y) return 0.0; return x/y; } void matrix_mul (double *c,double *a,double *b) { double q[16]; q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]); q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]); q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]); q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]); q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]); q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]); q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]); q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]); q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]); q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]); q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]); q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]); q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]); q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]); q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]); q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]); for(int i=0;i<16;i++) c[i]=q[i]; } void matrix_mul_vector(double *c,double *a,double *b) { double q[3]; q[0]=(a[ 0]*b[0])+(a[ 1]*b[1])+(a[ 2]*b[2])+(a[ 3]); q[1]=(a[ 4]*b[0])+(a[ 5]*b[1])+(a[ 6]*b[2])+(a[ 7]); q[2]=(a[ 8]*b[0])+(a[ 9]*b[1])+(a[10]*b[2])+(a[11]); for(int i=0;i<3;i++) c[i]=q[i]; } void matrix_subdet (double *c,double *a) { double q[16]; int i,j; for (i=0;i<4;i++) for (j=0;j<4;j++) q[j+(i<<2)]=matrix_subdet(a,i,j); for (i=0;i<16;i++) c[i]=q[i]; } double matrix_subdet ( double *a,int r,int s) { double c,q[9]; int i,j,k; k=0; // q = sub matrix for (j=0;j<4;j++) if (j!=s) for (i=0;i<4;i++) if (i!=r) { q[k]=a[i+(j<<2)]; k++; } c=0; c+=q[0]*q[4]*q[8]; c+=q[1]*q[5]*q[6]; c+=q[2]*q[3]*q[7]; c-=q[0]*q[5]*q[7]; c-=q[1]*q[3]*q[8]; c-=q[2]*q[4]*q[6]; if (int((r+s)&1)) c=-c; // add signum return c; } double matrix_det ( double *a) { double c=0; c+=a[ 0]*matrix_subdet(a,0,0); c+=a[ 4]*matrix_subdet(a,0,1); c+=a[ 8]*matrix_subdet(a,0,2); c+=a[12]*matrix_subdet(a,0,3); return c; } double matrix_det ( double *a,double *b) { double c=0; c+=a[ 0]*b[ 0]; c+=a[ 4]*b[ 1]; c+=a[ 8]*b[ 2]; c+=a[12]*b[ 3]; return c; } void matrix_inv (double *c,double *a) { double d[16],D; matrix_subdet(d,a); D=matrix_det(a,d); if (D) D=1.0/D; for (int i=0;i<16;i++) c[i]=d[i]*D; } //--------------------------------------------------------------------------- // now the object representation //--------------------------------------------------------------------------- const int pnts=8; double pnt[pnts*3]= // Vertexes for 100x100x100 cube centered at (0,0,0) { -100.0,-100.0,-100.0, -100.0,+100.0,-100.0, +100.0,+100.0,-100.0, +100.0,-100.0,-100.0, -100.0,-100.0,+100.0, -100.0,+100.0,+100.0, +100.0,+100.0,+100.0, +100.0,-100.0,+100.0, }; const int facs=6; int fac[facs*4]= // faces (index of point used) no winding rule { 0,1,2,3, 4,5,6,7, 0,1,5,4, 1,2,6,5, 2,3,7,6, 3,0,4,7, }; double rep[16]= // 4x4 transform matrix of object (unit from start) at (0,0,+100) { 1.0,0.0,0.0, 0.0, 0.0,1.0,0.0, 0.0, 0.0,0.0,1.0,100.0, 0.0,0.0,0.0,1.0, }; double eye[16]= // 4x4 transform matrix of camera at (0,0,-150) { 1.0,0.0,0.0, 0.0, 0.0,1.0,0.0, 0.0, 0.0,0.0,1.0,-150.0, 0.0,0.0,0.0,1.0, }; //--------------------------------------------------------------------------- // this is how to draw it //--------------------------------------------------------------------------- void obj(double *pnt,int pnts,int *fac,int facs,double *rep,double *ieye) { // variables for drawing int i; double p0[3],p1[3],p2[3],p3[3],m[16],d; // gfx api variables (change to your stuff) Main is the main form of this application TCanvas *scr=Main->bmp->Canvas; double xs2=Main->ClientWidth/2,ys2=Main->ClientHeight/2; double v=xs2*tan(30.0*deg); // 60 degree viewing angle perspective projection matrix_mul(m,ieye,rep); // cumulate all needed transforms for (i=0;i<facs*4;) // go through all faces { // convert all points of face matrix_mul_vector(p0,m,&pnt[fac[i]*3]); i++; matrix_mul_vector(p1,m,&pnt[fac[i]*3]); i++; matrix_mul_vector(p2,m,&pnt[fac[i]*3]); i++; matrix_mul_vector(p3,m,&pnt[fac[i]*3]); i++; // here goes perspective divide by z coordinate if needed d=divide(v,p0[2]); p0[0]*=d; p0[1]*=d; d=divide(v,p1[2]); p1[0]*=d; p1[1]*=d; d=divide(v,p2[2]); p2[0]*=d; p2[1]*=d; d=divide(v,p3[2]); p3[0]*=d; p3[1]*=d; // here is viewport transform (just translate (0,0) to middle of screen in this case p0[0]+=xs2; p0[1]+=ys2; p1[0]+=xs2; p1[1]+=ys2; p2[0]+=xs2; p2[1]+=ys2; p3[0]+=xs2; p3[1]+=ys2; // draw quad // I use VCL GDI TCanvas you use what you have ... // and wireframe only to keep this simple (no Z buffer,winding culling,...) scr->Pen->Color=clAqua; // perimeter wireframe scr->MoveTo(p0[0],p0[1]); scr->LineTo(p1[0],p1[1]); scr->LineTo(p2[0],p2[1]); scr->LineTo(p3[0],p3[1]); scr->LineTo(p0[0],p0[1]); // scr->Pen->Color=clBlue; // face cross to visualy check if I correctly generate the fac[] // scr->MoveTo(p0[0],p0[1]); // scr->LineTo(p2[0],p2[1]); // scr->MoveTo(p1[0],p1[1]); // scr->LineTo(p3[0],p3[1]); } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void TMain::draw() { if (!_redraw) return; bmp->Canvas->Brush->Color=clBlack; bmp->Canvas->FillRect(TRect(0,0,xs,ys)); // compute inverse of camera need to compute just once for all objects double ieye[16]; matrix_inv(ieye,eye); // draw all objects obj(pnt,pnts,fac,facs,rep,ieye); Main->Canvas->Draw(0,0,bmp); _redraw=false; } //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { // window constructor you can ignore this ... (just create a backbuffer bitmap here) bmp=new Graphics::TBitmap; bmp->HandleType=bmDIB; bmp->PixelFormat=pf32bit; pyx=NULL; } //--------------------------------------------------------------------------- void __fastcall TMain::FormDestroy(TObject *Sender) { // window destructor release memory ... also ignoe this if (pyx) delete pyx; delete bmp; } //--------------------------------------------------------------------------- void __fastcall TMain::FormResize(TObject *Sender) { // on resize event ... just resize/redraw backbuffer also can ignore this xs=ClientWidth; xs2=xs>>1; ys=ClientHeight; ys2=ys>>1; bmp->Width=xs; bmp->Height=ys; if (pyx) delete pyx; pyx=new int*[ys]; for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y]; _redraw=true; } //--------------------------------------------------------------------------- void __fastcall TMain::FormPaint(TObject *Sender) { // repaint event can ignore _redraw=true; } //--------------------------------------------------------------------------- void __fastcall TMain::tim_redrawTimer(TObject *Sender) { // timer event to animate the cube ... _redraw=true; // rotate the object to see it in motion double ang,c,s; ang=5.0*deg; c=cos(ang); s=sin(ang); // rotate baround z by 5 degrees per timer step double rz[16]= { c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; ang=1.0*deg; c=cos(ang); s=sin(ang); // rotate baround x by 1 degrees per timer step double rx[16]= { 1, 0, 0, 0, 0, c, s, 0, 0,-s, c, 0, 0, 0, 0, 1 }; matrix_mul(rep,rep,rz); matrix_mul(rep,rep,rx); draw(); } //---------------------------------------------------------------------------

así es como se ve:

Y animación GIF con selección posterior:

[notas]

Si tienes más preguntas, entonces coméntame ...

[Edit2] operaciones básicas de vectores 3D a menudo necesarias

Si no sabe cómo calcular operaciones vectoriales como productos cruzados / de puntos o valores absolutos, consulte:

// cross product: W = U x V W.x=(U.y*V.z)-(U.z*V.y) W.y=(U.z*V.x)-(U.x*V.z) W.z=(U.x*V.y)-(U.y*V.x) // dot product: a = (U.V) a=U.x*V.x+U.y*V.y+U.z*V.z // abs of vector a = |U| a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z))

Aquí mi matemática vectorial C ++:

static double vector_tmp[3]; double divide(double x,double y) { if ((y>=-1e-30)&&(y<=+1e-30)) return 0.0; return x/y; } double* vector_ld(double x,double y,double z) { double *p=vector_tmp; p[0]=x; p[1]=y; p[2]=z; return p;} double* vector_ld(double *p,double x,double y,double z) { p[0]=x; p[1]=y; p[2]=z; return p;} void vector_copy(double *c,double *a) { for(int i=0;i<3;i++) c[i]=a[i]; } void vector_abs(double *c,double *a) { for(int i=0;i<3;i++) c[i]=fabs(a[i]); } void vector_one(double *c,double *a) { double l=divide(1.0,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]))); c[0]=a[0]*l; c[1]=a[1]*l; c[2]=a[2]*l; } void vector_len(double *c,double *a,double l) { l=divide(l,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]))); c[0]=a[0]*l; c[1]=a[1]*l; c[2]=a[2]*l; } void vector_neg(double *c,double *a) { for(int i=0;i<3;i++) c[i]=-a[i]; } void vector_add(double *c,double *a,double *b) { for(int i=0;i<3;i++) c[i]=a[i]+b[i]; } void vector_sub(double *c,double *a,double *b) { for(int i=0;i<3;i++) c[i]=a[i]-b[i]; } void vector_mul(double *c,double *a,double *b) // cross { double q[3]; q[0]=(a[1]*b[2])-(a[2]*b[1]); q[1]=(a[2]*b[0])-(a[0]*b[2]); q[2]=(a[0]*b[1])-(a[1]*b[0]); for(int i=0;i<3;i++) c[i]=q[i]; } void vector_mul(double *c,double *a,double b) { for(int i=0;i<3;i++) c[i]=a[i]*b; } void vector_mul(double *c,double a,double *b) { for(int i=0;i<3;i++) c[i]=a*b[i]; } double vector_mul( double *a,double *b) { double c=0; for(int i=0;i<3;i++) c+=a[i]*b[i]; return c; } // dot double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); } double vector_len2(double *a) { return (a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]); }

[Edit3] rotaciones locales para el control de cámara y objetos a través del teclado

Como esto se ha preguntado mucho últimamente aquí, algunos ejemplos de respuestas mías con demos:

  • control de vista de cámara estacionaria (matriz pseudo inversa parcial)
  • Control de cámara y reproductor (matriz inversa)
  • Cómo preservar la precisión con transformaciones acumulativas en el tiempo (matriz pseudo inversa completa)
  • Ejemplo de control de reproductor de OpenGL / C ++ / VCL simple estilo rotundus