Aprende a desarrollar aplicaciones gráficas 2D y 3D paso a paso.
Programacion Grafica: Desarrollo 2D/3D con C++ y DirectX/OpenGL/GLUT/SDL - Windows/Linux


  Crear una cuenta
 • Portada • Descargas • Tu Cuenta • 
Menú
 Portada
 Noticias
 Archivo de Noticias
 Temas
 Descargas
 Inicio
 Ver Nuevas
 Más Popular
 Más Votadas
 Contenido
 Contenido
 Secciones
 Enciclopedia
 Revisiones
 FAQ
 Tus Opciones
 Tu Cuenta
 Mensajes Privados
 Recomiéndenos
 Buscar
 Diarios
 Contacto
 Enlaces Web
 Inicio
 Enviar Enlace
 Ver Nuevos
 Más Popular
 Más Valorados
 Estadísticas
 Inicio
 Detalladas
 Top 10
 
Estadísticas
 
Tutorial 21 - Alpha Blending 
DirectX/Opengl

Vamos con un nuevo tutorial en el que veremos cómo utilizar la técnica de "alpha blending" para producir un efecto de destello mediante una textura que usaremos como disparo. También renderizaremos elementos en primer plano usando esta técnica (en el punto de mira) y la técnica de "color key" que ya vimos en tutoriales anteriores para renderizar la cabina.



Bien, la técnica de "alpha blending" consiste en algo así como mezclar colores. En el fondo, lo que tendremos será una función para "mezclar colores" y si concretamente mezclamos el canal alpha con los colores que ya han sido renderizados previamente, pues lo que tendrémos será una mezcla con el canal alpha.

En el Tutorial 08 ya definimos nuestra función de mezclado:


    ///////////////////////////////////////////////////////////////////////////////
    ///     SetBlending: Fija los valores de mezclado de los canales RGBA.
    ///
    ///     @param  e_BLEND_FACTOR sourceBlendFactor: valor con el que se mezcla.
    ///     @param  e_BLEND_FACTOR destinationBlendFactor: valor de mezclado del color
    ///                 que ya se encuentra en el buffer.
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void SetBlending( e_BLEND_FACTOR sourceBlendFactor, e_BLEND_FACTOR destinationBlendFactor= eBlendDisable );

en el que se expresa el color origen a mezclar y el valor de mezclado que ya se ha enviado a renderizar (en el framebuffer).

En el fichero 3dstructures.h podréis ver los valores que pueden especificarse:


    ///////////////////////////////////////////////////////////////////////////////
    /// enum   e_BLEND_FACTOR Valores de mezclado.
    ///////////////////////////////////////////////////////////////////////////////
    enum e_BLEND_FACTOR
    {
        eBlendZero,             ///< Factor blend: (0, 0, 0, 0)
        eBlendOne,              ///< Factor blend: (1, 1, 1, 1)

        // DESTINATION
        eBlendDestColor,        ///< Factor blend: (Rd, Gd, Bd, Ad)
        eBlendOneMinusDestColor,///< Factor blend: (1-R, 1-G, 1-B, 1-A)
        eBlendInvDestColor,     ///< Factor blend: (1-Rd, 1-Gd, 1-Bd, 1-Ad)

        eBlendDestAlpha,        ///< Factor blend: (Ad, Ad, Ad, Ad)
        eBlendOneMinusDestAlpha,///< Factor blend: (1-Ad, 1-Ad, 1-Ad, 1-Ad).

        // SOURCE
        eBlendSrcColor,         ///< Factor blend: (Rs, Gs, Bs, As)
        eBlendOneMinusSrcColor, ///< Factor blend: (1-Rs, 1-Gs, 1-Bs, 1-As)

        eBlendSrcAlpha,         ///< Factor blend: (As, As, As, As)
        eBlendOneMinusSrcAlpha, ///< Factor blend: (1-As, 1-As, 1-As, 1-As)
        eBlendSrcAlphaSat,      ///< Factor blend: (f, f, f, 1); f = min(As, 1-Ad)

        eBlendDisable           ///< Desactiva el blending.
    };

