Wednesday, July 11, 2018

Development Updates



因為已經很久沒有新文章,今天就來更新一下開發進度吧。

六月將Switch繪圖底層移植的工作完成後,終於有時間回到自己的專案來工作。之前雖然已經在Unity上將 voxel系統做了一些嘗試,但一直都沒有時間將程式碼做一個整理以及最佳化,所以就趁著還沒辦法開始production的這段時間先來做一些前製的工作,將該準備好的技術都先整理完成。同時因為還是會用voxel的模型來製作遊戲,所以美術的製程跟一般傳統遊戲會有些不同,因此也需要將技術部分確定後建立起之後的美術製程。

之前其實我用了兩種方法在Unity上實作voxel系統,一種是用內建的particle system,但由voxel system控制每一個分子的移動以及位移,因為底層是Unity自家最佳化過的,所以效能較好,但是缺點就是彈性較小。另一種方法則是使用material instancing,這種做法就跟我之前引擎是類似的,優點就是彈性更大,但是在之前實作中效能跟particle system還是有點差距。所以這次的任務就是優化material instancing based voxel system,同時還要設計一個新的檔案格式符合新的voxel system的新功能。

分析了一下material instancing based voxel system,發現瓶頸是在每個update中的4x4矩陣相乘,不過這部分其實很單純,就是呼叫以下function

static void InstanceTransformUpdate(ref Matrix4x4 o, ref Matrix4x4 p, ref Matrix4x4 t){o = p * t;}


於是我試著將移除矩陣相乘的operator overloading,將function改成如下

static void InstanceTransformUpdate(ref Matrix4x4 o, ref Matrix4x4 p, ref Matrix4x4 t)
{
o.m00 = p.m00 * t.m00 + p.m01 * t.m10 + p.m02 * t.m20 + p.m03 * t.m30;
o.m01 = p.m00 * t.m01 + p.m01 * t.m11 + p.m02 * t.m21 + p.m03 * t.m31;
o.m02 = p.m00 * t.m02 + p.m01 * t.m12 + p.m02 * t.m22 + p.m03 * t.m32;
o.m03 = p.m00 * t.m03 + p.m01 * t.m13 + p.m02 * t.m23 + p.m03 * t.m33;
o.m10 = p.m10 * t.m00 + p.m11 * t.m10 + p.m12 * t.m20 + p.m13 * t.m30;
o.m11 = p.m10 * t.m01 + p.m11 * t.m11 + p.m12 * t.m21 + p.m13 * t.m31;
o.m12 = p.m10 * t.m02 + p.m11 * t.m12 + p.m12 * t.m22 + p.m13 * t.m32;
o.m13 = p.m10 * t.m03 + p.m11 * t.m13 + p.m12 * t.m23 + p.m13 * t.m33;
o.m20 = p.m20 * t.m00 + p.m21 * t.m10 + p.m22 * t.m20 + p.m23 * t.m30;
o.m21 = p.m20 * t.m01 + p.m21 * t.m11 + p.m22 * t.m21 + p.m23 * t.m31;
o.m22 = p.m20 * t.m02 + p.m21 * t.m12 + p.m22 * t.m22 + p.m23 * t.m32;
o.m23 = p.m20 * t.m03 + p.m21 * t.m13 + p.m22 * t.m23 + p.m23 * t.m33;
o.m30 = p.m30 * t.m00 + p.m31 * t.m10 + p.m32 * t.m20 + p.m33 * t.m30;
o.m31 = p.m30 * t.m01 + p.m31 * t.m11 + p.m32 * t.m21 + p.m33 * t.m31;
o.m32 = p.m30 * t.m02 + p.m31 * t.m12 + p.m32 * t.m22 + p.m33 * t.m32;
o.m33 = p.m30 * t.m03 + p.m31 * t.m13 + p.m32 * t.m23 + p.m33 * t.m33;
}
結果效能顯著的提升,從75 fps直接跳到120 fps! 起初我以為是C#的數學運算效能問題,後來有朋友傳來Unity的C# 原始碼,一切才水落石出,以下是Unity的矩陣相乘原始碼

