Movimiento local 3D

Iniciado por Desiresportal, 4 Febrero 2016, 18:08 PM

0 Miembros y 2 Visitantes están viendo este tema.

Desiresportal

Estoy buscando la forma de hacer que un cubo 3D de mueva sobre su eje X aun habiendolo rotado en todos sus ejes. Me esta siendo imposible encontrar algo util al respecto y me estoy empezando a hartar de no poder avanzar en mis videojuegos.

He visto que hay mucha teoria, pero no veo nadie que explique como aplicarlo sobre un objeto. He llegado a intentarlo con una formula que devuelve esta matriz: "[[1,0,0],[0,1,0],[0,0,1]]". Evidentemente los valores de esta matriz son el resultado de un objeto cuyos angulos son 0.

Mi pregunta es: ¿Como hago que un cubo se mueva sobre su eje X si su orientacion sobre sus ejes X, Y y Z no son igual a 0?

Se que para movimientos en mundos 2D se utiliza "X += cos(angulo_en_radianes)*desplazamiento" y "Y += sin(angulo_en_radianes)*desplazamiento". Pero en mundos 3D es mucho mas complejo y no llego a entenderlo del todo.

ivancea96

Según cómo quieras la rotación. Puedes rotar en los 3 ejes, una rotación completa, o puedes rotar en 2 ejes, como se haría en un shooter: vertical y horizontal. Este último sin duda es más fácil.

Si lo que quieres es rotar en 3 ejes, si usas OpenGL u otro motor gráfico que te lo permita, te dará funciones. por ejemplo, glRotate.

Si quieres modificar todos los puntos tú a mano, lo mejor es que esches un vistazo por ahí: https://en.wikipedia.org/wiki/Rotation_matrix

Desiresportal

Si la verdad es que buscaba la rotacion de los tres ejes a mano para hacer mas cosas que simplemente el movimiento. Por supuesto utilizo "glRotatef()" para posicionar los objetos en su sitio y con su orientacion, pero necesito obtener el resultado del calculo hecho a mano para otras tareas del juego.

¿Se puede obtener de OpenGL la posicion de un objeto? Es decir, si sé que OpenGL puede rotar los objetos por mi, desplazarlos en cierto eje (del objeto, no de los ejes globales) y devolverme sus nuevas posiciones, lo haría mucho mas facil y seguramente mucho mas rapido.

Con dos angulos lo he logrado hacer perfecto, pero con el tercer angulo es donde se tuerce todo. La cuestion es que necesito utilizar los 3 ejes de rotacion.

Me guardo la pagina del enlace, pero ahora mismo no entiendo ni papa. Lo cierto es que a mi las matematicas avanzadas, poco. Soy mas de calculos sencillos. Tengo entendido que para el proceso que busco hay que tener minimo matematicas de bachiller. Justo donde no he llegado. Y el ingles no domino nada. Como mucho los lenguajes de programacion y palabras sueltas.

Muchas gracias por la respuesta. Intentaré hacer algo con ello este finde. El lunes comento.

class_OpenGL

#3
No sé si estás usando el OpenGL antiguo o el moderno. No sabiendo eso, lo único que puedo hacer es orientarte. Tampoco sé si para rotar el cubo has usado ángulos, o has rotado el cubo alrededor de un eje.
Si lo has hecho con ángulos, espero que lo hayas hecho con los de Euler.

Si has usado un eje para rotarlo, es bastante simple. Lo único que tienes que hacer es rotar el vector unitario î alrededor de ese eje de rotación usado con el cubo, después multiplicas el resultado por las unidades que te quieres mover, y luego le sumas ese vector resultante a todos los vértices del cubo.

Esta es una representación gráfica:



En pseudocódigo, sería así:

vec3 eje_rotacion = {0.6, 0.4, 0.0} --Este evidentemente es arbitrario
vec3 posicion_cubo = {0, 0, 0} -- También es arbitrario
vec3 vector_unitario = {1, 0, 0} --Este no es arbitrario. Tiene que ser así
float angulo = 0.3 -- Arbritario
float unidades_a_mover = 6.5;

rotar_cubo(eje_rotacion, angulo, posicion_cubo)
rotar_eje(eje_rotacion, angulo, vector_unitario)

trasladar_cubo(posicion_cubo + unidades_a_mover*vector_unitario)