Así como una nueva definición de vértice que vamos a utilizar para renderizar los destellos y los elementos de la cabina:


    ///////////////////////////////////////////////////////////////////////////////
    /// struct   VertexBlend: Vértice para mezclas.
    ///
    /// rief
    /// author  Antonio Lucas Moreno version 1.00 date 06/05/2008 13:48:10
    ///////////////////////////////////////////////////////////////////////////////
    struct VertexBlend
    {
        float           x, y, z;            ///< Coordenadas del vértice.
        ulColorRGBA     color;              ///< Color del vértice.
        float           su, tv;             ///< Coordenadas de textura.
    };

He creado una nueva clase "Particle" que gestiona el movimiento, el ciclo de vida de la partícula y su renderizado.

La función de renderizado fija la matriz de transformación para actualizar su posición. Posteriormente seleccionamos el canal alpha de la textura y "mezclamos" con lo que ya se envió a renderizar en el "framebuffer". La partícula se renderiza como un cuadrado (y par de triángulos) con la textura mapeada sobre ella:


    ///////////////////////////////////////////////////////////////////////////////
    ///     Render: Renderiza la partícula.
    ///
    ///     @param   nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void Particle::Render()
    {
        Matrix transform, worldTransform;

        IVideoDriver *pVideoDriver= IVideoDriver::GetVideoDriver();

        if ( pVideoDriver )
        {
            // Transformamos del espacio local al espacio global.
            transform.Translate( m_vPosition );
            worldTransform= GetLocalMatrix() * transform;

            pVideoDriver->SetTransform( worldTransform );

            pVideoDriver->SetBlending ( eBlendDestAlpha, eBlendOne );
            pVideoDriver->SetTexture  ( m_Texture );

            pVideoDriver->RenderVertexArray( eTriangleStrip, (BYTE*)m_Vertex,
                                             sizeof(VertexBlend), 0, 4,
                                             eFVF_XYZ|eFVF_DIFFUSE|eFVF_TEX1 );
        }
    }

