音视频开发---深入理解sdl渲染、纹理

 

1. 概念

渲染在电脑绘图中是指用软件从模型生成图像的过程。模型是用严格定义的语言或者数据结构对于三维物体的描述,它包括几何、视点、纹理以及照明信息。

早期计算机生成的三维图像看起来往往像是发亮的塑料,虽然这在当时也是比较先进的,但是它们缺乏各种纹路——如磨损、裂痕、指纹和污渍等,而这些纹路会增加三维物体的真实感。近年来,纹理已经在开发人员中得到普及并作为增强计算机生成的三维图像的真实感的工具。

词语“纹理”在日常使用中表示物体的光滑度或粗糙度,但是在计算机图形学中,纹理指的是一张表示物体表面细节的位图。

因为Direct3D中所有纹理都是位图,所以可以把任何位图贴到Direct3D图元的表面。例如,应用程序可以创建物体并使它们的表面看起来有木纹的样式。可以把草、泥土和岩石等纹理贴在构成山的图元的表面,这样就能得到看起来很真实的山坡。应用程序也可以用纹理创建其它的效果,如:路边的路标,悬崖边的岩层,或是地面上的大理石

    上面的概念摘自互联网, 其中渲染还是容易理解的,我们可以理解成绘画,而纹理作为后来的产物,还是很模糊的,尤其是和编程结合起来,上面的解释就显得难以理解。

 

2. 案例理解: 小A同学画一幅画

    下面,我们以生活中的一个例子‘小A同学画一幅画’  来解释SDL渲染,纹理概念。

    计算机使用SDL渲染显示一幅图就相当于小A同学在墙上画一幅画, 我们先将整个过程中的角色划分为纹理, 渲染器, gpu(画家), cpu(小A同学)

依据SDL编程方式,这里又分为两种情况:

1. 不使用纹理

     即由cpu直接绘制一幅画(cpu需要将最原始的rgb/YUV数据,刷到屏幕上), 相当于学生小A直接在墙上画画

2. 使用纹理

     相当于小A同学(cpu)指挥画家(gpu)在纸上画, 然后把纸贴在墙上。 这个过程中画是由画家(gpu)画的, 小A同学负责发号施令(即告诉画家画什么),  纸代表纹理, 画家代表gpu, 所有绘制的操作都是在纹理上进行。事实上,纹理的概念并不仅仅是一张纸, 还包括小A同学中对这幅画的构思,可以理解成画画的算法, 而纸相当于是一个载体(内存空间,用于保存这些构思)。 gpu根据纹理就可以计算出这幅图每个像素点的颜色( 相当于画家根据小A同学的描述,画出一幅画一样)

可以看出,使用纹理,可以减轻cpu的负担, cpu处于一个发号施令的角色,图片的计算过程交给效率更好的gpu来做,可以提高渲染的效率。

 

3. 编程

下面通过SDL编程绘制一幅图来加深理解

渲染到纹理,一个完整的例子, 本例子可在屏幕中显示一个移动的矩形,碰着屏幕边框后反弹


#include <stdio.h>
#include <string.h>

#include <SDL2/SDL.h>

#define PLAYER_REFRESH_EVENT (SDL_USEREVENT)
#define PLAYER_QUIT_EVENT (SDL_USEREVENT + 1)
#define DEFAULT_WINDOW_WIDTH 1024
#define DEFAULT_WINDOW_HEIGHT 768
static SDL_Window *screen = NULL;
static SDL_Renderer * renderer = NULL;
static SDL_Texture * texture = NULL;

static int sdl_window_init(int width, int height)
{
    if(width == 0)
        width = DEFAULT_WINDOW_WIDTH;
    if(height == 0)
        height = DEFAULT_WINDOW_HEIGHT;
    screen = SDL_CreateWindow("player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        width, height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

    renderer = SDL_CreateRenderer(screen, -1, 0);

    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
        width, height);
    return 0;
}

static void sdl_window_destroy()
{
    if(renderer )
        SDL_DestroyRenderer(renderer);
    if( screen )
        SDL_DestroyWindow(screen);
    if(texture)
        SDL_DestroyTexture(texture);
}

static void display_advertisement(void * arg)
{
    static int sx = 0,sy = 0;
    
    SDL_Rect rect;
    int rect_width = 50, rect_height = 40;
    static int xx = 0,yy = 0;
    if(renderer == NULL)
        return;
    if( xx == 0 || yy == 0)
    {
        xx = rand()%300 + 100;
        yy = rand()%200 + 100;
    }else{

        if(sx == 0){
            xx += rand()%2;
            if( (xx + rect_width) > 1024){
                sx = 1;
                xx = 1024 - rect_width;
            }
        }else{
            xx -= rand()%2;
            if( xx < 0){
                xx = 1;
                sx = 0;
            }
        }
        if(sy == 0){
            yy += rand()%2;
            if( (yy + rect_height) > 768){
                sy = 1;
                yy = 768 - rect_height;
            }
        }else{
            yy -= rand()%2;
            if( yy < 0){
                yy = 1;
                sy = 0;
            }
        }   
    
    }
    
    rect.x = xx;
    rect.y = yy;
    rect.w = 50;
    rect.h = 40;

#if 0
    SDL_SetRenderDrawColor(renderer,255,255,255,0);
    SDL_RenderClear(renderer);//采用上面设置的画笔颜色来刷屏

    // 2. 将渲染目标设置为纹理testure, 之后所有的操作都是针对该纹理的.
    SDL_RenderDrawRect(renderer, &rect);
    SDL_SetRenderDrawColor(renderer,0,0,255,0);
    SDL_RenderFillRect(renderer, &rect);
    SDL_RenderPresent(renderer);

#else
    // 1. 绘图之前,设定一个纹理, 后续的操作都将是针对该纹理的
    SDL_SetRenderTarget(renderer, texture);

    SDL_SetRenderDrawColor(renderer,255,255,255,0);
    SDL_RenderClear(renderer);//采用上面设置的画笔颜色来刷屏

    // 2. 绘制矩形
    SDL_RenderDrawRect(renderer, &rect);
    SDL_SetRenderDrawColor(renderer,0,0,255,0);
    SDL_RenderFillRect(renderer, &rect);

    // 3. 将渲染的目标重新设置为渲染器
    SDL_SetRenderTarget(renderer, NULL);//将要渲染的目标设置为默认的窗口

    // 4. 将做好的纹理拷贝的渲染器,来覆盖默认纹理
    SDL_RenderCopy(renderer, texture, NULL, NULL);

    // 5. 展示
    SDL_RenderPresent(renderer);
#endif
}