Es difícil de explicar, pues es 3D y solo disponemos (al menos yo) de monitores 2D XDD

Espero que se haya comprendido

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

class_OpenGL


Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

Desiresportal

A ver. Vayamos por puntos.

Cuando dices "arbitrario" en una matriz, ¿te refieres a valores que puedo cambiar y valores que no? ¿O te refieres a valores que son aleatorios? Como me digas que son valores aleatorios me quedo peor que antes.

Por otra parte, yo utilizo "glTranslatef()" y "glRotatef()" para mover el centro del objeto que voy a dibujar. Despues dibujo los vertices y elimino las transformaciones realizadas. Para ser mas concretos lo hago con el siguiente orden:

glTranslatef(objPruebas1.x, objPruebas1.y, objPruebas1.z);

glRotatef(objPruebas1.rotX, 1.0, 0.0, 0.0);
glRotatef(objPruebas1.rotY, 0.0, 1.0, 0.0);
glRotatef(objPruebas1.rotZ, 0.0, 0.0, 1.0);


En mi caso los objetos tienen tres floats de posicion y tres floats de rotacion indicados en grados, no en radianes. Para dibujar no hay problema, el problema son otras cosas que quiero desarrollar y que no puedo hasta no tener una funcion para movimientos locales 3D.

Y por ultimo, cosillas que no alcanzo a entender:
- ¿Cual es la diferencia entre "rotar eje" y "rotar cubo"?
- ¿El vector unitario debo calcularlo? ¿O ya lo tengo y aún no lo sé?


Las imagenes las tendiendo y es justo lo que busco conseguir. El problema es el codigo. Hasta hace algun tiempo para mi los vectores solamente eran arrays de datos donde acumular variables, objetos, enemigos,... Pero ahora me encuentro en la necesidad de entender las matematicas para manejarlos.


Me estoy haciendo un lio con el "pseudocodigo" ese. Ponme un codigo para esto y ya trataré de entenderlo despues (que es lo que he hecho mogollon de veces [no siempre, pero muchas veces]). ¿Que debería poner en donde tengo "#5248596GIRLEP"?

struct objType {

float posX;
float posY;
float posZ;

float rotX;
float rotY;
float rotZ;

objType();

};

objType::objType() {

posX = 0.0;
posY = 0.0;
PosZ = 0.0;

rotX = 0.0;
rotY = 0.0;
rotZ = 0.0;

}

objType movimientoLocal(float XOrigen = 0.0, float YOrigen = 0.0, float ZOrigen = 0.0, float rotX = 0.0, float rotY = 0.0, float rotZ = 0.0, float desplazamientoX = 0.0, float desplazamientoY = 0.0, float desplazamientoZ = 0.0) {
objType tempObj;

/*
¿Cual es el codigo que debería poner aqui?
#5248596GIRLEP (Esto tan extraño es como referencia, nada mas.)
*/

tempObj.rotX = rotX;
tempObj.rotY = rotY;
tempObj.rotZ = rotZ;

return tempObj;
}

objType objPruebas;

int main() {

objPruebas.rotX = 154.2;
objPruebas.rotY = 362.4;
objPruebas.rotZ = 15.7;
objPruebas = movimientoLocal(objPruebas.posX, objPruebas.posY, objPruebas.posZ, objPruebas.rotX, objPruebas.rotY, objPruebas.rotZ, 1.2, 0.0, 0.0);

cout << "La posicion del objeto es: {" << objPruebas.posX << "," << objPruebas.posY << "," << objPruebas.posZ << "}" << endl;

return 0;
}


Como se puede ver he puesto tres ejes de desplazamiento. Esto se debe a que aveces puede ser util un eje u otro dependiendo de como este distribuida la malla del objeto. Aveces el eje X del objeto puede representar el adelante y atrás ("+X" y "-X") de este. Otras veces puede ser el eje Z ("+Z" y "-Z"). Es por tener a mano un código mas polivalente.

También es evidente que podría pasar el objeto por referencia e indicarle los valores de desplazamiento para ahorrarme pasar tantas variables, pero no sería tan polivalente. También habría que evitar que la función utilizase la estructura del objeto en cuestión, pero para el ejemplo es lo de menos. Ya me encargaré después de adecuarla a cada necesidad que me surja.