Cada vez que pulsemos el botón izquierdo del ratón, crearemos una nueva partícula desde la posición en la que se encuentra nuestro punto de vista (el de la cámara) y fijamos su velocidad y el tiempo de vida hasta desintegrarse:


    ///////////////////////////////////////////////////////////////////////////////
    ///     OnLButtonDown: Función invocada cuando se pulsa el botón izquierdo ratón.
    ///
    ///     @param  int mouseX: Coordenada X de pantalla.
    ///     @param  int mouseY: Coordenada Y de pantalla.
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::OnLButtonDown( int mouseX, int mouseY )
    {
    // ********************************* NEW **************************************

        char name[32];
        Particle *pParticle= NULL;

        Vector3 vPosition= m_Player.GetPosition() + m_Camera.GetPosition();
        Vector3 vUp      = m_Camera.GetLocalMatrix().GetUpVector();
        Vector3 vLook    = vPosition - m_Camera.GetLocalMatrix().GetForwardVector();

        sprintf( name, "PARTICLE-%d", m_IdParticle++ );

        pParticle= new Particle(name);

        if ( pParticle )
        {
            pParticle->SetPosition      ( vPosition  );
            pParticle->SetOrientation   ( vLook, vUp );
            pParticle->SetTexture       ( m_ParticleTexture );
            pParticle->SetSpeed         ( 0.02f );
            pParticle->SetTimeLife      ( 2000 );

            m_ObjectList[ string(name) ]= pParticle;
        }

        m_msShootEffect= 80;

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

Posteriormente, en el método RenderSceneObjects(), desactivamos la iluminación, activamos el uso de texturas y las mezclas con el "framebuffer".


    // ********************************* NEW **************************************
    else if ( strstr(pObject->GetName(),"PARTICLE") )
    {
        // Renderizamos las partículas con Alpha Blending
        m_pVideoDriver->EnableLighting( false );
        m_pVideoDriver->EnableTexture ( true  );
        m_pVideoDriver->EnableBlending( true  );

        pParticle= dynamic_cast( pObject );
        if ( pParticle )
        {
            if ( pParticle->GetTimeLife() < 0 )
            {
                // Está muerta, la eliminamos
                delete pParticle;
                m_ObjectList.erase( itObj++ );
            }
            else
            {
                pParticle->Render();
                ++itObj;
            }
        }
        m_pVideoDriver->EnableBlending( false );
    }
    // ****************************************************************************

Fijaros bien en que al eliminar la partícula de la lista de objetos: m_ObjectList.erase( itObj++ ), se realiza un incremento del iterador para que apunte al siguiente elemento. Cuando se recorre la lista m_ObjectList en el "for", no se incrementa el iterador, ya que puede que se elimine un elemento como ocurre en el caso anterior. Se incrementa sólo cuando corresponda.

Finalmente, tras renderizar los elementos "3D", renderizamos los del primer plano. Fijémonos en el método OnRender():


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

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

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

            Vector3 vPosition= m_Player.GetPosition() + m_Camera.GetPosition();


            Vector3 vUp      = m_Camera.GetLocalMatrix().GetUpVector();
            Vector3 vLook    = vPosition - m_Camera.GetLocalMatrix().GetForwardVector();

            m_pVideoDriver->LookAt( vPosition, vLook, vUp );

            m_pVideoDriver->BeginRender();

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

            m_pVideoDriver->EnableLighting( false );            // Desactivamos la iluminación.
            m_pVideoDriver->EnableTexture ( false );            // Desactivamos el uso de texturas.
            RenderFloor ( 20, 20 );                             // Renderizamos el suelo.

            if ( m_bWireframe )
            {
                m_pVideoDriver->SetRenderMode ( eWireFrame );   // Renderizado wireframe
                m_pVideoDriver->SetCullFaces  ( eCullNone  );   // Desactivamos la selección de caras a renderizar.
            }
            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.
            RenderSceneObjects();                               // Renderizamos las geometrías.
            m_pVideoDriver->EnableLighting( false );            // Desactivamos la iluminación.

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

            // Reseteamos las transformaciones previas (si no, en DirectX no va bien)
            m_pVideoDriver->SetTransform( mIdentity );

            // Reseteamos la cámara (si no, en DirectX no va bien)
            m_pVideoDriver->LookAt( Vector3( 0.0f, 0.0f, 0.1f ) ,   // Posición de la cámara.
                                    Vector3( 0.0f, 0.0f, 0.0f ) ,   // A dónde mira.
                                    Vector3( 0.0f, 1.0f, 0.0f ) );  // ¿Qué es arriba? (el eje Y).

            // Fijamos la perspectiva ortogonal
            m_pVideoDriver->SetOrthoProjection( -m_WindowProps.width /2, m_WindowProps.width /2,
                                                -m_WindowProps.height/2, m_WindowProps.height/2,
                                                -1.0f, 1.0f );
            // Renderizamos el punto de mira
            RenderCrosshair();

            // Renderizamos la cabina
            RenderCockpit();

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

            m_pVideoDriver->EndRender();
        }
    }

Vemos cómo el truco consiste en cambiar de la proyección 3D a una proyección ortogonal en 2D. Tras lo cual renderizamos el punto de mira y la cabina.