// Multiplies two matrices.
public static Matrix4x4 operator*(Matrix4x4 lhs, Matrix4x4 rhs)
{
    Matrix4x4 res;
    res.m00 = lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10 + lhs.m02 * rhs.m20 + lhs.m03 * rhs.m30;
    res.m01 = lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11 + lhs.m02 * rhs.m21 + lhs.m03 * rhs.m31;
    res.m02 = lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02 * rhs.m22 + lhs.m03 * rhs.m32;
    res.m03 = lhs.m00 * rhs.m03 + lhs.m01 * rhs.m13 + lhs.m02 * rhs.m23 + lhs.m03 * rhs.m33;

    res.m10 = lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10 + lhs.m12 * rhs.m20 + lhs.m13 * rhs.m30;
    res.m11 = lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21 + lhs.m13 * rhs.m31;
    res.m12 = lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22 + lhs.m13 * rhs.m32;
    res.m13 = lhs.m10 * rhs.m03 + lhs.m11 * rhs.m13 + lhs.m12 * rhs.m23 + lhs.m13 * rhs.m33;

    res.m20 = lhs.m20 * rhs.m00 + lhs.m21 * rhs.m10 + lhs.m22 * rhs.m20 + lhs.m23 * rhs.m30;
    res.m21 = lhs.m20 * rhs.m01 + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21 + lhs.m23 * rhs.m31;
    res.m22 = lhs.m20 * rhs.m02 + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22 + lhs.m23 * rhs.m32;
    res.m23 = lhs.m20 * rhs.m03 + lhs.m21 * rhs.m13 + lhs.m22 * rhs.m23 + lhs.m23 * rhs.m33;

    res.m30 = lhs.m30 * rhs.m00 + lhs.m31 * rhs.m10 + lhs.m32 * rhs.m20 + lhs.m33 * rhs.m30;
    res.m31 = lhs.m30 * rhs.m01 + lhs.m31 * rhs.m11 + lhs.m32 * rhs.m21 + lhs.m33 * rhs.m31;
    res.m32 = lhs.m30 * rhs.m02 + lhs.m31 * rhs.m12 + lhs.m32 * rhs.m22 + lhs.m33 * rhs.m32;
    res.m33 = lhs.m30 * rhs.m03 + lhs.m31 * rhs.m13 + lhs.m32 * rhs.m23 + lhs.m33 * rhs.m33;

    return res;
}
原來問題是來自於pass by value,如果直接使用Matrix4x4的乘法operator,每一次的相乘就會new出三個暫時的Matrix4x4,而這就是效能衰退的原因,同時還可能會造成GC的問題。不過我比較不理解的是為何Unity不另外增加一個static的數學lib是可以直接 pass by reference,讓需要效能的地方可以有個選擇,之前我的引擎雖然是C++,但是為了避免多一個copy動作,我就有設計另一組static的數學lib來給需要效能的地方使用。

之後我還嘗試了將矩陣相乘用C++寫成plugin,甚至用組合語言改寫並加上SSE加速,但效能提升很有限,所以最後我還是決定使用C#的static math function就好。不過即便如此,提升後的效能還是跟particle system有段差距。我想到如果能將矩陣運算丟入GPU的話,應該能提升更多效能,於是修改了rendering的code以及shader,終於可以得到跟Particle System差不多的效能,在某些情況下還可以超越。將來如果有時間,我大概會想辦法把剩下的CPU運算都丟進compute shader中,不過以目前的專案來說這樣的效能應該已經足夠,所以我就先在這裡打住了。

這次還做了一個之前一直想做的新設計就是將voxel data跟rendering的code分離,藉由將這兩個類別decouple讓整個系統更有彈性同時也可以得到更好的效能。

回到遊戲專案上,其實目前專案的進度有點卡住,主要是因為還沒有找到合適的美術人選,所以只能先從技術部分下手,但是關於rendering的部分因為沒有確定風格也無法展開太多的研究。這次的專案是一個以voxel為基礎的動作遊戲,如果有對做voxel類型遊戲有興趣的美術高手我們可以聊聊喔 :)