static Uint32 video_refresh_timer_cb(Uint32 interval, void *param)
{
    SDL_Event event;
    event.type = PLAYER_REFRESH_EVENT;
    event.user.data1 = param;
    SDL_PushEvent(&event);
    return 0;
}

static void start_video_refresh_timer( int delay)
{
    SDL_AddTimer(delay, video_refresh_timer_cb, NULL);
}

static void video_refresh(void * arg)
{
    display_advertisement(arg);
    start_video_refresh_timer( 20);
    
}

int main(int argc, char * argv[])
{
    SDL_Event event;
    int ret = -1;
    int len = 0;
	int quit = 0;
    
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
        printf("SDL_Init failed:%s\n", SDL_GetError());
        return ret;
    }
    sdl_window_init(1024,768);
    start_video_refresh_timer(20);
    while(!quit){
        SDL_WaitEvent(&event);
        switch(event.type){
            case PLAYER_QUIT_EVENT:
            case SDL_QUIT:
				quit = 1;
                printf("receive quit event\n");
                break;
            case PLAYER_REFRESH_EVENT:
                video_refresh(event.user.data1);
                break;
            default:
                break;
        }
    }
    ret = 0;

__FAILED:
	sdl_window_destroy();
    SDL_Quit();
    if( ret == 0){
        printf("end...success\n");
    }else{
        printf("end...failed\n");
    }
    return ret;
}

SDL_CreateTexture用于创建纹理, 若要使用SDL_SetRenderTarget()设置渲染到纹理时, 必须使用SDL_TEXTUREACCESS_TARGET方式来创建纹理, 函数SDL_SetRenderTarget()用于在渲染到纹理或屏幕之间进行选择。切换方式如下:

        SDL_SetRenderTarget(renderer, texture) // 渲染到纹理

        SDL_SetRenderTarget(renderer, NULL) // 渲染到屏幕

SDL_Renderer表示渲染上下文。这意味着它包含与渲染相关的所有当前设置,以及有关如何渲染当前帧的说明。

要创建渲染上下文,可以使用函数SDL_CreateWindowAndRenderer()或SDL_CreateRenderer()。前者同时创建窗口和渲染器。后者要求我们先创建一个窗口。

在程序中,您将使用SDL_SetRenderDrawColor()等函数更改上下文中的设置,并使用SDL_RenderDrawPoint()等函数执行渲染操作。

 

4. sdl surface与texture

 

     texture是surface 的新版本,surface是直接放在内存中的,没有硬件加速,由cpu计算每个像素点.而texture放在显存中,有硬件加速,效率更高.与surface一样,您可以直接编辑texture的像素数据。

我们可以基于surface来创建纹理,代码如下:

SDL_Surface* surface = SDL_LoadBMP("a.bmp");

SDL_Texture* texture = SDL_CreateTextureFromSurface( renderer, surface );
if( texture == NULL){
    //failed;
}

SDL_FreeSurface( surface );

 使用函数SDL_CreateTextureFromSurface()从表面创建纹理, 失败时返回NULL,通过这种方式创建的纹理,数据会被复制到纹理, 从而我们可以释放surface。

使用函数SDL_CreateTexture来创建空白纹理,此函数采用要创建的纹理的渲染上下文,像素格式,宽度和高度。

当使用surface时,我们可以使用SDL_UpdateWindowSurface在窗口显示。在使用texture时, 我们可以使用SDL_RenderPresent告诉渲染器在其窗口上显示其操作。

 

5. 一些函数说明

SDL提供了几种绘制基本形状的功能 - 包括点,线,矩形和填充矩形。

SDL_RenderDrawPoint
SDL_RenderDrawPoints
SDL_RenderDrawLine
SDL_RenderDrawLines

SDL_RenderDrawRect
SDL_RenderDrawRects
SDL_RenderFillRect
SDL_RenderFillRects

 

SDL_DestroyRenderer:在关闭窗口之前释放渲染器

 

int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,
                                           SDL_Texture * texture,
                                           const SDL_Rect * srcrect,
                                           const SDL_Rect * dstrect);
renderer:渲染目标。
texture:输入纹理。
srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入。
dstrect:选择渲染目标的一块矩形区域作为输出。设置为NULL的时候整个渲染目标作为输出。
 

 

6. 参考 

https://blog.csdn.net/victo2012/article/details/51866305

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值