Tutorial 10 - Luces, normales, materiales
Fecha Miércoles, 05 septiembre a las 21:20:58
Tema DirectX/Opengl


En este tutorial vamos a añadir luz a nuestro mundo 3-D. Con ello podremos recrear objetos sólidos con sombreado.



En primer lugar hemos cambiado la estructura que define nuestro cubo de forma que almacenaremos lo que se denominan normales.

Una normal es un vector perpendicular a un plano cuya longitud es uno. Se utilizan vectores normales para calcular cómo debe renderizarse una cara, para calcular cómo incide la luz en ella en función de su inclinación respecto a la fuente de luz. Dado un vértice, que comparte caras, puede asociarse un vector normal a un vértice como el que resulta de la suma y normalización de los vectores de las caras a las que pertecede el vértice.

Así que ahora, para cada cara del cubo, tendremos un vector normal a ésta más los íncides que referenciarán a los vértices que componen la cara del cubo.


    //
    //      v7     v6
    //      *------*
    //   v4/.  v5 /|
    //    *-----*  |
    //   v|3*...|..* v2
    //    |.    | /
    //    *-----*/
    //   v0     v1
    //
    MeshCube GraphicApplication::m_Cube =
    {
        /* Vertex */
        {
            {-0.5f,-0.5f, 0.5f},    /* v0 */
            { 0.5f,-0.5f, 0.5f},    /* v1 */
            { 0.5f,-0.5f,-0.5f},    /* v2 */
            {-0.5f,-0.5f,-0.5f},    /* v3 */
            {-0.5f, 0.5f, 0.5f},    /* v4 */
            { 0.5f, 0.5f, 0.5f},    /* v5 */
            { 0.5f, 0.5f,-0.5f},    /* v6 */
            {-0.5f, 0.5f,-0.5f}     /* v7 */
        } ,

        /* Faces */
        {
            { { 0, 0, 1 }, {4,0,5,1} }, /* frontal   */
            { { 1, 0, 0 }, {5,1,6,2} }, /* derecha   */
            { { 0, 0,-1 }, {6,2,7,3} }, /* trasera   */
            { {-1, 0, 0 }, {7,3,4,0} }, /* izquierda */
            { { 0, 0, 1 }, {4,5,7,6} }, /* superior  */
            { { 0, 0,-1 }, {3,2,0,1} }  /* inferior  */
        }
    };

Para trabajar con luces, hemos definido la estructura Light que define una fuente de luz. Las habrá de tres tipos, direcciónal, puntual o cónica. También hemos definido la estructura Material para especificar cómo refleja la luz un objeto en función del material que lo compone.


    ///////////////////////////////////////////////////////////////////////////////
    /// enum   e_LIGHT_TYPE Tipos de luces.
    ///////////////////////////////////////////////////////////////////////////////
    enum e_LIGHT_TYPE
    {
        ePointLight,                        ///< Fuente puntual que emite luz de 360º.
        eSpotLight,                         ///< Fuente puntual que emite luz cónica (como una linterna).
        eDirectionalLight                   ///< Fuente de luz situada en el infinito (no importa su posición).
    };

    ///////////////////////////////////////////////////////////////////////////////
    /// struct   LIGHT Estructura que define las propiedades de la luz.
    ///
    /// rief   
    /// author  Antonio Lucas Moreno version 1.0 date 28/09/2005 9:26:20
    ///////////////////////////////////////////////////////////////////////////////
    struct Light
    {
        unsigned int    id;                 ///< Identificador de la luz.
        e_LIGHT_TYPE    type;               ///< Tipo de luz.
        Vector3         position;           ///< Posición (sólo para Point y Spot).
        Vector3         direction;          ///< Dirección.

        // Polinomio que describe como se atenúa la iluminación con la distancia
        //  atenuación= attenuation2*x^2 + attenuation0*x + attenuation0
        float           attenuation0;
        float           attenuation1;
        float           attenuation2;

        float           theta;              ///< Ángulo en grados del cono de iluminación.
        float           phi;                ///< Ángulo en grados del cono de penumbra.
        float           fallOff;            ///< Cómo varía de la zona de ilumación a menumbra (0 a x).
        float           range;              ///< Distancia máxima de alcance de la luz (para Point y Spot).
        fColorRGBA      ambient;            ///< Color ambiente(RGBA)
        fColorRGBA      diffuse;            ///< Color difuso(RGBA)
        fColorRGBA      specular;           ///< Color especular(RGBA)
    };

    ///////////////////////////////////////////////////////////////////////////////
    /// struct   MATERIAL Estructura que define las propiedades del material.
    ///
    /// rief   
    /// author  Antonio Lucas Moreno version 1.0 date 28/09/2005 9:26:27
    ///////////////////////////////////////////////////////////////////////////////
    struct Material
    {
        char            name[256];              ///< Nombre del material.
        fColorRGBA      ambient;                ///< Color ambiente(RGBA).
        fColorRGBA      diffuse;                ///< Color difuso(RGBAA).
        fColorRGBA      specular;               ///< Color especular(RGBA).
        fColorRGBA      emissive;               ///< Color emisivo(RGBA).
    };