Wednesday, March 21, 2018

Switch Hardware Experiment

現在的遊戲程式們大概很難想像,在遊戲引擎變成免費之前,想要將遊戲移植到新平台,如果沒錢買引擎,唯一的方法就是花時間啃文件,然後紮紮實實的從無到有將系統一步一步實作出來。原本以為今後應該是沒有機會再幹這種需要硬功夫的工作了,沒想到最近在因緣際會下,接到了一份將OGLES的繪圖引擎移植到Switch原生的繪圖API之上的工作,而又有了一次摸到新主機硬體的機會。



通常在移植既有引擎到新硬體上時,我都會先完成memory,file system以及OS相關部分的移植,然後才開始動繪圖部分。這次因為只需要移植繪圖部分,所以輕鬆了一些,不過取而代之的就是要先讀懂別人的程式碼並推測出整個系統架構。而繪圖部分通常我會從幾個步驟來逐步實作

  1. 初始化device
  2. 將device與OS原生context串接,這部分有時還挺麻煩的
  3. 建立frame buffers以及swap chain. 到了這一步,基本上就可以看到畫面了。在fixed function的硬體上,到這一步後其實就能draw primitive了。但是現代支援shader的硬體就比較麻煩了,還要很多步驟才能開始繪出東西。
  4. 實作vertex attribute layout以及vertex buffer
  5. 了解shader語言以及如何Compile and load shaders
  6. 實作貼圖的生成以及載入,到這一步才能開始測試draw primitive

理解硬體如何運作並實作其實並不難,看看文件以及範例其實就懂八九成了,真正麻煩的地方是要思考如何巧妙的把新的繪圖流程套到既有的系統架構上,讓系統可以跨平台執行,同時又不失效能,這部分就很需要經驗了,尤其是遇到架構大更新時(比如DX9進化到DX11)就更考驗功力了。這次Switch的原生繪圖API是類似Vulkan的架構,為的是減少driver的複雜程度以及負擔,而將大部分驗證以及command buffer、queue、記憶體等的管理工作都交由application來負責,藉此解決small batch的問題。也因為整個pipeline 完全不同了,要套用到原本的架構需要許多特別處理。

接下來兩個月大概都會忙這份工作而無法專心在新遊戲的開發上面,不過新遊戲的概念總算有些突破,接下來應該就可以花些時間來製作雛形了,不過恐怕就要犧牲些休息時間了。完成這份工作後,應該就可以投入更多的時間在新遊戲的開發上了吧。

Friday, February 02, 2018

New Chapter

2018對我來說可能會是很特別的一年,因為去年結束掉一些事後,今年可以說是重新開始。前一陣子終於有時間靜下來回想一下過去幾年的生活,卻發現自己越走越遠離原本回國時想要的生活型態。因此今年開始,我便試著調整生活型態,看看接下來能走到哪裡。

首先最重要的當然就是做自己的遊戲了,不過光做自己遊戲沒有收入也是不行,再加上除了自己的遊戲也想做做其他有趣的東西同時多認識一些人,因此將目標訂在用40%時間做一些比較有趣或有技術挑戰性的工作來賺點奶粉錢,剩下60%的時間則用來做自己的遊戲。

所以今年一月開始參與一個有趣的專案,也因此開始更加深入的接觸了UE4。之前其實有開始試著將voxel的東西移植到UE4上,但因為種種原因做到一半就沒有繼續了,但是這次的專案卻得直搗UE4核心,改寫許多跟deferred shading相關的code跟 shader。這一陣子幾乎都是在trace UE4的原始碼,讓我回想起之前在AMD trace UE3原始碼的日子,不過那時修改的是global shader,相對起來不用對引擎架構了解太深入,只要了解RHI的架構就可以解決。這次因為要動到整個rendering pipeline,因此要對UE4整個架構有更深入的挖掘才有辦法植入一些新的shading模式。等專案結束後有時間我再來說說UE4的修改經驗。

