Xcode與C++之遊戲開發: 2D圖形

上一篇:Xcode與C++之遊戲開發:創建環境

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。意味着有 2242^{24} 或者說 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_SOFTWARESDL_RENDERER_TARGETTEXTURE,見 API 文檔

檢驗創建是否成功的手法和前面創建窗口時是一樣的。最後,我們需要在離開的時候手動銷燬渲染器。通常析構(銷燬)順序和構造順序相反,因此先銷燬渲染器,再銷燬窗口。

void Game::Shutdown()
{
  SDL_DestroyRenderer(mRenderer);
  SDL_DestroyWindow(mWindow);
  SDL_Quit();
}

渲染 Tiffany 藍

有一種藍,代表着一種浪漫與幸福。有一種藍,它每的一個細節和理念,都始終只詮釋兩種東西,這兩種東西,一種叫愛,而另一種叫美。這種藍,就叫做Tiffany(蒂芙尼)藍。

任何遊戲圖形庫繪製圖形時都包含這三個步驟:

  1. 清空後緩衝區的顏色(當前遊戲的緩衝區)
  2. 寫入整個遊戲的場景
  3. 交換前後緩衝區

渲染圖形屬於最終的輸出,我們把這部分代碼放到 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遊戲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章