上一篇:Xcode与C++之游戏开发:创建环境
<https://blog.csdn.net/guyu2019/article/details/87402109>

<>2D 图形

今天电视、电脑显示屏、手机、平板电脑用的显示图形基本都是光栅图形,我们常称之为位图。这些屏幕由像素点构成,每个像素点代表了不同颜色。分辨率
就是像素点方格的宽度和高度。例如,1920 × 1080,即1080p,意味着有1080行的像素点,每行由1920个像素点组成。类似的,3840 ×
2160,即4K,意味着每行有3840个像素点,总共由2160行。

每个像素的色调最常见的就是将红色®、绿(G)、蓝(B)三种颜色混合在一起。三种颜色不同强度的组合,形成了色域。要使显示器显示 RGB
图像,显示器必须知道每个位置上像素的颜色。除了 GRB 之外,很多游戏内部都会用 alpha 值来控制透明度。

<>颜色缓冲区

在计算机图形中,颜色缓冲区是内存中包含整个屏幕颜色信息的内存区域。显示屏可以使用颜色缓冲区在屏幕上绘制内容。将颜色缓冲区视为二维数组,其中每个 (x, y)
索引对应于屏幕上相应位置的像素。在游戏循环“生成输出”阶段的每个帧中,游戏都会将图形输出写入到颜色缓冲区。

颜色缓冲区的内存使用率取决于色彩深度(color
depth),即储存1像素的颜色所用的位数,它也称为位/像素(bpp)。举个例子,一个24比特的颜色深度,红、绿、蓝每个使用8个bit。意味着有224
2^{24}224 或者说 16,777,216 种独一无二的颜色。如果游戏还要另外使用8位来存储 alpha 值,每个像素总共需要32位来存储。

一个1080p(1920 × 1080)的图像,每个像素点32位,那么大概需要的内存空间就是1920 × 1080 × 4 bytes,大概是 7.9
MB。一些现代游戏使用16位来表示 RGB
的每个组成,这可以增加颜色的数量。当然,这将导致内存的使用量是原来的两倍,一张1080p的图像就接近16MB。不过现在显卡的显存基本都有几千MB,这个使用量还是显得微不足道。

<>色彩的表示


要在代码中表示颜色,通常有两种方法。假如给定一个8bit的颜色值,一种方法是简单的使用非负整数值去代表每个颜色(通道,channel)。8bit色彩深度的通道,值就介于0到255之间。另外一种方法就是将这个值规范化到0.0到1.0之间。

使用浮点数的一个好处是可以不用过分地关注色彩深度。举个例子,RGB 颜色(1.0, 0.0,
0.0)是一个纯粹的红色。在8bit色彩深度下表示成非负整数是(255, 0, 0),但是如果是在16bit的色彩深度下,它不再是纯红色,而接近黑色。


在这两种表示之间转换是很简单的。给定一个非负整数值,除以非负整数的最大值就可以获得规范化的浮点数。给定一个颜色的浮点数表示,乘以非负整数的最大值,就可以获得非负整数的表示形式。

SDL 接受的是非负整数的表示形式

<>双缓冲

屏幕刷新的频率可能不同于游戏刷新的频率。有的显示屏刷新的频率是 59.94 Hz,这就意味着它比每秒60次的刷新频率要低。有的显示屏刷新频率支持 144Hz
的刷新频率,这比游戏的刷新频率的2倍还多。

此外,
目前的任何显示技术都不能立即更新整个屏幕。总是有一些更新顺序——是逐行、逐列等等。假设游戏写入颜色缓冲区,同时从相同的颜色缓冲区中读取颜色的来显示。由于游戏帧速率的计时可能与显示器的刷新率不匹配,因此有可能还在游戏正在写入缓冲区时同时在读取颜色缓冲区。这是有问题的。

很容易想到的一个问题就是游戏正在写入 b 帧,用来覆盖颜色缓冲区中的 a 帧。然而,还没等 b 帧写入结束,该画面就被读取,造成只显示部分的 a 帧和部分的
b 帧。这种现象称为画面撕裂(screen tearing)。



要解决画面撕裂的问题,可以采用双缓冲区。将游戏写入的颜色缓冲区和屏幕读取的颜色缓冲区分开,交替的读取和写入这两个缓冲区。一般游戏写入的缓冲区叫后缓冲区
(back buffer),而屏幕读取的是前缓冲区(front buffer)。可是,双缓冲区本身并不能解决画面撕裂的问题。如果要显示 x
缓冲区的时候,游戏要写入该缓冲区。这种情况通常发生在游戏更新过快的时候,一样会导致画面撕裂。

