Rotación de OpenGL desde Velocity Vector

Esto debería ser fácil, pero he estado tratando de encontrar una explicación simple que pueda comprender. Tengo un objeto que me gustaría representar en OpenGL como un cono. El objeto tiene coordenadas x, y, z y un vector de velocidad vx, vy y vz. El cono debe apuntar en la dirección del vector de velocidad.

Entonces, creo que mi código de PyOpenGL debería verse como esto:

glPushMatrix() glTranslate(x, y, z) glPushMatrix() # do some sort of rotation here # glutSolidCone(base, height, slices, stacks) glPopMatrix() glPopMatrix() 

Entonces, ¿es correcto (hasta ahora)? ¿Qué debo poner en lugar de “# hacer algún tipo de rotación aquí #”?

En mi mundo, el eje Z apunta hacia arriba (0, 0, 1) y, sin ninguna rotación, también lo hace mi cono.


De acuerdo, la respuesta de Reto Koradi parece ser el enfoque que debería tomar, pero no estoy seguro de algunos de los detalles de la implementación y mi código no funciona.

Si entiendo correctamente, la matriz de rotación debe ser un 4×4. Reto me muestra cómo obtener un 3×3, por lo que asumo que el 3×3 debería ser la esquina superior izquierda de una matriz de identidad 4×4. Aquí está mi código:

 import numpy as np def normalize(v): norm = np.linalg.norm(v) if norm > 1.0e-8: # arbitrarily small return v/norm else: return v def transform(v): bz = normalize(v) if (abs(v[2]) < abs(v[0])) and (abs(v[2]) < abs(v[1])): by = normalize(np.array([v[1], -v[0], 0])) else: by = normalize(np.array([v[2], 0, -v[0]])) #~ by = normalize(np.array([0, v[2], -v[1]])) bx = np.cross(by, bz) R = np.array([[bx[0], by[0], bz[0], 0], [bx[1], by[1], bz[1], 0], [bx[2], by[2], bz[2], 0], [0, 0, 0, 1]], dtype=np.float32) return R 

Y aquí está la forma en que se inserta en el código de representación:

 glPushMatrix() glTranslate(x, y, z) glPushMatrix() v = np.array([vx, vy, vz]) glMultMatrixf(transform(v)) glutSolidCone(base, height, slices, stacks) glPopMatrix() glPopMatrix() 

Desafortunadamente, esto no está funcionando. Mis conos de caso de prueba simplemente no apuntan correctamente y no puedo identificar el modo de falla. Sin la línea “glutMultMatrixf (transform (v)”), los conos se alinean a lo largo del eje z, como se esperaba.


Esta funcionando. Reto Koradi identificó correctamente que la matriz de rotación debía transponerse para coincidir con el orden de las columnas de OpenGL. El código debería tener este aspecto (antes de la optimización):

 def transform(v): bz = normalize(v) if (abs(v[2]) < abs(v[0])) and (abs(v[2]) < abs(v[1])): by = normalize(np.array([v[1], -v[0], 0])) else: by = normalize(np.array([v[2], 0, -v[0]])) #~ by = normalize(np.array([0, v[2], -v[1]])) bx = np.cross(by, bz) R = np.array([[bx[0], by[0], bz[0], 0], [bx[1], by[1], bz[1], 0], [bx[2], by[2], bz[2], 0], [0, 0, 0, 1]], dtype=np.float32) return RT 

Un concepto útil para recordar aquí es que una transformación lineal también se puede interpretar como un cambio de los sistemas de coordenadas. En otras palabras, en lugar de representar los puntos que se transforman dentro de un sistema de coordenadas, también puede visualizar los puntos que permanecen en su lugar, pero sus coordenadas se expresan en un nuevo sistema de coordenadas. Al observar la matriz que expresa la transformación, los vectores base de este nuevo sistema de coordenadas son los vectores de columna de la matriz.

A continuación, los vectores base del nuevo sistema de coordenadas se denominan bx , by y bz . Dado que las columnas de una matriz de rotación deben ser ortonormales, bx , by y bz necesitan formar un conjunto ortonormal de vectores.

En este caso, el cono original está orientado a lo largo del eje z. Como quiere que el cono esté orientado a lo largo (vx, vy, vz) , usamos este vector como el eje z de nuestro nuevo sistema de coordenadas. Como queremos un sistema de coordenadas ortonormal, lo único que queda por hacer para obtener bz es normalizar este vector:

  [vx] bz = normalize([vy]) [vz] 

Dado que el cono es rotacionalmente simétrico, realmente no importa cómo se elijan los dos vectores base restantes, siempre que ambos sean ortogonales a bz y ortogonales entre sí. Una forma sencilla de encontrar un vector ortogonal arbitrario para un vector dado es mantener una coordenada 0 , intercambiar las otras dos coordenadas y cambiar el signo de una de esas dos coordenadas. Una vez más, el vector necesita ser normalizado. Los vectores que podríamos elegir con este enfoque incluyen:

  [ vy] [ vz] [ 0 ] by = normalize([-vx]) by = normalize([ 0 ]) by = normalize([ vz]) [ 0 ] [-vx] [-vy] 

El producto punto de cada uno de estos vectores con (vx, vy, vz) es cero, lo que significa que los vectores son ortogonales.

Si bien la elección entre estas (u otras variaciones) es principalmente arbitraria, se debe tener cuidado de no terminar con un vector degenerado. Por ejemplo, si vx y vy son ambos cero, usar el primero de estos vectores sería malo. Para evitar elegir un vector (cercano) degenerado, una estrategia simple es usar el primero de estos tres vectores si vz es más pequeño que vx y vy , y uno de los otros dos de lo contrario.

Con dos nuevos vectores base en su lugar, el tercero es el producto cruzado de los otros dos:

 bx = by x bz 

Todo lo que queda es rellenar la matriz de rotación con los vectores de columna bx , by y bz , y la matriz de rotación está completa:

  [ bx.x by.x bz.x ] R = [ bx.y by.y bz.y ] [ bx.z by.z bz.z ] 

Si necesita una matriz de 4×4, por ejemplo, porque está utilizando la función OpenGL existente heredada, puede extender esto a una matriz de 4×4:

  [ bx.x by.x bz.x 0 ] R = [ bx.y by.y bz.y 0 ] [ bx.z by.z bz.z 0 ] [ 0 0 0 1 ]