myshmup.com 允許在瀏覽器中創建 shmup(射擊)游戲。 你可以使用具有創意通用許可證的資源或上傳自己的藝術作品和聲音。 創建的游戲可以在網站上發布。 該平臺不需要編碼,游戲對象的配置是在用戶界面的幫助下執行的。 后端是使用Django框架開發的。 編輯器 UI 用 Javascript 編寫并使用 REACT 框架 游戲用 Typescript 編寫并調用低級 Webgl API 進行渲染。
在這篇文章中,我將解釋我在游戲部分使用的優化,以確保在大多數瀏覽器中獲得流暢的 60PS 體驗。
推薦:用 NSDT編輯器 快速搭建可編程3D場景
1、Webgl API
Webgl(Web 圖形庫)API 允許使用現代 GPU 在瀏覽器內渲染圖形。 為了讓 GPU 工作,你需要提供兩個稱為著色器的函數:頂點著色器和片段著色器。 著色器是用類似于 C++ 的 GLSL(GL Shader Language)編寫的。
頂點著色器旨在計算場景的頂點位置。 然后,頂點著色器的輸出被發送到片段著色器,片段著色器計算渲染的所有像素的顏色。 在 myshmup.com 中,我使用了一對簡單的頂點和片段著色器。 它們僅處理 2D 矩形作為原始形狀,每個矩形都有自己的紋理要繪制在其表面上。 可以調整紋理顏色以啟用閃爍效果。 大多數渲染工作包括向著色器提供每幀所需的數據。
2、簡單的實現
當我第一次嘗試使用 Webgl 渲染 2D 游戲時,我編寫了一對在屏幕上繪制紋理的著色器。 著色器由打字稿函數處理,該函數一次繪制一個游戲對象。 這個低級函數由繪制函數調用,以游戲對象作為輸入。 為屏幕上可見的每個游戲對象調用繪制函數。
gameObjects.forEach( gameObject => draw(gameObject) );
雖然這種方法對于對象數量較少的情況效果很好,但當在最近的 mac book pro 上游戲對象數量超過 50 個時,這種方法的結果非常差。 出事了…
3、盡可能減少Draw Call次數
每個渲染幀都是 CPU 和 GPU 共同完成工作的結果。 CPU 為 GPU 準備數據和指令。 GPU 內存位于 GPU 上。 它稱為 VRAM,與主 RAM 分開。 因此,我們需要在經典 RAM 和 GPU VRAM 之間傳輸數據。 在每次繪制調用時,GPU 都必須等待。 只有將所需數據從 RAM 推送到 VRAM 后才能開始渲染。 準備就緒后,由于其高并行化級別,顯卡可以開始高效地完成其工作。
它就像一家工廠:它的設計目的是在給定批次中生產大量商品,但批次的設置時間可能很長。 你不想使用這條生產線進行手工作業,每批次生產一個物體,你希望每批次生產數百個木托盤,以優化生產成本。
我們現在了解最小化每幀執行的繪制調用次數的重要性。 CPU 到 GPU 的數據傳輸開銷是快速渲染的主要瓶頸。
4、實例繪制
我們的目標是盡量減少繪制調用的數量。 為此,我們將使用相同紋理的所有游戲對象打包在一起。 想想射擊游戲中敵人或英雄的所有子彈。 它們在屏幕上數量眾多(因此有“彈幕地獄”的表達方式),但它們具有相同的紋理。 這只是在不同位置繪制相同的精靈。 裝飾也是如此,它們通常由屏幕上重復的瓷磚制成。 你無需將游戲對象集處理為簡單數組,而是在渲染之前準備數據。 你構建一個貼圖,其中鍵是紋理 ID,值是使用該紋理的對象數組。 然后你可以只為每個紋理調用一次繪制函數。 對于有大量重復精靈的 shmup 來說,這是一個巨大的節省。
textures.forEach( texture => draw(texture.gameObjects))
為了在一批中多次使用相同紋理繪制對象數組,我們應該使用 Webgl 2 的“實例繪制”功能。此功能在 Webgl 1 中作為一個選項提供。為了簡單起見,我們決定使用 Webgl 2 盡管它并不與當今所有的瀏覽器兼容。
5、紋理圖集
我實現了實例繪制,一切都很好。 經過一年的開發,我向公眾發布了該網站。 組織了一次游戲開發活動,所有游戲都是使用 myshmup.com 創建的。 每個參與者都在短時間內創造了非常原創的游戲。 Game Jam 的獲勝者發布了一個受 TRON 電影啟發的帶有霓虹燈像素藝術的關卡。 他創造了大量的裝飾瓷磚和可破壞的地面敵人來提供豐富的游戲環境。 然后又出現了這樣的情況:在我最先進的、潮人認可的 mac book pro 上,游戲有時會出現滯后。 什么問題? 該游戲在給定時間顯示的不同紋理的數量比簡單游戲中的要多。 接下來做什么?
靈丹妙藥是“紋理圖集”技巧。 這個想法是創建一個非常大的紋理:在 myshmup.com 中,圖集大小是 4096 x 4096 像素。 然后你只需在這個大紋理中繪制所有游戲對象的紋理即可。 當你將一個紋理復制到圖集中時,你可以跟蹤與其關聯的紋理坐標,以便以后可以檢索它。 如果你的圖集太小,只需創建另一個圖集。
實現紋理圖集后,我獲得了 Webgl 必殺技。 我每幀只調用一次繪制函數。 好吧,說實話,更準確的說法是每層每幀只調用一次繪制函數。 這意味著 myshmup.com 中有 10 個平局:游戲中有 6 個視差層,另外 4 個用于游戲 UI(得分欄和按鈕)。 就是這樣。 我可以有 1000 個對象,每幀只會繪制 10 次。 GPU 像天才一樣完成繁重的工作和渲染一切。
6、得到的教訓
myshmup.com 紋理圖集示例
這次 webgl 優化之旅充滿了驚喜。 如果實現實例化繪圖和紋理圖集看起來像是過度設計,請相信我,事實并非如此。 在瀏覽器中擁有流暢的動作游戲是關鍵。 只有在那之后,我才對我的平臺提供流暢娛樂的穩健性充滿信心。 當你擁有幾乎恒定的 60 FPS 幀速率時,喜悅是發自內心的!
原文鏈接:WebGL射擊游戲的優化 — BimAnt