Como referencia, yo me sé manejar con el logic brick de desplazamiento de Blender. Es por eso que me gustaría tener una función similar en C++ (pero en otros lenguajes también me puede ser útil para el futuro).

class_OpenGL

#6
1º aclaración: un valor arbitrario es aquel valor que tu decides, no es un valor aleatorio.
2º Aclaración: Vas a necesitar saber sobre matrices para entender todo lo que voy a explicar abajo.

//////////////////////////////////////////////////////////////

Bueno, sabiendo que trabajas con el OpenGL antiguo, te puedo indicar mejor.

Cuando rotas con glTranslatef, los tres últimos parámetros son vectores que representan ejes de rotación. Entonces, en resumidas cuentas, hay que seguir estos pasos:

1º Paso (Opcional): Declarar una estructura llamada vec3, que represente un vector en 3D:

Código (cpp) [Seleccionar]
struct vec3 {
   float x;
   float y;
   float z;
};


Además de una estructura que represente una matriz de 3x3:

Código (cpp) [Seleccionar]
struct mat3 {
   float elementos[3][3];
};



2º Paso: Declarar un vector 3D que represente la dirección relativa en la que quieres mover el cubo (en tu caso, como lo quieres mover por el eje X, pues el vector sería (1, 0, 0)):

Código (cpp) [Seleccionar]
vec3 eje_desplazamiento = {1, 0, 0};

3º Paso: Crear matriz de rotación. Para realizar este paso, debes saber que la matriz de rotación sobre un eje es la siguiente:



Una vez sabido esto, declaramos una función que asigne una matriz de rotación a nuestra matriz:

Código (cpp) [Seleccionar]
void rotar_matriz(float angulo, mat3& matriz, vec3& eje) {
   matriz.elementos[0][0] = cos(angulo) + pow(eje.x, 2.0)*(1 - cos(angulo));
   matriz.elementos[0][1] = eje.x*eje.y*(1 - cos(angulo)) - eje.z*sin(angulo);
   matriz.elementos[0][2] = eje.x*eje.z*(1 - cos(angulo)) + eje.y*sin(angulo);

   matriz.elementos[1][0] = eje.x*eje.y*(1 - cos(angulo)) + eje.z*sin(angulo);
   matriz.elementos[1][1] = cos(angulo) + pow(eje.y, 2.0)*(1 - cos(angulo));
   matriz.elementos[1][2] = eje.y*eje.z*(1 - cos(angulo)) - eje.x*sin(angulo);

   matriz.elementos[2][0] = eje.x*eje.z*(1 - cos(angulo)) - eje.y*sin(angulo);
   matriz.elementos[2][1] = eje.y*eje.z*(1 - cos(angulo)) + eje.x*sin(angulo);
   matriz.elementos[2][2] = cos(angulo) + pow(eje.z, 2.0)*(1 - cos(angulo));
}


4º Paso: Crear una función (o si quieres sobrecargas el operador *) que multiplique una matriz por un vector:

Código (cpp) [Seleccionar]
void multiplicar(mat3& matriz, vec3& vector) {
   vec3 resultado = {0, 0, 0};

   resultado.x = matriz.elementos[0][0] * vector.x + matriz.elementos[0][1] * vector.y + matriz.elementos[0][2] * vector.z;
   resultado.y = matriz.elementos[1][0] * vector.x + matriz.elementos[1][1] * vector.y + matriz.elementos[1][2] * vector.z;
   resultado.y = matriz.elementos[2][0] * vector.x + matriz.elementos[2][1] * vector.y + matriz.elementos[2][2] * vector.z;

   vector.x = resultado.x;
   vector.y = resultado.y;
   vector.z = resultado.z;
}


5º Paso: rotar el vector que declaramos en el paso dos tal y como has rotado el cubo:

Código (cpp) [Seleccionar]
vec3 eje_desplazamiento = {1, 0, 0};
mat3 matriz_rotacion;

rotar_matriz(objPruebas1.rotX, matriz_rotacion, {1, 0, 0});
multiplicar(matriz_rotacion, eje_desplazamiento);
rotar_matriz(objPruebas1.rotY, matriz_rotacion, {0, 1, 0});
multiplicar(matriz_rotacion, eje_desplazamiento);
rotar_matriz(objPruebas1.rotZ, matriz_rotacion, {0, 0, 1}); // Date cuenta que el tercer parametro es el mismo que usabas en glRotatef
multiplicar(matriz_rotacion, eje_desplazamiento);


