Tutorial 20 - Mapeado Esférico.
Fecha Martes, 29 abril a las 11:42:08
Tema DirectX/Opengl


Vamos a ver cómo mapear una textura sobre una esfera. Nos vendrá bien para renderizar planetas, por ejemplo.



En el método Render() de la clase Sphere, se ha añadido las coordenadas de textura con el siguiente planteamiento:

Tenemos que mapear la textura sobre el total de la circunferancia. Es decir, si la longitud de la circunferencia es 2*PI*r tenemos que asociarla al rango 0-1. Nos da igual el radio de la circunferencia, así que simplificaremos para r=1.

Horizontalmente, tenemos que cada división es xzAngle y xyAngle en vertical. Así cada coordenada de textura será igual a la porción multiplicada por el elemento i o j usado en ese momento. Para reducirlo a un rango de 0 a 1 habrá que dividir por el total, 2*PI.

En vertical, en lugar de ser 2*PI (la longitud total de la circunferencia), sólo será la mitad PI, ya que la textura recorre de un polo a otro.

Como renderiamos la esfera (recorremos sus vértices) de Este a Oeste y el rango de la textura va de 0 a 1 de Oeste a Este (al revés) por eso hacemos 1-coordenada_textura, para invertir dicho orden.

Si finalmente sustituimos xzAngle y xyAngle por sus valores 2*PI/slices y 2*PI/stacks, las fórmulas se nos simplifican. Sólo hay que reseñar, que si el sistema de ejes es de mano izquierda, la coordenada su sigue el mismo orden que los vértices.

Con todo esto, la función que renderiza la esfera queda:


    ///////////////////////////////////////////////////////////////////////////////
    ///     Render: Renderiza la esfera.
    ///
    ///     @param  int slices: Nº de divisiones verticales (meridianos).
    ///     @param  int stacks: Nº de divisiones transversales (paralelos).
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void Sphere::Render( int slices, int stacks )
    {
        float x0, y0, z0;
        float x1, y1, z1;
        float sinxy;
        int i, j, v= 0;

        unsigned int numVertex  = 2*slices*stacks;
        float        xzAngle    = 2.0f*PI/(float)slices;
        float        xyAngle    = 2.0f*PI/(float)stacks;

        if ( numVertex <= MAX_VERTEX_SPHERE )
        {
            for (i=0; i// Meridianos
            {
                for (j=0; j// Paralelos
                {
                    sinxy= sin(j*xyAngle);  // Para no calcularlo 4 veces

                    x0= sinxy * cos(i*xzAngle);
                    y0= cos(j*xyAngle);
                    z0= sinxy * sin(i*xzAngle);

                    // Vértice
                    m_Vertex[v].x = m_Radius*x0;
                    m_Vertex[v].y = m_Radius*y0;
                    m_Vertex[v].z = m_Radius*z0;

                    // Normal
                    m_Vertex[v].nx= x0;
                    m_Vertex[v].ny= y0;
                    m_Vertex[v].nz= z0;

    // ********************************* NEW **************************************
                    if ( IVideoDriver::GetVideoDriver()->GetAxisSystem() == eRightHanded )
                        m_Vertex[v].su= 1.0f-i/(float)slices;               // SU= 1-i*xzAngle/2PI
                    else
                        m_Vertex[v].su= i/(float)slices;                    // SU= i*xzAngle/2PI

                    m_Vertex[v].tv= ((float)stacks-2.0f*j)/(float)stacks;   // TV= 1-j*xyAngle/PI
    // ****************************************************************************

                    v++;

                    x1= sinxy * cos((i+1)*xzAngle);
                    y1= y0;
                    z1= sinxy * sin((i+1)*xzAngle);

                    m_Vertex[v].x = m_Radius*x1;
                    m_Vertex[v].y = m_Radius*y1;
                    m_Vertex[v].z = m_Radius*z1;
                    m_Vertex[v].nx= x1;
                    m_Vertex[v].ny= y1;
                    m_Vertex[v].nz= z1;
    // ********************************* NEW **************************************
                    if ( IVideoDriver::GetVideoDriver()->GetAxisSystem() == eRightHanded )
                        m_Vertex[v].su= 1.0f-(i+1)/(float)slices;
                    else
                        m_Vertex[v].su= (i+1)/(float)slices;

                    m_Vertex[v].tv= m_Vertex[v-1].tv;   // Igual que el anterior.
    // ****************************************************************************

                    v++;
                }
            }

            IVideoDriver::GetVideoDriver()->RenderVertexArray( eTriangleStrip, (BYTE*)m_Vertex, sizeof(Vertex),
                                                               0, numVertex, eFVF_XYZ|eFVF_NORMAL|eFVF_TEX1 );
        }
    }

Si queréis renderizar más planetas, aquí podéis encontrar las texturas para los planetas del Sistema Solar:
http://planetpixelemporium.com/planets.html

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