Tutorial 11 - Transformaciones
Fecha Miércoles, 05 septiembre a las 21:44:40
Tema DirectX/Opengl


Empecemos con un poco de movimiento. Vamos a realizar algunas transformaciones en 3-D, traslación, giro y escalado.



Para realizar transformaciones utilizaremos la clase matriz que se muestra a continuación:


    class Matrix
    {
    public:

        union
        {
            struct
            {
                float   _11, _12, _13, _14;
                float   _21, _22, _23, _24;
                float   _31, _32, _33, _34;
                float   _41, _42, _43, _44;
            };

            struct
            {
                float   StrafeX, UpX, ForwardX, DummyX;
                float   StrafeY, UpY, ForwardY, DummyY;
                float   StrafeZ, UpZ, ForwardZ, DummyZ;
                float   PositionX, PositionY, PositionZ, One;
            };

            float m[4][4];
        };

La matriz está definida como un union de C de forma que podemos acceder a los elementos que componen la matriz de diferentes formas.

Básicamente es la misma definición que tiene DirectX, pero le he añadido otro struct más que corresponden con los vectores de la base vectorial que representa la matriz.

En el fondo, una matriz define los tres vectores de un sistema de coordenadas. Así que cuando se hace una transformación (multiplicando matrices) lo que estamos haciendo es trasformar este sistema.

Hay que hacer una puntualización importante. Una transformación inversa, es aquella que deshace una transformación, de forma que vuelve a dejar el objeto como estaba. Esto se consigue multiplicando por la inversa de la matriz. Y he aquí una conclusión importante: EN UNA MATRIZ EN LA QUE LOS VECTORES QUE LA FORMAN SON ORTOGONALES (forman 90º entre ellos) LA INVERSA DE UNA MATRIZ ES IGUAL A LA TRANSPUESTA.

Debido a que siempre vamos a mantener dichos vectores ortogonales, cuando querramos realizar una operación inversa, sólo tenemos que transponer la matriz, no hace falta calcular la inversa.

Entonces, para realizar una transformación, calculamos una matriz con la transformación a realizar y se la pasamos al Driver de Vídeo. Este aplicará dicha transformación a todos los vértices a renderizar hasta que se fije una nueva transformación.

Para acumular transformaciones, sólo tenemos que multiplicar la matriz por la siguiente matriz con la transformación a aplicar. Veamos el método OnRender() para que nos sirva de ejemplo:


    ///////////////////////////////////////////////////////////////////////////////
    ///     OnRender: Esta función es invocada cuando el sistema está listo para renderizar.
    ///
    ///     @param  nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::OnRender()
    {
        fColorRGBA clearColor   = { 0.0f, 0.0f, 0.8f, 1.0f };   // Color de fondo.
        fColorRGBA yellowColor  = { 1.0f, 1.0f, 0.0f, 0.0f };   // Amarillo
        fColorRGBA greenColor   = { 0.0f, 1.0f, 0.0f, 0.0f };   // Verde

        Material yellowMaterial, greenMaterial;

        memset( &yellowMaterial, 0, sizeof(Material) );
        yellowMaterial.ambient  = yellowColor;
        yellowMaterial.diffuse  = yellowColor;
        yellowMaterial.specular = yellowColor;

        memset( &greenMaterial, 0, sizeof(Material) );
        greenMaterial.ambient   = greenColor;
        greenMaterial.diffuse   = greenColor;
        greenMaterial.specular  = greenColor;


        if ( m_pVideoDriver )
        {
            // Limpiamos la pantalla.
            m_pVideoDriver->ClearColor( clearColor );
            m_pVideoDriver->ClearZBuffer();
    // ********************************* NEW **************************************
            m_pVideoDriver->EnableDepthBuffer( true );
    // ****************************************************************************

            // Proyección en perspectiva
            m_pVideoDriver->SetPerspective( FOV, m_WindowProps.width, m_WindowProps.height );

            m_pVideoDriver->LookAt( Vector3(2.0f,1.0f,3.0f) ,   // Posición de la cámara.
                                    Vector3(0.0f,0.0f,0.0f) ,   // A dónde mira.
                                    Vector3(0.0f,1.0f,0.0f) );  // Dónde se encuentra el "techo", eje Y. 

            m_pVideoDriver->BeginRender();

    // ********************************* NEW **************************************

            // Reseteamos las transformaciones previas.
            Matrix  mIdentity;
            m_pVideoDriver->SetTransform ( mIdentity );

            m_pVideoDriver->EnableLighting( false );            // Desactivamos la iluminación.
            m_pVideoDriver->RenderAxis( 1.1f );                 // Renderizamos un eje de coordenadas en el origen.

    // ****************************************************************************

            if ( m_bWireframe )
            {
                m_pVideoDriver->SetRenderMode ( eWireFrame );   // Renderizado wireframe
                m_pVideoDriver->SetCullFaces  ( eCullNone  );   // Desactivamos la selección de caras a renderizar.
                m_pVideoDriver->EnableLighting( false      );   // Desactivamos la iluminación.
            }
            else
            {
                m_pVideoDriver->SetRenderMode ( eSolid     );   // Renderizado sólido.
                m_pVideoDriver->SetCullFaces  ( eCullBack  );   // No se renderizarán las caras traseras.
                m_pVideoDriver->EnableLighting( true       );   // Activamos la iluminación.
            }

    // ********************************* NEW **************************************

            Matrix  transform;
            Matrix  worldTransform;

            worldTransform = transform.RotateY( -m_AngleRotation );         // Rotamos sobre el eje Y.
            m_pVideoDriver->SetTransform ( worldTransform );                // Aplicamos la transformación.

            m_pVideoDriver->SetMaterial  ( yellowMaterial );                // Fijamos el material
            RenderCube   ( 1.1f );                                          // Renderizamos el cubo.

            worldTransform  = transform.RotateY( m_AngleRotation );         // 1. Rotamos sobre el eje Y.
            worldTransform *= transform.TranslateX( 5.0f );                 // 2. Trasladamos 5 unidades.
            worldTransform *= transform.RotateY( 2.0f*m_AngleRotation );    // 3. Orbitamos sobre el cubo anterior.
            m_pVideoDriver->SetTransform ( worldTransform );                // Aplicamos la transformación.

            m_pVideoDriver->SetMaterial  ( greenMaterial );                 // Fijamos el material
            RenderCube   ( 1.0f );                                          // Renderizamos el cubo.

    // ********************************* NEW **************************************

            m_pVideoDriver->EndRender();
        }
    }

Lo primero que se ha añadido, es habilitar el DepthBuffer. Esto hace que cada pixel que se va a renderizar en pantalla tenga asociado un valor que indica si está más lejos o más cerca. De esta forma los elementos más próximos que ocultan a otros más lejanos serán los que se pinten en pantalla.

Posteriormente se aplica la transformación identidad para anular cualquier transformación realizada en la iteración anterior. Se habilita la iluminación y dibujamos un sistema de coordenadas que nos sirva de referencia.

A continuación definimos una matriz (transform) para realizar un trasformación individual. El producto acumulado de estas transformaciones indiviuales en worldTransform nos dará como resultado la transformación final.

Fijaros bien que el orden en el que se realizan las tranformaciones es importante. Se ve más claro en el cubo que da órbitas. Dicho cubo (el verde) para rotar sobre sí mismo, hay que hacerlo rotar en primer lugar y luego trasladarlo. Posteriormente se vuelve a aplicar una transformación de rotación para que describa órbitas.

Podéis descargaros el ejemplo: tutorial-11.zip



Compartir en Facebook



Este artículo proviene de Programacion Grafica: Desarrollo 2D/3D con C++ y DirectX/OpenGL/GLUT/SDL - Windows/Linux
http://www.programaciongrafica.com

La dirección de esta noticia es:
http://www.programaciongrafica.com/modules.php?name=News&file=article&sid=12