而三月開始則要參與一個非常奇妙的專案,要直接跟某個console的繪圖底層對戰,幫新console硬體寫code是我覺得最有趣的工作之一了,所以我個人還蠻期待這個專案的。不過目前還不能透漏太多,等完成專案之後我再來分享一些經驗吧。

接下來則是自己的遊戲了,考量到時間以及人力,這次就決定使用Unity而不用自己引擎了。雖然改用了Unity,但我還是想繼續做voxel的東西,所以花了一些時間把voxel相關功能想辦法加入Unity之中了。因為Unity 5之後支援了hardware instancing功能,所以這一次我實際上implement了兩種不同的voxel renderer,一種是使用particle system,另一種則是使用了hardware instancing。而兩種voxel renderer也可以隨時更換。兩種voxel renderer的效能其實在不同狀況下各有優劣,可以視狀況選用。

有了voxel renderer後,一些voxel的特效也就能輕易地加入了,這次我還增加了對Magic Voxel檔案格式的直接支援,所以.vox檔可以直接拉入Unity中使用,這樣美術就可以直接用Magic Voxel來製作模型了。而為了增加同一個畫面中voxel object的數量,這次還使用了greedy mesh來做最佳化,同時還能自動產生LOD。


至於遊戲的內容,其實都還在構思中,也還有很多問題尚未想出很好的解決方案,所以其實進度是有點卡住了..... 只好多玩玩電動看看能不能找到解答的靈感了 XD 

Saturday, December 09, 2017

Something About Me

最近有空寫了一點回憶錄,但是不用擔心,我還很健康 XDDD

About fallingCAT

Tuesday, March 29, 2016

Qubot iOS版封測登記開跑!

本來應該要貼[秘辛]第一款國產XBOX遊戲製作回憶錄 - Part Two的,但是為了Qubot上市的事忙到不可開交,所以就留待下一次再分享了。

今天要分享的是Qubot iOS版登錄終於於開跑了。回想2013年三月開始這個專案時預計是一年內要完成,結果這個專案花了我兩年多的時間,寫了20萬行程式碼(不包含引擎程式碼)@@.... 不過終於是快要上市了 。

有興趣參加封測的朋友可以到這裡登記,到4/8截止,我們會選出500位玩家來參與封測。參加封測的玩家還有機會得到超稀有的活動限定機甲 「破空‧尤米尼斯」以及專屬駕駛員「墮影 青瀨隼人X」喔。



另外,由於我一個人負責整個遊戲的Client端以及工具,已經快要爆炸了,所以我們也開始在找新的程式以及合作夥伴加入公司,有興趣的朋友可以參考一下
http://www.qubitgames.com#join

以下是目前職缺,歡迎大家來apply :)

營運企劃

具遊戲及活動關卡內容規則及設計能力
善於分析遊戲結構
善於溝通與統合意見
熟稔遊戲社群的生態
(額外加分)熟日本動漫文化, 具英/日語能力者
(額外加分)活動策畫經驗者

社群行銷經理

熟稔遊戲/ACG社群的生態
有社群經營經驗者
擅長數據分析,能從數據解析社群行為及活動成效者
(額外加分)具英/日語能力者
(額外加分)熟悉歐美獨立遊戲社群者


CLIENT端程式人員

俱備C++/C#程式寫作能力,熟悉物件導向程式設計
熟悉STL以及Design Pattern
具備遊戲系統設計/實做能力
熟悉iOS/Android系統
(額外加分)了解DirectX/OpenGL等繪圖API基本運作方式
(額外加分)有第三方引擎(UE4, CryEngine...)開發經驗者
(額外加分)喜愛voxel者

美術設計人員

須兼具2D及3D造型及製作能力
具良好商用向排版, 構圖, 色彩及設計概念
具良好Maya或3D Max低模及貼圖製作能力
具良好自我時間控管能力
對pixel art及機器人題材ACG有愛
(額外加分)有設計或動畫經驗者


Friday, July 10, 2015