En el método OnCreateWindow() crearemos una fuente de luz (ver funciones SetDirectionalLight(), SetPointLight() y SetSpotLight()) direccional. Esto es equivalente a una luz situada en el infinito (por ejemplo el Sol) cuyos rayos de luz inciden con la misma inclinación.


    ///////////////////////////////////////////////////////////////////////////////
    ///     OnCreateWindow: Función invocada al iniciarse el programa y DESPUÉS de crear la ventana.
    ///
    ///     @param  nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::OnCreateWindow( WindowProps windowProps )
    {
        if ( m_pVideoDriver )
        {
            m_pVideoDriver->Init( windowProps );
            m_pVideoDriver->SetDepthBufferSize( MIN_DEPTH_BUFFER, MAX_DEPTH_BUFFER );

    // ********************************* NEW **************************************
            // Luz direccional.
            memset( &m_Light, 0, sizeof(Light) );
            m_Light.id          = ID_LIGHT;
            m_Light.type        = eDirectionalLight;
            m_Light.direction   = Vector3( -1.0f, -0.5f, -0.2f);

            m_Light.ambient.r   = 0.2f;
            m_Light.ambient.g   = 0.2f;
            m_Light.ambient.b   = 0.2f;
            m_Light.ambient.a   = 1.0f;

            m_Light.diffuse.r   = 1.0f;
            m_Light.diffuse.g   = 1.0f;
            m_Light.diffuse.b   = 1.0f;
            m_Light.diffuse.a   = 1.0f;

            m_Light.specular.r  = 0.0f;
            m_Light.specular.g  = 0.0f;
            m_Light.specular.b  = 0.0f;
            m_Light.specular.a  = 1.0f;

            m_pVideoDriver->SetLight    ( m_Light );
            m_pVideoDriver->EnableLight ( m_Light.id, true );

    // ****************************************************************************
        }
    }

Cada fuente de luz creada se identifica con un valor, ID_LIGHT en este caso, la cual habrá que activar así como es uso de iluminación y el uso de materiales.

Posteriormente en la función OnRender(), antes de renderizar nuestro cubo, definiremos el color del material usado así como sus propiedades en cuánto a cómo refleja la luz dicho material.


    ///////////////////////////////////////////////////////////////////////////////
    ///     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.

    // ********************************* NEW **************************************
        fColorRGBA yellowColor  = { 1.0f, 1.0f, 0.0f, 0.0f };   // Amarillo
        Material yellowMaterial;

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

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

            // 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();

            if ( m_bWireframe )
            {
                m_pVideoDriver->SetRenderMode ( eWireFrame );   // Renderizado wireframe
                m_pVideoDriver->SetCullFaces  ( eCullNone  );   // Desactivamos la selección de caras a renderizar.
    // ********************************* NEW **************************************
                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.
    // ********************************* NEW **************************************
                m_pVideoDriver->EnableLighting( true       );   // Activamos la iluminación.
    // ****************************************************************************
            }

    // ********************************* NEW **************************************
            m_pVideoDriver->SetMaterial  ( yellowMaterial );           // Fijamos el material
    // ****************************************************************************

            RenderObjects();

            m_pVideoDriver->EndRender();
        }
    }

El método RenderObjects() varía ahora ligeramente, se ha añadido a la estructura Vertex las coordenadas que definen una normal.


    ///////////////////////////////////////////////////////////////////////////////
    /// struct  Vertex Estructura del vértice.
    ///
    /// rief   
    /// author  Antonio Lucas Moreno version 1.02 date 30/08/2005 9:24:19
    ///////////////////////////////////////////////////////////////////////////////
    struct Vertex
    {
        float           x , y , z ;         ///< Coordenadas del vértice.
        ulColorRGBA     color;              ///< Color del vértice.
    // ********************************* NEW **************************************
        float           nx, ny, nz;         ///< Normal del vértice.
    // ****************************************************************************
        float           tu, sv;             ///< Coordenadas de textura.
    };
	
	
    ///////////////////////////////////////////////////////////////////////////////
    ///     RenderObjects: Dibuja las geometrías.
    ///
    ///     @param  nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::RenderObjects()
    {
        Vertex vertexArray[4];

        if ( m_pVideoDriver )
        {
    // ********************************* NEW **************************************

            // Para todas las caras
            for (int f=0; f<6; f++)
            {
                // Renderizamos los vértices de la cara
                for (int i=0; i<4; i++)
                {
                    vertexArray[i].nx= m_Cube.faces[f].normal[0];
                    vertexArray[i].ny= m_Cube.faces[f].normal[1];
                    vertexArray[i].nz= m_Cube.faces[f].normal[2];

                    vertexArray[i].x= m_Cube.vertex[ m_Cube.faces[f].index[i] ][0];
                    vertexArray[i].y= m_Cube.vertex[ m_Cube.faces[f].index[i] ][1];
                    vertexArray[i].z= m_Cube.vertex[ m_Cube.faces[f].index[i] ][2];

                    vertexArray[i].color.rgba= 0xffffff;
                }

                if ( m_bWireframe )
                    m_pVideoDriver->RenderVertexArray( eTriangleStrip, (BYTE*)vertexArray,
                                                       sizeof(Vertex), 0, 4, eFVF_XYZ|eFVF_DIFFUSE );
                else
                    m_pVideoDriver->RenderVertexArray( eTriangleStrip, (BYTE*)vertexArray, 
                                                       sizeof(Vertex), 0, 4, eFVF_XYZ|eFVF_DIFFUSE|eFVF_NORMAL|eFVF_TEX1 );
            }

    // ****************************************************************************
        }
    }

Podéis descargaros el ejemplo: tutorial-10.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=11