Tutorial de SDL – Parte 43 – Renderização para texturas

Continuando nossa série de artigos traduzidos do site lazyfoo, veremos agora como renderizar uma cena para uma textura.
Para alguns efeitos, ser capaz de renderizar uma cena para uma textura se faz necessário. Aqui estaremos renderizando uma cena para uma textura para conseguir um efeito de cena giratória.

//Texture wrapper class
class LTexture
{
    public:
        //Initializes variables
        LTexture();
        //Deallocates memory
        ~LTexture();
        //Loads image at specified path
        bool loadFromFile( std::string path );
        #ifdef _SDL_TTF_H
        //Creates image from font string
        bool loadFromRenderedText( std::string textureText, SDL_Color textColor );
        #endif
        //Creates blank texture
        bool createBlank( int width, int height, SDL_TextureAccess = SDL_TEXTUREACCESS_STREAMING );
        //Deallocates texture
        void free();
        //Set color modulation
        void setColor( Uint8 red, Uint8 green, Uint8 blue );
        //Set blending
        void setBlendMode( SDL_BlendMode blending );
        //Set alpha modulation
        void setAlpha( Uint8 alpha );
        //Renders texture at given point
        void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE );
        //Set self as render target
        void setAsRenderTarget();
        //Gets image dimensions
        int getWidth();
        int getHeight();
        //Pixel manipulators
        bool lockTexture();
        bool unlockTexture();
        void* getPixels();
        void copyPixels( void* pixels );
        int getPitch();
        Uint32 getPixel32( unsigned int x, unsigned int y );
    private:
        //The actual hardware texture
        SDL_Texture* mTexture;
        void* mPixels;
        int mPitch;
        //Image dimensions
        int mWidth;
        int mHeight;
};

Aqui adicionamos mais funcionalidade para a classe da textura. A função createBlank agora precisa de outro argumento que define como ela é acessada. Nós também temos uma função setAsRenderTarget que possibilita que possamos renderizar para essa textura.

bool LTexture::createBlank( int width, int height, SDL_TextureAccess access )
{
    //Create uninitialized texture
    mTexture = SDL_CreateTexture( gRenderer, SDL_PIXELFORMAT_RGBA8888, access, width, height );
    if( mTexture == NULL )
    {
        printf( "Unable to create blank texture! SDL Error: %s\n", SDL_GetError() );
    }
    else
    {
        mWidth = width;
        mHeight = height;
    }
    return mTexture != NULL;
}

Quando quisermos renderizar para uma textura precisamos configurar o acesso à essa textura como SDL_TEXTUREACCESS_TARGET, que é o motivo pelo qual essa função precisa de mais um argumento agora.

void LTexture::setAsRenderTarget()
{
    //Make self render target
    SDL_SetRenderTarget( gRenderer, mTexture );
}

Para renderizar para um textura, temos que configurar o alvo da renderização, que é feito usando a função SDL_SetRenderTarget.

bool loadMedia()
{
    //Loading success flag
    bool success = true;
    //Load texture target
    if( !gTargetTexture.createBlank( SCREEN_WIDTH, SCREEN_HEIGHT, SDL_TEXTUREACCESS_TARGET ) )
    {
        printf( "Failed to create target texture!\n" );
        success = false;
    }
    return success;
}

Criamos nossa textura na função de carregamento de mídia.

            //Main loop flag
            bool quit = false;
            //Event handler
            SDL_Event e;
            //Rotation variables
            double angle = 0;
            SDL_Point screenCenter = { SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 };

Nesse exemplo, iremos renderizar uma forma geométrica para a textura e girar essa textura no centro da tela. Por isso temos variáveis para ângulo de rotação e centro da tela.

            //While application is running
            while( quit == false )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }
                //rotate
                angle += 2;
                if( angle > 360 )
                {
                    angle -= 360;
                }
                //Set self as render target
                gTargetTexture.setAsRenderTarget();
                //Clear screen
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
                SDL_RenderClear( gRenderer );
                //Render red filled quad
                SDL_Rect fillRect = { SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 };
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0x00, 0x00, 0xFF );
                SDL_RenderFillRect( gRenderer, &fillRect );
                //Render green outlined quad
                SDL_Rect outlineRect = { SCREEN_WIDTH / 6, SCREEN_HEIGHT / 6, SCREEN_WIDTH * 2 / 3, SCREEN_HEIGHT * 2 / 3 };
                SDL_SetRenderDrawColor( gRenderer, 0x00, 0xFF, 0x00, 0xFF );
                SDL_RenderDrawRect( gRenderer, &outlineRect );
                //Draw blue horizontal line
                SDL_SetRenderDrawColor( gRenderer, 0x00, 0x00, 0xFF, 0xFF );
                SDL_RenderDrawLine( gRenderer, 0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT / 2 );
                //Draw vertical line of yellow dots
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0x00, 0xFF );
                for( int i = 0; i < SCREEN_HEIGHT; i += 4 )
                {
                    SDL_RenderDrawPoint( gRenderer, SCREEN_WIDTH / 2, i );
                }
                //Reset render target
                SDL_SetRenderTarget( gRenderer, NULL );
                //Show rendered to texture
                gTargetTexture.render( 0, 0, NULL, angle, &screenCenter );
                //Update screen
                SDL_RenderPresent( gRenderer );
            }

Em nosso loop principal, antes de fazermos quaisquer renderização, configuramos a textura alvo como target. Em seguida renderizamos nossa cena geométrica e quando tivermos terminado para a textura chamamos SDL_SetRenderTarget com uma textura NULL de forma que qualquer renderização posterior seja feita na tela. Com nossa cena renderizada para a textura, agora renderizamos a textura alvo na tela de acordo com o ângulo de rotação.

Baixe os arquivo de mídia e de código fonte desse artigo aqui.