A continuación vemos que las renderizamos como simples elementos 2D, un cuadrado/rectánculo (un par de triángulos) con una textura mapeada en ellos. En el caso del punto de mira lo renderizamos mediante "alpha blending" y la cabina con "color key" haciendo que todo lo que sea color fuxia no se mezcle con lo que ya se encuentra en el "framebuffer".


    ///////////////////////////////////////////////////////////////////////////////
    ///     RenderCrosshair: Renderiza el punto de mira.
    ///
    ///     @param  nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::RenderCrosshair()
    {
        // Definimos un rectángulo (formado por dos triángulos consecutivos)
        //
        // v0   v2
        //  *---*
        //  |  /|
        //  |/  |
        //  *---*
        // v1   v3
        //
        static VertexBlend crosshairVertex[4] =
        {
        //      x  ,   y  ,  z  ,    color  , su,tv
            {-50.0f, 50.0f, 0.5f, 0xffffffff,  0,1 },   /* v0 */
            {-50.0f,-50.0f, 0.5f, 0xffffffff,  0,0 },   /* v1 */
            { 50.0f, 50.0f, 0.5f, 0xffffffff,  1,1 },   /* v2 */
            { 50.0f,-50.0f, 0.5f, 0xffffffff,  1,0 }    /* v3 */
        };

        if ( m_pVideoDriver )
        {
            m_pVideoDriver->EnableTexture ( true );
            m_pVideoDriver->EnableBlending( true );

            // Alpha blending
            m_pVideoDriver->SetBlending ( /*fuente*/eBlendDestAlpha, /*destino*/eBlendOne );
            m_pVideoDriver->SetTexture  ( m_CrosshairTexture );

            m_pVideoDriver->RenderVertexArray( eTriangleStrip, (BYTE*)crosshairVertex,
                                               sizeof(VertexBlend), 0, 4,
                                               eFVF_XYZ|eFVF_DIFFUSE|eFVF_TEX1 );

            m_pVideoDriver->EnableBlending( false );
        }
    }

Fijáos en el truco de usar dos texturas en la cabina para dar la sensación de que ésta se ilumna cuando disparamos. Para ello he definido la variable m_msShootEffect que indica durante cuánto tiempo debe mostrarse la cabina iluminada.


    ///////////////////////////////////////////////////////////////////////////////
    ///     RenderCockpit: Renderiza la cabina.
    ///
    ///     @param  nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::RenderCockpit()
    {
        // Definimos un rectángulo (formado por dos triángulos consecutivos)
        //
        // v0   v2
        //  *---*
        //  |  /|
        //  |/  |
        //  *---*
        // v1   v3
        //
        static VertexBlend cockpitVertex[4] =
        {
        //      x   ,   y   ,  z  ,    color  , su,tv
            {-320.0f, 240.0f, 0.5f, 0xffffffff,  0,1 }, /* v0 */
            {-320.0f,-240.0f, 0.5f, 0xffffffff,  0,0 }, /* v1 */
            { 320.0f, 240.0f, 0.5f, 0xffffffff,  1,1 }, /* v2 */
            { 320.0f,-240.0f, 0.5f, 0xffffffff,  1,0 }  /* v3 */
        };

        if ( m_pVideoDriver )
        {
            m_pVideoDriver->EnableTexture ( true );
            m_pVideoDriver->EnableBlending( true );

            // Blend color-key
            m_pVideoDriver->SetBlending( /*fuente*/eBlendSrcAlpha, /*destino*/eBlendOneMinusSrcAlpha );

            if ( m_msShootEffect > 0 )
                m_pVideoDriver->SetTexture( m_CockpitTexture02 );
            else
                m_pVideoDriver->SetTexture( m_CockpitTexture01 );

            m_pVideoDriver->RenderVertexArray( eTriangleStrip, (BYTE*)cockpitVertex,
                                               sizeof(VertexBlend), 0, 4,
                                               eFVF_XYZ|eFVF_DIFFUSE|eFVF_TEX1 );

            m_pVideoDriver->EnableBlending( false );
        }
    }

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



Nota: Compartir en Facebook
Enviado el Viernes, 04 julio a las 18:24:01 por administrador
 
Enlaces Relacionados
· Más Acerca de DirectX/Opengl
· Noticias de administrador


Noticia más leída sobre DirectX/Opengl:
Tutorial 16 - Movimiento-3D - (Tercera Persona)

 
Votos del Artículo
Puntuación Promedio: 4.66
votos: 3


Por favor tómate un segundo y vota por este artículo:
Excelente
Muy Bueno
Bueno
Regular
Malo

 
Opciones

 Versión Imprimible  Versión Imprimible

 
"Tutorial 21 - Alpha Blending" | Entrar / Crear Cuenta | 0 Comentarios
Los comentarios son propiedad de quien los envió. No somos responsables por su contenido.

No se permiten comentarios Anónimos, Regístrese por favor

Sitio potenciado por NukeProjects.Net        NukeStilo 1.0 se basa en PHP-Nuke        Theme Diseñado por NukeMods

© Antonio Lucas Moreno