SDL编程入门(7)纹理加载和渲染

纹理加载和渲染

SDL2 的一个主要新功能是纹理渲染 API。这为您提供了快速、灵活的基于硬件的渲染。在本教程中,我们将使用这种新的渲染技术。

//将单个图像作为纹理加载
SDL_Texture* loadTexture( std::string path );

//我们要渲染的窗口
SDL_Window* gWindow = NULL;

//窗口渲染器
SDL_Renderer* gRenderer = NULL;

//当前显示的纹理
SDL_Texture* gTexture = NULL;

SDL中的纹理有自己的数据类型,直观地称为SDL_Texture。当我们处理SDL纹理时,你需要一个SDL_Renderer来将它渲染到屏幕上,这就是为什么我们要声明一个名为 "gRenderer "的全局渲染器。

你也可以看到,我们有一个loadTexture的新图像加载例程和一个我们将要加载的全局声明的纹理。

        //创建窗口
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
            success = false;
        }
        else
        {
            //创建窗口的渲染器
            gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
            if( gRenderer == NULL )
            {
                printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
                success = false;
            }
            else
            {
                //初始化渲染器颜色
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

                //初始化PNG加载
                int imgFlags = IMG_INIT_PNG;
                if( !( IMG_Init( imgFlags ) & imgFlags ) )
                {
                    printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
                    success = false;
                }
            }
        }

在我们创建了窗口之后,我们必须为我们的窗口创建一个渲染器,这样我们就可以在上面渲染纹理,幸运的是这很容易通过调用SDL_CreateRenderer来完成。幸运的是,只要调用SDL_CreateRenderer就可以轻松完成。

创建渲染器后,我们要使用SDL_SetRenderDrawColor来初始化渲染颜色。这可以控制各种渲染操作使用什么颜色。

SDL_Texture* loadTexture( std::string path ){
    //最终的纹理
    SDL_Texture* newTexture = NULL;

    //在指定路径加载图像
    SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
    if( loadedSurface == NULL )
    {
        printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
    }
    else
    {
        //用表面像素创建纹理
        newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
        if( newTexture == NULL )
        {
            printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
        }

        //Get rid of old loaded surface
        SDL_FreeSurface( loadedSurface );
    }

    return newTexture;
}

我们的纹理加载函数看起来和之前基本相同,只是现在我们没有将加载的表面转换为显示格式,而是使用SDL_CreateTextureFromSurface从加载的表面中创建一个纹理。和之前一样,这个函数从一个现有的表面创建一个新的纹理,这意味着和之前一样,我们必须释放加载的表面,然后返回加载的纹理。

bool loadMedia(){
    //Loading success flag
    bool success = true;

    //Load PNG texture
    gTexture = loadTexture( "07_texture_loading_and_rendering/texture.png" );
    if( gTexture == NULL )
    {
        printf( "Failed to load texture image!\n" );
        success = false;
    }

    return success;
}

void close(){
    //Free loaded image
    SDL_DestroyTexture( gTexture );
    gTexture = NULL;

    //Destroy window
    SDL_DestroyRenderer( gRenderer );
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;
    gRenderer = NULL;

    //Quit SDL subsystems
    IMG_Quit();
    SDL_Quit();
}

由于纹理加载与我们的图像加载函数一起被抽象化了,所以loadMedia()函数的工作原理和之前差不多。

在我们的清理函数中,我们必须记住使用SDL_DestroyTexture来deallocate我们的纹理。

             //While application is running
            while( !quit )
            {
                //Handle events on queue
                while( SDL_PollEvent( &e ) != 0 )
                {
                    //User requests quit
                    if( e.type == SDL_QUIT )
                    {
                        quit = true;
                    }
                }

                //清空屏幕
                SDL_RenderClear( gRenderer );

                //将纹理渲染到屏幕上
                SDL_RenderCopy( gRenderer, gTexture, NULL, NULL );

                //更新屏幕
                SDL_RenderPresent( gRenderer );
            }

在事件循环之后的主循环中,我们调用SDL_RenderClear。这个函数用上次SDL_SetRenderDrawColor设置的颜色填充屏幕。

清空屏幕后,我们用SDL_RenderCopy渲染纹理。纹理渲染完成后,我们仍然需要更新屏幕,但由于我们没有使用SDL_Surfaces来渲染,所以不能使用SDL_UpdateWindowSurface。相反,我们必须使用SDL_RenderPresent

这里下载本教程的媒体和源代码。

原文链接

关注我的公众号:编程之路从0到1
编程之路从0到1

## 代码 ```C #include<stdbool.h> #include<SDL2/SDL.h> const int FPS=60; const char*title="Test"; int width=640,height=480; bool isRun; SDL_Window*win=NULL; SDL_Renderer*ren=NULL; bool initSDL(){ if(SDL_Init(SDL_INIT_VIDEO)!=0){ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"SDL2 init error:%s",SDL_GetError()); return false; } return true; } void quit(){ if(ren!=NULL){ SDL_DestroyRenderer(ren); } if(win!=NULL){ SDL_DestroyWindow(win); } SDL_Quit(); } bool createSDL(){ win=SDL_CreateWindow(title,SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,width,height,SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE); if(win==NULL){ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"SDL2 create window error:%s",SDL_GetError()); return false; } ren=SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC); if(ren==NULL){ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"SDL2 create window error:%s",SDL_GetError()); return false; } return true; } void render(){ /*背景颜色*/ SDL_SetRenderDrawColor(ren,18,18,18,0xFF); SDL_RenderClear(ren); /*画正方形*/ SDL_SetRenderDrawColor(ren,0xFF,0,0,0xFF); SDL_Rect rt={(width-100)/2,(height-100)/2,100,100};/*奇怪,我宽和高都是100,怎么在改变窗口大小时宽高也会改变?*/ SDL_RenderFillRect(ren,&rt); SDL_RenderPresent(ren); } /*绘制线程*/ int drawing(void*data){ unsigned long long start; int waitTime; while(isRun){ /*控制帧率*/ start=SDL_GetPerformanceCounter(); render(); waitTime=1000/FPS-(SDL_GetPerformanceCounter()-start)*1000/SDL_GetPerformanceFrequency(); if(waitTime<0 || waitTime>=500){ waitTime=1000/FPS; } SDL_Delay(waitTime); } return 0; } void handleEvent(){ SDL_Event event; SDL_WaitEvent(&event); if(true){ switch(event.type){ case SDL_QUIT: isRun=false; break; case SDL_WINDOWEVENT: switch(event.window.event){ case SDL_WINDOWEVENT_SIZE_CHANGED: SDL_GetWindowSize(win,&width,&height); SDL_Log("size change to %d,%d\n",width,height); break; case SDL_WINDOWEVENT_RESIZED: width=event.window.data1; height=event.window.data2; SDL_Log("resize to %d,%d\n",width,height); break; default: break; } default: break; } } } int main(int argc,char**argv){ if(!initSDL()){ return 1; } isRun=createSDL(); SDL_Thread*th=SDL_CreateThread(drawing,"drawing",NULL); while(isRun){ handleEvent(); } SDL_WaitThread(th,NULL); quit(); return 0; } ``` ## 运行结果 ![图片说明](https://img-ask.csdn.net/upload/202004/13/1586737460_687820.gif) 为什么会这样?如果我要固定正方形的大小该怎么做?
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页