解决这个问题的方法是同步,交换缓冲区的时候必须等到显示完成。换句话说,游戏必须等到屏幕显示完成才能开始写入。这被称为垂直同步(vertical
synchronization, vsync),由显示屏发送刷新的信号。

在垂直同步的情况下,有可能游戏刷新需要等待更长的时间。这就意味着游戏循环可能达不到 30 或 60 FPS。这可能是有的玩家无法接受的帧速。因此,
是否启用垂直同步不同游戏、不同用户的选择可能不同。一个好主意是在引擎中提供 vsync 作为一个选项,这样就可以在偶尔的屏幕撕裂或偶尔屏幕卡壳之间做出选择。

现在,一些高端的显示屏采用了新的显示技术,可以做到自适应刷新频率(adaptive refresh
rate),保持和游戏的刷新率一样以避免同步问题。当然,这种屏幕很贵。iPad Pro
现在就支持这种显示技术,根据浏览的内容自动调整刷新率,既可以保证流畅的体验,又节约能源。

<>实现基本的 2D 图形

SDL 有一组简单的函数可以实现 2D 图形的绘制。为了使用 SDL 的图形代码,需要通过 SDL_CreateRenderer 来构造
SDL_Renderer。渲染器(renderer)支持绘制 2D 和 3D。在这之前,先在 Game 类中添加 mRenderer
作为成员变量,同时在构造函数中增加nullptr 的默认初始化:
// 渲染器 SDL_Renderer* mRenderer;
在 Game.cpp 中:
Game::Game() :mWindow(nullptr) ,mRenderer(nullptr) ,mIsRunning(true) { }
下一步,在初始化 Game::Initialize() 时,同时初始化渲染器。
// 创建渲染器 mRenderer = SDL_CreateRenderer( mWindow, -1, SDL_RENDERER_ACCELERATED
| SDL_RENDERER_PRESENTVSYNC ); if (!mRenderer) { SDL_Log("创建渲染器失败: %s",
SDL_GetError()); return false; }
第一个参数是显示的窗口。第二的参数是用来指定驱动程序的索引,如果游戏有多个窗口,这可能是需要考虑的。不然的话采用 -1 由 SDL 来决定。
SDL_RENDERER_ACCELERATED 是采用硬件加速,SDL_RENDERER_PRESENTVSYNC 就是用来保持刷新率一致的。

还有另外两个标志:SDL_RENDERER_SOFTWARE、SDL_RENDERER_TARGETTEXTURE,见 API 文档
<https://wiki.libsdl.org/SDL_CreateRenderer#flags>

检验创建是否成功的手法和前面创建窗口时是一样的。最后,我们需要在离开的时候手动销毁渲染器。通常析构(销毁)顺序和构造顺序相反,因此先销毁渲染器,再销毁窗口。
void Game::Shutdown() { SDL_DestroyRenderer(mRenderer); SDL_DestroyWindow(
mWindow); SDL_Quit(); }
<>渲染 Tiffany 蓝


有一种蓝,代表着一种浪漫与幸福。有一种蓝,它每的一个细节和理念,都始终只诠释两种东西,这两种东西,一种叫爱,而另一种叫美。这种蓝,就叫做Tiffany(蒂芙尼)蓝。

任何游戏图形库绘制图形时都包含这三个步骤:

* 清空后缓冲区的颜色(当前游戏的缓冲区)
* 写入整个游戏的场景
* 交换前后缓冲区
渲染图形属于最终的输出,我们把这部分代码放到 Game::GenerateOutput 之中。要清除后缓冲区,需要用
SDL_SetRenderDrawColor 先指定一种颜色。这个函数接受一个渲染器的指针,和RGB色值(0到255)外带一个 Alpha
透明度。Tiffany 蓝的 RGB 色值是(129, 216, 209)。
void Game::GenerateOutput() { // 设置 Tiffany 蓝 SDL_SetRenderDrawColor( mRenderer
, 129, // R 216, // G 209, // B 255 // A ); }
接着,调用 SDL_RenderClear 清理后缓冲区,最终用 SDL_RenderPresent 交换前后缓冲区:
// 清理后缓冲区 SDL_RenderClear(mRenderer); // 交换前后缓冲区 SDL_RenderPresent(mRenderer);
<>最终

现在,编译运行项目,就可以看到这浪漫幸福的蒂芙尼蓝了:


下一篇:Xcode与C++之游戏开发:Pong游戏
<https://blog.csdn.net/guyu2019/article/details/87551008>

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信