[秘辛]第一款國產XBOX遊戲製作回憶錄 - Part One

之前整理硬碟時發現一些老檔案,是15年前製作的XBOX遊戲<魔武爭鋒(Stake)>的一些相關檔案跟影片,很令人懷念,所以就想說寫一篇文章來紀念一下在那個沒有Unity的年代,我們是如何辛苦的獨立製作出一款3D動作遊戲,並把它移植到XBOX上的。這些東西大概是現在開發遊戲的小朋友們沒經歷過也無法想像的艱苦過程吧 :)

遊戲最後在XBOX上的樣子

話說2000年的時候,我跟大宇資訊合資成立了一家遊戲公司,當時我們打算開發一款3D的連線動作遊戲,可以讓最多八個人在場上戰鬥。遊戲除了死鬥模式還有組隊推塔模式,其實有點像現在的DOTA或LOL,但是沒有小兵只有英雄。當初設定的目標還包含一個遊戲編輯器,可以讓玩家自製地圖,甚至是自訂英雄外型及招式。在2000年時這是個相當大的目標,要知道那時候上的了檯面的的引擎就只有Quake3跟Unreal,而那時引擎授權價格都要上千萬,我們當然沒錢買只能自己研發。以一個六人團隊要同時研發遊戲加上引擎還有編輯器,幾乎是不可能的任務,而如果還只有一位工程師,那根本就是自殺啊 :)

當年做的推塔地圖之一

在2000年的時候,想要做3D遊戲的人沒有足夠的資金就只能自己開發引擎了。不過由於我在前幾份工作中都一直在做建構3D引擎的工作,加上我下班後都有在維護自己的一個圖形引擎,這部分倒不是太大的問題。2000年的時候繪圖加速卡才開開始流行,D3D也不是那麼成熟,許多大作都是用OpenGL來當作繪圖API,尤其那時受John Carmack跟Chris Hecker這些大神影響很深,所以其實我一開始遊戲引擎也是採用OpenGL作為繪圖API。另一個影響則是我決定整個引擎採用C來撰寫而不是C++,因為除了JC跟CH大力鼓吹用C比C++有效率外,另一個因素是我前一個Dreamcast的引擎使用了SEGA的SDK但它卻沒有很完整的支援C++,所以到最後整個遊戲、引擎以及編輯器都是用C寫出來的,現在想起來還是覺得超猛的 XD 更猛的是因為用C所以無法使用MFC,結果整個編輯器是用Windows SDK硬幹出來的.....

特效編輯(包含碰撞範圍設定)

Windows SDK硬幹出來的遊戲編輯器

另一個問題則是角色動作了,在那個年代,bone&skin還是非常神秘而且高技術的東西。遊戲通常都使用segment animation。即使是Quake 3跟Unreal用的也是vertex animation而已。但是對FPS這種動作量很少的遊戲vertex animation不是問題,但像我們這種遊戲角色有許多招式的動作遊戲就是個大問題了。所以那時我決定要自己搞定bone&skin的東西。這不單是要了解bone&skin的運作原理,還必須要瞭解到bone&skin的資料在3ds max這類軟體中是怎麼被運算跟儲存的,也必須要了解這些軟體中animation key的原理跟運作方式,然後還要寫出exporter將必要的資料輸出。結果這部分花了好幾個月才完全搞定。


角色編輯
現在已經是標配的的物理引擎那時也都還沒生出來,自然碰撞部分也都得自己來搞定。而那時的顯卡繪圖能力也還很弱,所以計算PVS是一件很重要的事,那時最流行的是BSP,我也嘗試過使用portal來計算PVS。但是由於我們的遊戲是在開放場景中亂鬥,這兩個方式都不是很合適,因此我最後採用的是OSP八元樹的方式來分割場景。同時OSP也可以做於碰撞偵測的多邊形篩選以加速碰撞偵測。當然所有物件的碰撞偵測以及位移都得自己搞定。

3ds Max 中的Preview Plug-in

結果好像一篇文章寫不完.... 這次先到此為止,下一篇再繼續,keep tuned  :)