SDL2 Render、Surface、Texture概念

SDL2 Render、Surface、Texture概念


对Render、Surface和Texture的作用和相互之间的关系做个总结。首先Render是渲染,可以简单理解成绘制,可以绘制矩形,线段等图形,也可以将Texture渲染在窗口上,Surface是SDL1.X时代的产物,也是用来显示BMP文件。Texture是SDL2版本的产物.

下面引用SDL官网《SDL 1.2 to 2.0 Migration Guide》的介绍

视频API是1.2版本中最引人注目的变化。自从SDL的API在20世纪90年代末设计以来,需求发生了很大变化。为了处理现代硬件和操作系统功能,我们几乎完全取代了旧的1.2视频API。
对于2D图形,SDL 1.2提供了一个称为“Surface”的概念,即像素的内存缓冲区。如果你在做2D软件渲染,屏幕本身就是一个“表面”,我们提供了在Surface之间复制(“blit”)像素的功能,并根据需要转换格式。你几乎总是在系统RAM中的CPU上工作,而不是在视频存储器中的GPU上工作。SDL 2.0改变了这一点;现在,您几乎总是可以获得硬件加速,并且API已经更改以反映这一点。
SDL 2.0允许您拥有多个窗口,因此旧的功能不再有意义。SDL_SetVideoMode()
所以你可能有这样的经历:
SDL_WM_SetCaption("My Game Window", "game");
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_FULLSCREEN | SDL_OPENGL);
现在是这样的:
SDL_Window *screen = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
你可以看到这个映射非常接近1.2。不同的是,你可以有多个窗口(如果你愿意的话),你可以更多地控制它们。已经消失,因为我们希望允许每个窗口都有自己的标题(您可以稍后使用SDL_SetWindowTitle()更改它),并且我们希望允许您指定窗口位置(或者,在这种情况下,使用SDL_WINDOWPOS_UNDEFINED,因为我们不在乎系统将其放置在哪里。SDL_WINDOVPOS_CENTERED也是一个不错的选择)。
SDL2仍然有SDL_Surface,但如果可能的话,您需要的是新的SDL_Texture,Surface现在总是在系统RAM中,并且总是由CPU操作,所以我们想摆脱它。SDL2有一个新的渲染API。它适用于简单的2D游戏,但最值得注意的是,它旨在将所有软件渲染到视频RAM和GPU中。即使你只想使用它将软件渲染器的工作显示在屏幕上,它也带来了一些非常好的好处:如果可能的话,它将在幕后使用OpenGL或Direct3D,这意味着你可以自由获得更快的blits、 a working Steam Overlay和缩放。
SDL_SetVideoMode()变为SDL_CreateWindow(),正如我们之前所讨论的那样。但是,我们对这项决议提出了什么建议?例如,如果你的游戏被硬编码为640x480,你可能会遇到无法达到全屏分辨率的显示器,而在窗口模式下,你的游戏可能看起来像是真正高端显示器上的动画邮票。SDL2中有一个更好的解决方案。
DL2中有一个等价的功能(循环调用SDL_GetDisplayMode(),SDL_GetNumDisplayModes()次),但我们将使用一个称为“全屏桌面”的新功能,它告诉SDL“给我整个屏幕,不要改变分辨率”。对于我们假设的640x480游戏,它可能如下所示:SDL_ListModes()
SDL_Window *sdlWindow = SDL_CreateWindow(title,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
0, 0,
SDL_WINDOW_FULLSCREEN_DESKTO
请注意,我们没有指定640或480…全屏桌面会为您提供整个显示,并忽略您指定的任何尺寸。游戏窗口应该立即出现,而无需等待监视器点击到新的分辨率,我们将使用GPU缩放到桌面大小,这往往比LCD假装更低的分辨率更快、更干净。额外的好处是:你的背景窗口现在都没有调整大小。
现在我们需要一个渲染上下文。
SDL_Renderer *renderer = SDL_CreateRenderer(sdlWindow, -1, 0);
渲染器隐藏了我们如何在窗口中绘制的细节。这可能是在后台使用Direct3D、OpenGL、OpenGL ES或软件表面,具体取决于系统提供的功能;无论SDL选择什么,您的代码都不会更改(尽管欢迎您强制使用一种或另一种渲染器)。如果您想尝试强制同步到vblank以减少撕裂,可以使用SDL_RENDERER_PRESENTVSYNC而不是零作为第三个参数。您不应该在此处创建带有SDL_window_OPENGL标志的窗口。如果SDL_CreateRenderer()决定要使用OpenGL,它会为您适当地更新窗口。
现在您已经了解了这是如何工作的,如果您不想要任何花哨的东西,也可以使用SDL_CreateWindowAndRenderer()一步完成这一切:
SDL_Window *sdlWindow;
SDL_Renderer *sdlRenderer;
SDL_CreateWindowAndRenderer(0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP, &sdlWindow, &sdlRenderer);
假设这些函数没有失败(请始终检查NULL!),您就可以开始在屏幕上绘图了。让我们从将屏幕清除为黑色开始。
SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255);
SDL_RenderClear(sdlRenderer);
SDL_RenderPresent(sdlRenderer);
这就像你可能想的那样;用黑色绘制(r,g,b全部为零,alpha完全),清除整个窗口,将清除的窗口放在屏幕上。没错,如果你一直在使用或将你的比特放在屏幕上,渲染API会使用
SDL_RenderPresent().SDL_UpdateRequest()SDL_Flip()
这里还有一个要设置的一般内容。由于我们使用的是SDL_WINDOW_FULLSCREEN_DESKTOP,我们实际上不知道要画多少屏幕。幸运的是,我们不必知道。1.2的一个好处是,你可以说“我想要一个640x480的窗口,我不在乎你如何完成它”,即使完成它意味着代表你的应用程序以更大的分辨率将窗口居中。
对于2.0,渲染API允许您执行此操作:
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // 使缩放后的渲染看起来更平滑
SDL_RenderSetLogicalSize(sdlRenderer, 640, 480);
它会为你做正确的事情。这很好,因为您可以更改逻辑渲染大小以实现各种效果,但主要用途是:我们现在可以让您的渲染大小与系统配合使用,而不是试图让系统与您的渲染尺寸配合使用。在我的1920x1200显示器上,这个应用程序认为它现在的分辨率是640x480,但SDL正在使用GPU将其放大以使用所有这些像素。请注意,640x480和1920x1200的纵横比并不相同:SDL也会考虑到这一点,尽可能多地进行缩放,并对差异进行字母组合。
现在我们已经准备好开始真正的绘画了。
如果你的游戏只是想在屏幕上获得完全渲染的帧
老式软件渲染游戏的一个特殊情况是:应用程序希望自己绘制每个像素,并在一次大的blit中有效地将最后一组像素绘制到屏幕上
为此,您需要一个SDL_Texture 来表示屏幕。现在让我们为640x480游戏创建一个:
sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640, 480);
这表示GPU上的纹理。游戏计划是通过将像素上传到该纹理,将纹理绘制到窗口,并将该绘制翻转到屏幕上来完成每一帧。SDL_TEXTUREACCESS_STREAMING告诉SDL此纹理的内容将频繁更改。
之前,你可能有一个SDL_Surface用于你的应用程序绘制的屏幕,然后调用它放在屏幕上。现在,您可以创建一个始终在RAM中的SDL_Surface,而不是使用您本来可以从中获得的SDL_Ssurface,或者只是malloc()一个要写入的像素块。理想情况下,您可以写入RGBA像素的缓冲区,但如果需要进行转换,也可以。SDL_Flip()SDL_SetVideoMode()
extern Uint32 *myPixels; // 这可能是一个surface->pixels,或者malloc()缓冲区,或者其他什么。
在帧的末尾,我们想上传到纹理,如下所示:
SDL_UpdateTexture(sdlTexture, NULL, myPixels, 640 * sizeof (Uint32));
这将把你的像素上传到GPU内存。如果你想乱搞脏矩形,NULL可能是一个子区域,但现代硬件很可能会毫不费力地吞下整个帧缓冲区。最后一个参数是间距——从一行开始到下一行的字节数——由于我们在这个例子中有一个线性RGBA缓冲区,它只是4(r,g,b,a)的640倍。
现在将该纹理放到屏幕上:
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);
这就是全部。SDL_RenderClear()清除现有的视频帧缓冲区(例如,在最后一帧上写入的Steam Overlay),SDL_RenderCopy()将纹理的内容移动到视频帧缓冲区时(由于SDL_RenderSetLogicalSize(),它将被缩放/居中,就像监视器是640x480一样),SDL_RenderPresent()将其放在屏幕上。
如果你的游戏想blit surfaces 显示在屏幕上
这个场景让你的SDL 1.2游戏将一堆图形从磁盘加载到一堆SDL_Surface中,可能试图用将它们加载到视频RAM中。您加载这些文件一次,然后根据需要将它们一次又一次地快速传输到帧缓冲区,但除此之外,它们永远不会改变。一个简单的2D平台生成器可以做到这一点。如果你倾向于将surfaces 视为“精灵”,而不是像素的缓冲区,那么这可能就是你-SDL_HWSURFACE
你可以像我们为一个大纹理所做的那样构建单独的纹理(存在于GPU内存中的表面):
sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STATIC,
myWidth, myHeight);
这符合你的期望。我们使用SDL_TEXTUREACCESS_STATIC,因为我们要上传一次像素,而不是一遍又一遍。但更方便的解决方案可能是:
sdlTexture = SDL_CreateTextureFromSurface(sdlRenderer, mySurface);
使用这个,你可以像往常一样加载SDL_Surface,但最后你可以用它制作一个纹理。一旦你有了SDL_texture,你就可以释放原始Surface
在这一点上,您的1.2游戏有一堆SDL_surface,它将SDL_BlitSurface()添加到屏幕表面以组成最终的帧缓冲区,并最终SDL_Flip()到屏幕。对于SDL 2.0,您有一堆SDL_Textures,您将SDL_RenderCopy()添加到渲染器以组成最终的帧缓冲区,并最终将SDL_RederPresent()添加至屏幕。就这么简单。如果这些纹理永远不需要修改,你可能会发现你的帧速率也刚刚突破了极限。
如果你的游戏想同时做到这两点
如果您想blit surfaces并修改帧缓冲区中的单个像素,情况会稍微复杂一些。往返——从纹理中读取数据——可能会非常昂贵(Round trips--reading data back from textures--can be painfully expensive。)通常,您希望始终向一个方向推送数据。在这种情况下,最好将所有内容都保存在软件中,直到最后推送到屏幕,所以我们将结合前面的两种技术。
好消息是:1.2 SDL_Surface API大部分仍然存在。因此,更改您的屏幕表面:from
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, 0);
...to this...
// 如果所有这些十六进制都让你害怕,请查看SDL_PixelFormatEnumToMasks()!
SDL_Surface *screen = SDL_CreateRGBSurface(0, 640, 480, 32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
SDL_Texture *sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640, 480);
…并继续像以前一样快速移动和调整像素,将您的最终帧缓冲区组合到这个SDL_Surface中。一旦你准备好在屏幕上显示这些像素,你就可以像在我们的第一个场景中一样这样做:
SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch);
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);
请注意,texture 创建可能既昂贵又资源有限:不要每帧都调用SDL_CreateTextureFromSurface()。设置一个texture 和一个surface ,并从后者更新前者。
Render API有更多的功能,其中一些功能可能能够取代应用程序的代码:缩放、线条绘制等。如果你正在阅读本节,因为除了 blitting surfaces之外,你有简单的需求,你可能能够停止戳单个像素,并将所有内容移动到GPU上,这将大大提高程序的速度,并可能大大简化代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值