5º Paso: desplazar el cubo. Ahora ya tenemos el eje donde se desplazará el cubo rotado tal y como hemos rotado el cubo. Ahora tenemos que desplazar el cubo:

Código (c++) [Seleccionar]
float unidades_a_desplazar = 5.0f; // Aquí pones las unidades que te quieres desplazar. Por ejemplo, yo he puesto 5.0f, pero tu puedes poner el valor que quieras.

eje_desplazamiento.x *= unidades_a_desplazar;
eje_desplazamiento.y *= unidades_a_desplazar;
eje_desplazamiento.z *= unidades_a_desplazar;

glTranslatef(eje_desplazamiento.x, eje_desplazamiento.y, eje_desplazamiento.z);


Como ves, es un proceso relativamente largo, pero si quieres hacerlo manualmente, me temo que no tenemos otra salida.

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

Desiresportal

Uff... ahora mismo ando algo perdido de tiempo, pero cuando pueda trataré de ponerlo en practica.

Gracias por la explicación. Si que es largo el proceso, pero una vez que se tiene hecho es solo copiar y pegar. Y si se entiende el proceso es fácil modificarlo para ajustarlo a las nuevas necesidades.

Si me surge alguna duda mas, preguntaré aquí mismo.

Gracias de nuevo.

Desiresportal

Ok. Al final ya lo conseguí. Pero justo al terminar me dí cuenta de un error. Las rotaciones no ocurren sobre los ejes del objeto, sino sobre los ejes del mundo de OpenGL (yo tambien suelo llamarlos "ejes globales". No sé si es correcto.).

Ahora que ya he terminado de entender el movimiento local tridimensional, ¿como hago para que el objeto rote sobre sus propios ejes? En juegos FPS es facil porque solo hay que rotar el eje Z y el eje X. Pero si quiero hacer uno de naves o coches la cosa se complica. Doy por sentado que la clave esta en "glRotatef(angulo, fuerzaX, fuerzaY, fuerzaZ)" y que hay que aplicar diferentes fuerzas dependiendo del eje a rotar.

PD: Por cierto, tuve problemas para aplicar el proceso. En el ejemplo no se menciona que el orden en que se aplican las rotaciones de OpenGL deben ser del reves que en los calculos para el movimiento local. Lo menciono por si alguien tiene el mismo problema y no encuentra la solucion de ninguna forma.

En resumen: Lo conseguí, pero ahora tengo problemas con las rotaciones locales. He buscado, pero no encuentro nada que me ayude. ¿Una ayudita?

Gracias de nuevo.

class_OpenGL

#9
Todo es cuestión de ordenarse. Hace tiempo que no leo el tema, así que puede que conteste adecuadamente.

Te aconsejo que leas una página que trata sobre el OpenGL moderno (es decir, no usa glRotatef ni ese tipo de funciones pues han sido descatalogadas). Aunque no trate con el OpenGL antiguo, creo que el concepto se puede captar, pues habla de las matrices de modelado, de visionado y de proyección.
http://learnopengl.com/#!Getting-started/Coordinate-Systems
El siguiente link trata el tema de la cámara:
http://learnopengl.com/#!Getting-started/Camera

En concreto, la idea fundamental es que hay que tener cuidado con el orden en el que se realizan las operaciones. Por ejemplo, como sabrás, no es lo mismo primero trasladar y luego rotar. Entonces, las trasformaciones relativas a la transformación del objeto (escalarlo, moverlo, rotarlo...) hazlas sobre la matriz de modelado y visionado (GL_MODELVIEW); y las transformaciones de proyección (ortográfica o en perspectiva) hazlas sobre la matriz de proyección (GL_PROJECTION).

Dentro del orden de transformación del objeto deberías seguir el siguiente:
Primero trasformas el objeto (lo escalas o lo rotas localmente).
Luego mueves el objeto donde quieras.

Con esto consigues que al hacer una rotación local se vea adecuadamente.

Si los problemas están con la cámara, te aconsejo que veas el link que te he dado antes (http://learnopengl.com/#!Getting-started/Camera)

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL