本文編譯自VGHF游戲歷史基金會網(wǎng)站文章SEGA VR REVIVED: EMULATING AN UNRELEASED GENESIS ACCESSORY(https://gamehistory.org/segavr/),作者為Rich Whitehouse,文中的“我”均指原作者Rich Whitehouse。
自誕生以來,虛擬現(xiàn)實(shí)經(jīng)歷了幾波被稱為視頻游戲“Next Big Thing”的浪潮。
90年代初期,虛擬現(xiàn)實(shí)的潛力給大眾帶來了充分想象空間。報(bào)紙、雜志、電視新聞?lì)l道甚至包括《The Lawnmower Man》等主流電影都在描繪VR引人注目的功能。包括任天堂在內(nèi)的不少公司都在嘗試將VR技術(shù)用于新的產(chǎn)品,但最終真正推向市場的僅僅只有少數(shù)。
這一時(shí)期,世嘉也在積極探索如何設(shè)計(jì)一套價(jià)格合適的VR外設(shè),來拓展家用主機(jī)世嘉MD(即SEGA Genesis)的游戲性。與一些VR街機(jī)設(shè)備動(dòng)輒5位數(shù)的價(jià)格不同,世嘉VR頭顯的目標(biāo)售價(jià)僅200美元,配備高頻慣性測量單元和兩個(gè)LCD屏幕,與如今的VR頭顯設(shè)計(jì)十分相似。1993年世嘉向記者和經(jīng)銷商公布了這一全新的外設(shè),期望在VR的前沿領(lǐng)域取得新的突破。得益于當(dāng)時(shí)一家初創(chuàng)公司One-Sendai的專利授權(quán),世嘉VR頭顯的追蹤解決方案僅需1美元的生產(chǎn)成本,這也是其價(jià)格能夠低至200美元的關(guān)鍵。
盡管世嘉官方對于最終取消VR頭顯上市給出的理由是:因?yàn)閂R體驗(yàn)過于逼真和沉浸,玩家可能在使用這款VR頭顯時(shí)因?yàn)樗奶幾邉?dòng)而面臨極高的傷害風(fēng)險(xiǎn),但這一理由十分牽強(qiáng)。因?yàn)槭兰螐乃固垢Q芯克@得的反饋稱,使用世嘉VR頭顯可能導(dǎo)致頭昏眼花甚至頭痛,特別是對于兒童和青少年有更高的風(fēng)險(xiǎn)。而世嘉前首席執(zhí)行官Tom Kalinske也在Retro Gamer Podcast的一集中證實(shí),后者才是世嘉放棄VR項(xiàng)目的主要原因。
迄今為止,我們對于世嘉VR的了解大部分來源于早年CES展示的外觀原型、世嘉的廣告、專利文件以及來自內(nèi)部工作人員的部分資料,但這也意味著我們對于世嘉VR許多技術(shù)細(xì)節(jié)的了解大多基于推測,甚至根本是未知的。想要回顧和研究這樣一款在當(dāng)時(shí)來說突破了許多技術(shù)界限的產(chǎn)品,這些細(xì)節(jié)十分重要。無論世嘉VR是否實(shí)現(xiàn)了世嘉的野心,它在VR的歷史上依然是一款耀眼的產(chǎn)品。
如果你想了解世嘉VR到底是一款怎樣的產(chǎn)品,最佳的選擇當(dāng)然是找到世嘉VR頭顯硬件,其次則是從世嘉VR的配套游戲入手,研究出游戲運(yùn)行時(shí)對硬件的需求,這樣我們也能夠獲取足夠多的信息來模擬真正的世嘉VR頭顯。通過軟件來推測出世嘉VR的真實(shí)體驗(yàn),正是我的研究目標(biāo)。
世嘉VR這款產(chǎn)品公開時(shí),有多款為這一VR外設(shè)開發(fā)的游戲正在研發(fā)中,但由于世嘉VR取消了發(fā)售,這些游戲也隨之被埋入了歷史的塵埃。不過令人驚喜的是,來自 Gaming Alexandria的Dylan Mansfield努力聯(lián)系上了Futurescape Productions的聯(lián)合創(chuàng)始人Kenneth Hurley,他們曾為世嘉VR開發(fā)了一款名為《Nuclear Rush》的第一人稱動(dòng)作游戲。
《Nuclear Rush》的背景設(shè)定在2032年,化石即將枯竭,而世界對于電力的需求仍在不斷增長。玩家在游戲中需要進(jìn)入擁有老式核反應(yīng)堆的秘密區(qū)域,收集放射性燃料。由于畫面和背景較為相似,這款游戲常常被和另一款世嘉VR游戲《Iron Hammer》混淆。1993年的夏季CES上,《Nuclear Rush》放出了部分未加入最終版本HUD的游戲畫面,更加清晰的展示了游戲本身的內(nèi)容。
在Dylan聯(lián)系上Hurley并表明了自己對于世嘉VR的興趣后,Hurley拿出了一張1994年8月6日制作的CD-ROM光盤,這張光盤包含了《Nuclear Rush》的完整源碼,并且歷經(jīng)26年依然沒有任何的數(shù)據(jù)丟失。
這張光盤正是對世嘉VR探索的起點(diǎn)。我們首先要做的是編譯《Nuclear Rush》并讓其能夠正常運(yùn)行,然后通過這款游戲,在世嘉VR廢棄超過25年后的今天,重建世嘉歷史中值得被銘記的一頁。
構(gòu)建光盤源碼
盡管Hurley的光盤中包含了《Nuclear Rush》的完整源碼,卻沒有包含任何已經(jīng)編譯好的二進(jìn)制文件。為了能夠?qū)⒃创a很好的組織起來,Dylan找到了我。乍一看,我們已經(jīng)收集齊了編譯游戲所需的全部資料,包括所有的美術(shù)資源和大部分需要的工具,盡管其中有部分?jǐn)?shù)據(jù)來自光盤上另一款世嘉游戲《Monster Hunter(怪物獵人)》
大部分《Nuclear Rush》的游戲代碼是用C語言寫的,需要使用Sierra 68000 C語言編譯器進(jìn)行編譯,其中包含了一些樣板代碼(例如世嘉VR頭顯驅(qū)動(dòng))以及其他零碎的匯編代碼。游戲代碼編譯最耗時(shí)的部分是縮放工具(scaler),同樣用C語言編寫,但是進(jìn)行了一些改造。編譯器會按照需要根據(jù)C語言代碼生成本地M68K代碼,供游戲運(yùn)行時(shí)調(diào)用。
除標(biāo)準(zhǔn)的構(gòu)建(Build)工具之外,構(gòu)建過程還需要一些專有工具來提取美術(shù)資源數(shù)據(jù)并將其轉(zhuǎn)換成游戲直接使用的格式。例如CVTSCE.EXE用來提取靜態(tài)背景以及UI;ANM2FPA用來提取可選RLE的縮放化精靈和動(dòng)畫;最后LZSSC.EXE用標(biāo)準(zhǔn)的LZSS壓縮算法(12-bit偏移,4-bit長度)來壓縮CVTSCE.EXE生成的資源。
在我首次嘗試構(gòu)建時(shí),發(fā)現(xiàn)有一個(gè)工具DUMP.EXE缺失了。查找該工具可能的作用時(shí),我發(fā)現(xiàn)它只是打開了一個(gè)文件,然后使用stdout逐個(gè)字節(jié)將信息寫入該文件中,以便匯編程序能夠讀取該二進(jìn)制文件。在花費(fèi)了寶貴的時(shí)間用Borland C ++ 3.0編寫一些代碼后,我生成了一個(gè)替換的可執(zhí)行文件,最終成功完成了源碼的構(gòu)建。
源碼構(gòu)建成功后生成了一個(gè)COFF格式文件,但游戲ROM最重要的部分被分成了數(shù)個(gè)額外的文件。我希望在相同的MS-DOS環(huán)境下將這些文件打包成可運(yùn)行的游戲ROM,因此向好友Borland尋求幫助讓他幫忙寫了另一個(gè)程序來分析COFF文件,從而生成正確包含其他文件的二進(jìn)制ROM,并在該ROM的頭部寫入校驗(yàn)數(shù)據(jù)。
首次運(yùn)行ROM文件
當(dāng)我在MD模擬器中首次運(yùn)行構(gòu)建出的ROM文件時(shí),模擬器的歡迎畫面讓我非常開心:至少游戲已經(jīng)進(jìn)入了正常的執(zhí)行步驟。
但顯而易見的是,我并沒有頭部追蹤器。游戲成功識別了頭部追蹤器的缺失,進(jìn)入了非VR的運(yùn)行模式。隨著背景音樂的播放,游戲似乎運(yùn)行的非常正常,直到我按下開始按鈕并進(jìn)入主菜單。菜單字體使用的調(diào)色板似乎損壞了,所有的菜單文字都很難辨認(rèn)。我嘗試著加載游戲的第一關(guān),但程序計(jì)數(shù)器開始報(bào)錯(cuò),整個(gè)程序崩潰了。
像往常一樣,Debug的時(shí)間到了。我從COFF文件中轉(zhuǎn)儲(dump)出了全部的Debug符號,然后使用這些符號的地址加入了一些新的斷點(diǎn),來查看關(guān)卡加載出錯(cuò)前程序都做了寫什么。
經(jīng)過一段時(shí)間的反復(fù)嘗試,我發(fā)現(xiàn)游戲成功的加載了第一關(guān),甚至在崩潰前還顯示了1-2幀的畫面,然后縮放器為一個(gè)精靈設(shè)置了一個(gè)錯(cuò)誤的偏移,并造成了后續(xù)一系列的程序錯(cuò)誤。前文提到過,我從另一個(gè)游戲的數(shù)據(jù)中提取了一些工具,這就是問題的所在。這些工具可能在完成《Nuclear Rush》后更改了部分代碼,最終我在縮放器主入口代碼前幾行發(fā)現(xiàn)了導(dǎo)致問題的代碼:
后兩行指針的累加是為了跳過一些完全無用的數(shù)據(jù),而這些數(shù)據(jù)不知什么時(shí)候似乎已經(jīng)從ANM2FPA.EXE的輸出中被移除了。既然這些數(shù)據(jù)已經(jīng)完全被移除,我只需要?jiǎng)h除這兩行指針累加的命令即可。
完成改動(dòng)后,關(guān)卡終于能正常加載了,但背景中一些圖塊和一些HUD精靈依然顯示為亂碼。這看起來應(yīng)該和之前是同樣的問題,由于《怪物獵人》游戲的數(shù)據(jù)格式發(fā)生了改動(dòng)導(dǎo)致工具代碼發(fā)生變化,只不過沒有像之前的代碼那樣造成災(zāi)難性的后果。最終在一個(gè)忙碌的周六,我排查解決了之前遇到的問題并重新構(gòu)建了一版正確的ROM,最終成功進(jìn)入了游戲的關(guān)卡中,享受我的首次《Nuclear Rush》之旅。
尋找游戲源碼BUG
當(dāng)游戲能夠正常運(yùn)行后,我還需要回頭去解決一些之前碰到的其他BUG。我并不知道到底有多少BUG是由于工具的混用引起的,有多少是游戲“最終”版本的遺留BUG。這里的“最終”指的不是最終零售版本,盡管游戲已經(jīng)有效的完成了開發(fā),但顯然沒有經(jīng)歷過經(jīng)典的debug過程來保證游戲的質(zhì)量。
根據(jù)整個(gè)代碼中出現(xiàn)的少量WCES94預(yù)處理程序檢查,我推測《Nuclear Rush》原本計(jì)劃在1994年冬季CES上再次進(jìn)行展示。而Kenneth1994年8月6日刻錄的CD-ROM盡管包含了游戲接近開發(fā)完成時(shí)的源代碼,但顯然還不是能夠作為商業(yè)發(fā)行的版本。
在整個(gè)游戲中泛濫的調(diào)色板錯(cuò)誤很可能還是和構(gòu)建時(shí)用到的工具集有關(guān),這是一個(gè)很有意思的問題。CVTSCE.EXE文件很可能在《Nuclear Rush》開發(fā)完成后也被修改,同時(shí)在文章前半部分的圖片中你也能夠看到,CVTSCE.EXE打包資源其實(shí)還有很多參數(shù)可以進(jìn)行設(shè)置,這些參數(shù)影響了資源打包成為二進(jìn)制文件的方式。調(diào)色板的錯(cuò)誤可能和CVTSCE.EXE的默認(rèn)參數(shù)有關(guān),但還存在著更深層次的問題。
CVTSCE.EXE提取的許多基于圖塊的源圖像都用到了多個(gè)調(diào)色板,數(shù)量和偏移也不盡相同。例如,一幅圖像可能使用調(diào)色板1和調(diào)色板2,而另一幅圖像可能僅使用調(diào)色板0,依此類推。CVTSCE.EXE輸出的文件格式旨在為文件中的第一個(gè)調(diào)色板指定索引,并設(shè)定調(diào)色板的數(shù)量。加載圖像后,它將使用該索引和計(jì)數(shù)來確定當(dāng)前使用的調(diào)色板并將其加載到CRAM中。這里的問題是調(diào)色板索引/計(jì)數(shù)的默認(rèn)值對《Nuclear Rush》中的所有圖像來說可能都是錯(cuò)誤的,而且我沒有看到任何文件數(shù)據(jù)幫助說明工具針對目錄之外的內(nèi)容使用時(shí)如何進(jìn)行設(shè)置。
最終我不得不修改CVTSCE.EXE工具。我添加了一個(gè)選項(xiàng)來掃描圖塊地圖,找出圖塊引用的調(diào)色板,并使用該數(shù)據(jù)確定調(diào)色板范圍和要輸出的計(jì)數(shù)。我不知道這一工具最初如何處理《Nuclear Rush》的圖塊資源,但按照我目前的方法進(jìn)行處理已經(jīng)不再出現(xiàn)問題。
在硬件上進(jìn)行實(shí)機(jī)測試時(shí),我還遇到了一個(gè)大問題?!禢uclear Rush》將畫面的水平滾動(dòng)與基于滾動(dòng)表的垂直滾動(dòng)相結(jié)合,較新的MD主機(jī)型號可以通過這種方式正常處理畫面的滾動(dòng),但是較舊的機(jī)型可能會導(dǎo)致背景的左側(cè)出現(xiàn)空白列。發(fā)生這種情況時(shí),游戲畫面看起來像這樣:
《Nuclear Rush》會嘗試通過檢查硬件的版本(寄存器$ A10001)來避免此問題,如果版本檢查返回0,程序會強(qiáng)制截?cái)嗨綕L動(dòng),這意味著實(shí)際上畫面背景一次只能滾動(dòng)一個(gè)圖塊,避免了空白列的問題。但這樣就造成了畫面的水平滾動(dòng)十分不平滑,給玩家的感覺很遲鈍。由于該問題在特定MD機(jī)型上的出現(xiàn)毫無征兆,因此僅僅截?cái)嗨綕L動(dòng)顯然不是最理想的解決方案。我碰巧有一臺第二版的MD主機(jī),在進(jìn)行版本檢測時(shí)返回值大于0,但后續(xù)的MD主機(jī)才進(jìn)行了VDP(Video Display Processor)的改進(jìn)從而解決了這一問題,因此我在自己的MD上運(yùn)行游戲時(shí)也碰到了背景出現(xiàn)空白列的狀況。
《Nuclear Rush》利用垂直滾動(dòng)表來實(shí)現(xiàn)圍繞Z軸的旋轉(zhuǎn)(Roll)效果。通過為滾動(dòng)表中的每個(gè)列條目累積一個(gè)小的偏移量,并通過應(yīng)用對象所屬列的偏移量來補(bǔ)償Sprite的位置,可以達(dá)到下圖中的顯示效果:
相比對程序進(jìn)行大的改動(dòng),我通過加入在菜單中加入一個(gè)選項(xiàng)進(jìn)行了簡單的修復(fù)。該選項(xiàng)設(shè)置游戲畫面的四種渲染模式,第一種模式會禁用垂直滾動(dòng)表(允許在較舊的硬件版本上進(jìn)行平滑滾動(dòng)),第二種模式會強(qiáng)制進(jìn)行平滑滾動(dòng)(即使因?yàn)橛布恢С謺?dǎo)致空白列的出現(xiàn)),第三種模式則會強(qiáng)制截?cái)嗨綕L動(dòng),第四種模式模式將恢復(fù)原始的硬件版本檢查。該選項(xiàng)不是萬能的,但可以改進(jìn)游戲在第二版MD主機(jī)上的運(yùn)行情況,也能夠適配后續(xù)更新型號的MD主機(jī)。
之后,我還對游戲進(jìn)行了少量其他的debug工作直到對游戲狀態(tài)感到滿意為止,其中包括了修復(fù)DMA計(jì)時(shí)錯(cuò)誤和一些崩潰,使輸入密碼的功能再次生效,并且修復(fù)了一堆僅在游戲以立體模式運(yùn)行時(shí)出現(xiàn)的問題。
通過模擬器模擬世嘉VR
工作終于進(jìn)行到模擬世嘉VR頭顯這一步了。這也是最讓我興奮的一步。如果我只有一個(gè)預(yù)先構(gòu)建的ROM鏡像而不是源代碼,我可能還需要進(jìn)行大量的拆裝篩選,以找出游戲想要從VR頭顯中獲得什么樣的數(shù)據(jù)。但是,有了源代碼,這一過程就容易得多了。
《Nuclear Rush》的源代碼有兩個(gè)文件可供參考世嘉VR頭顯的通信數(shù)據(jù)。HEADSET.ASM是由Sega of America提供的原始驅(qū)動(dòng)程序源代碼,而VRDRV.ASM是《Nuclear Rush》基于原始驅(qū)動(dòng)修改后的版本,兩套源代碼處理VR頭顯數(shù)據(jù)的核心方法是相同的。驅(qū)動(dòng)程序源碼還引用了“VR.DOC/VR.TXT”,其中可能包含關(guān)于接入VR頭顯的一些有趣的技術(shù)信息,但不幸的是,該文件是缺失的。不過我已經(jīng)有足夠多的信息可以僅從源代碼來讓VR功能正常運(yùn)行起來。
讀取VR頭顯數(shù)據(jù)是通過多路復(fù)用方案進(jìn)行的,就像普通的世嘉MD手柄一樣。實(shí)際上,與VR頭顯的所有交互都是通過游戲手柄2號端口進(jìn)行的,驅(qū)動(dòng)程序會假定VR頭顯已經(jīng)正常接入游戲主機(jī)。
軟件方面,VR頭顯的初始化從握手開始。該軟件將TR和TH引腳都設(shè)置為通過寄存器$ A1000B寫入。與僅將TH引腳設(shè)置為寫入的標(biāo)準(zhǔn)手柄不同,這種設(shè)置讓我們通過兩位(2 Bits)來區(qū)分通過寄存器$ A10005向VR頭顯發(fā)送的不同命令類型。例如發(fā)送$60(TR+TH)給VR頭顯表示讓頭顯進(jìn)入閑置模式,$ 40(TH)則讓VR頭顯進(jìn)行復(fù)位。我正是利用了復(fù)位命令來讓世嘉MD模擬器進(jìn)入VR模式。當(dāng)發(fā)出閑置模式的命令后,軟件會觸發(fā)$ 20(TR),以推進(jìn)可從$ A10005讀取的數(shù)據(jù)。軟件空閑命令后會立即讀取$ 70作為響應(yīng)的確認(rèn),并驗(yàn)證TH,TR和TL是否按預(yù)期設(shè)置。軟件確認(rèn)之后會發(fā)送VR頭顯標(biāo)識$ 08,$ 00,并將其后半部分作為保留數(shù)據(jù)。如果初始化過程中的任何部分沒有按預(yù)期進(jìn)行,驅(qū)動(dòng)程序就會認(rèn)為初始化出錯(cuò),然后游戲就會以非VR模式運(yùn)行。
▲模擬器VR模式初始化成功
初始握手完成后,程序?qū)㈨樞蜃x取所有VR頭顯數(shù)據(jù),請求當(dāng)前的偏航角(Yaw),俯仰角(Pitch)和左/右眼的位數(shù)據(jù)。Z軸旋轉(zhuǎn)數(shù)據(jù)的缺失意味著世嘉VR頭顯只能用于跟蹤兩個(gè)旋轉(zhuǎn)軸。同時(shí),我不知道程序會從IMU讀取怎樣的位置漂移數(shù)據(jù),也不知道在累計(jì)角度運(yùn)動(dòng)之前應(yīng)用了哪種濾波器,因?yàn)閂R頭顯需要向程序提供絕對坐標(biāo)和角度。這意味著游戲僅僅讀取了VR頭顯的輸出數(shù)據(jù)并按原樣應(yīng)用它,而不會對數(shù)據(jù)進(jìn)行其他處理。雖然這樣一來僅憑軟件我很難知道硬件內(nèi)部發(fā)生了什么,但是考慮到世嘉MD的處理能力時(shí),這種實(shí)現(xiàn)細(xì)節(jié)很有意義。
現(xiàn)在,我需要了解VR頭顯初始化后的數(shù)據(jù)流格式,幸運(yùn)的是,驅(qū)動(dòng)程序源碼的注釋很好地列出了所有內(nèi)容:
上圖是VR頭顯讀取功能返回的數(shù)據(jù)格式,x位是32位寄存器頭部的未使用位,其中包含了讀取函數(shù)返回時(shí)VR頭顯數(shù)據(jù)的累加值。
這樣的數(shù)據(jù)格式表示我們需要分別使用9Bit來表示VR頭顯的360度左右轉(zhuǎn)動(dòng)角度和60度的俯仰角度。L/R位指定VR頭顯下一步要掃描的眼睛。在我的實(shí)現(xiàn)中,我使用32位浮點(diǎn)值來儲存角度數(shù)據(jù),以保持敏感的高精度輸入。當(dāng)將角度返回給世嘉MD時(shí),我會首先對浮點(diǎn)數(shù)據(jù)取整然后進(jìn)行量化和編碼。
uint32_t encode_headset_angles(const float *pAngles)
{
//assumes angles have already been clamped
const uint32_t pitch = (pAngles[0] >= 0.0f) ? (uint8_t)pAngles[0] : ((uint8_t)-pAngles[0] ^ 0xFF) | (1 << 16);
const uint32_t yaw = (uint32_t)pAngles[1];
return pitch | ((yaw & 0xFF) << 8) | ((yaw & 0x100) << 9);
}
該函數(shù)的輸出將與雙眼位數(shù)據(jù)的模擬進(jìn)行結(jié)合,接下來的工作就是更有意思的顯示同步了。
《Nuclear Rush》如何進(jìn)行雙眼畫面渲染?
《Nuclear Rush》被設(shè)計(jì)為按照鎖定的15Hz幀率運(yùn)行,這意味著完整的游戲循環(huán)每秒將運(yùn)行15次。但是VBlank仍然會以60Hz的頻率進(jìn)行。
注:CRT顯示器通過將電子束發(fā)射到熒光屏上來顯示圖像。顯示畫面時(shí),電子束將按照從左到右、從上到下的順序來掃描整個(gè)屏幕從而顯示一幅完整的畫面。VBlank是指電子束從瞄準(zhǔn)屏幕右下角調(diào)整到重新瞄準(zhǔn)屏幕左上角的時(shí)間段。
在VBlank中斷結(jié)束時(shí),程序會讀取了VR頭顯的數(shù)據(jù),但在下一個(gè)VBlank開始時(shí),我們還要從前一個(gè)VBlank中讀取VR頭顯數(shù)據(jù),以確定要對哪只眼睛對應(yīng)的屏幕(可能會啟動(dòng)DMA)進(jìn)行掃描輸出畫面。因此盡管世嘉VR頭顯的驅(qū)動(dòng)程序源碼顯示其支持以60Hz運(yùn)行,但《Nuclear Rush》僅在每隔一個(gè)VBlank的末尾時(shí)讀取VR頭顯的數(shù)據(jù)。
如果你將世嘉MD連接到舊的NTSC顯示器時(shí),假設(shè)VDP不在隔行模式下運(yùn)行,它將以大約60Hz的頻率掃描生成新的畫面幀。通常,由于VR頭顯需要顯示雙眼的畫面,以60Hz的頻率生成新的畫面幀意味著每只眼的畫面都會以30Hz的頻率更新。但《Nuclear Rush》讀取VR頭顯數(shù)據(jù)并掃描幀的方式實(shí)在有點(diǎn)怪異。
《Nuclear Rush》輸出幀的方式:
1.進(jìn)入VBlank
?讀取上一幀VBlank時(shí)的頭顯數(shù)據(jù)。設(shè)置左眼位。
?設(shè)置左眼屏幕為下一次掃描目標(biāo)。
?VBlank結(jié)束時(shí)再次讀取頭顯數(shù)據(jù)。設(shè)置右眼位。
2.掃描左眼屏幕。
3.進(jìn)入VBlank
?讀取上一幀VBlank時(shí)的頭顯數(shù)據(jù)。右眼位已設(shè)置。
?設(shè)置右眼屏幕為下次掃描目標(biāo)。
?跳過頭顯數(shù)據(jù)讀取。這是一次異常的VBlank。
4.掃描右眼屏幕。
5.進(jìn)入VBlank
?讀取上一幀VBlank結(jié)束時(shí)的頭顯數(shù)據(jù)。右眼位已設(shè)置。上一個(gè)VBlank結(jié)束時(shí)跳過了頭顯數(shù)據(jù)的讀取。
?設(shè)置右眼屏幕為下次掃描目標(biāo)。
? VBlank結(jié)束時(shí)再次讀取頭顯數(shù)據(jù)。設(shè)置左眼位。
6.掃描右眼屏幕。
7.進(jìn)入VBlank
?讀取上一幀VBlank時(shí)的頭顯數(shù)據(jù)。左眼位已設(shè)置。
?設(shè)置左眼屏幕為下一次掃描目標(biāo)。
?VBlank結(jié)束時(shí)跳過讀取頭顯數(shù)據(jù)。又一次異常VBlank。
8.掃描左眼屏幕。
9.回到第1步重復(fù)整個(gè)循環(huán)。
所以按照上文的序列,《Nuclear Rush》實(shí)際上是按照“左、左、右、右”的順序來掃描顯示游戲畫面。以立體模式直接查看游戲輸出的畫面也證實(shí)了這一點(diǎn)。下圖從左到右展示的是游戲連續(xù)4幀的畫面,順序正是左左右右。
這樣的渲染方式令我感到奇怪,因?yàn)橥ǔ碚f世嘉VR頭顯在掃描顯示畫面時(shí)應(yīng)當(dāng)采用左右交替的順序,而且世嘉VR頭顯的硬件設(shè)計(jì)也不太可能圍繞讓每個(gè)顯示屏僅以15Hz幀率顯示特定畫面來而開展。唯一的解釋是,VR頭顯僅在讀取數(shù)據(jù)時(shí)會更改其顯示目標(biāo),這樣的方案也更加通用,無論我們每個(gè)VBlank結(jié)束時(shí)都讀取頭顯數(shù)據(jù)還是每隔一個(gè)VBlank才讀取頭顯數(shù)據(jù)都能夠正常工作。
世嘉VR頭顯期望游戲在每個(gè)VBlank結(jié)束時(shí)讀取自身數(shù)據(jù),并告訴游戲在下一個(gè)VBlank結(jié)束時(shí)期望為左眼還是右眼生成畫面。這是我對于世嘉VR頭顯畫面顯示方式的理論。而我在嘗試運(yùn)行《Nuclear Rush》時(shí)也成功實(shí)現(xiàn)了這種方式,僅在讀取VR頭顯數(shù)據(jù)后切換顯示目標(biāo),取得了很好的顯示效果。
同時(shí)我還嘗試著實(shí)現(xiàn)讓游戲輸出畫面立體化。為此我專門編寫了3D立體畫面的后處理代碼,該后處理在將顏色映射為亮度后會將左右眼的輸出組合在一起。我在模擬器配置中自定義了雙眼的顏色,并對當(dāng)時(shí)我使用的眼鏡做了改造。最終的成果看起來很漂亮:
但運(yùn)行游戲時(shí),我還是注意到了一些異常,在轉(zhuǎn)動(dòng)頭部時(shí),雙眼的畫面偶爾會不同步,這在3D立體模式下尤其明顯:
最初,我以為我的代碼出現(xiàn)了問題,重新進(jìn)行了調(diào)試。我連續(xù)截取了4幀畫面,并設(shè)法找到了畫面不同步的地方:
上圖4幀的渲染順序?yàn)樽笞笥矣?,但是你能夠明顯的看到右眼第二幀實(shí)際上前進(jìn)了很多距離。當(dāng)截取這些畫面時(shí),我在VR頭顯中向左轉(zhuǎn)動(dòng)了頭部,但這里游戲?qū)嶋H上在右眼的兩幀畫面之間向前移動(dòng)了很長的距離。
目前還沒有發(fā)現(xiàn)如何阻止這種情況發(fā)生,同時(shí)它也可能出現(xiàn)在任何時(shí)刻,甚至是左右眼畫面切換的時(shí)候。游戲在每一幀畫面后都會等待4次VBlank來保持15Hz幀率鎖定,但偶爾的卡頓可能導(dǎo)致意外出現(xiàn),游戲可能在未與VR頭顯同步的情況下就設(shè)置了下一次為哪只眼睛生成畫面。這一問題可能同樣會出現(xiàn)在真正的世嘉VR頭顯上。由于實(shí)際顯示畫面時(shí),世嘉VR頭顯主要依靠視覺的持久性來補(bǔ)償?shù)头直媛省⒌退⑿侣室约皶r(shí)序的限制,在其他狀況都比較糟糕的情況下,這一問題很可能不那么明顯,甚至不能被稱為問題。
最終我在游戲源代碼中添加了一些內(nèi)容,讓游戲每次循環(huán)結(jié)束時(shí)都會和頭顯進(jìn)行同步,解決了這一問題。我很好奇這樣做會不會真的能改善世嘉VR頭顯的體驗(yàn),還是說每個(gè)VBlank結(jié)束時(shí)對VR頭顯進(jìn)行采樣會帶來額外的開銷,但這一問題可能永遠(yuǎn)都沒法得出答案。
全新的世嘉VR體驗(yàn)
既然游戲已經(jīng)能夠顯示3D影像,我也可以通過搖桿來模擬VR頭顯的轉(zhuǎn)動(dòng),自然而然我開始想真正的把《Nuclear Rush》搬到VR中。
我制作了一個(gè)特別版本的《Nuclear Rush》,并進(jìn)行了一些修復(fù)使游戲在非立體模式下能夠以30Hz運(yùn)行,更改了每個(gè)VBlank上VR頭顯的采樣,并啟用了上一章節(jié)中提到的修復(fù)以使VR頭顯能夠和游戲畫面同步。在立體聲模式下,VR頭顯也可以始終保持15Hz的幀率。為了增強(qiáng)這一特殊的游戲版本,我還為世嘉VR模擬器添加了一個(gè)選項(xiàng),實(shí)現(xiàn)了模擬器對M68K的超頻。
盡管以30Hz刷新率運(yùn)行會導(dǎo)致游戲邏輯的實(shí)際運(yùn)行頻率比最初高出兩倍,游戲的感覺仍然不錯(cuò)。另一方面,由于擺脫了ROM不能超過2MB大小的限制,我還能夠通過靜態(tài)的擴(kuò)展更多精靈數(shù)據(jù),并加入針對性優(yōu)化。
既然游戲準(zhǔn)備好在要求更高的VR環(huán)境中運(yùn)行,現(xiàn)在我應(yīng)當(dāng)將重點(diǎn)轉(zhuǎn)移到復(fù)原世嘉VR體驗(yàn)上了。我的下一步工作將是在模擬器中添加對現(xiàn)代VR頭顯的支持。我基于OpenVR編寫了MD模擬器對于HTC Vive Cosmos的支持。
要實(shí)現(xiàn)模擬器對于VR的支持,主要工作在于從模擬器獲取立體的畫面輸出并直接傳輸?shù)絍R頭顯中。這看起來似乎很簡單,但并不是一個(gè)普通的用例,還有一些特殊事項(xiàng)需要注意。我必須對模擬器提供的“原始”正交投影畫面進(jìn)行處理,來適應(yīng)眼睛/鏡頭的畸變。我還必須去掉OpenVR庫中全部的運(yùn)動(dòng)平滑或插幀算法,這意味著要在維持VR頭顯內(nèi)部刷新率的同時(shí),保證MD模擬器鎖定60Hz的輸出。
當(dāng)我解決了OpenVR的時(shí)序問題后,還要處理更多世嘉VR時(shí)序問題,世嘉VR頭顯似乎并未嘗試對眼前的屏幕進(jìn)行同步。為了能夠在不對ROM進(jìn)行任何更改的情況下正確運(yùn)行《Nuclear Rush》,我在模擬器中添加了一個(gè)名為dgen_openvr_eyes_sync的選項(xiàng)。如果啟用此選項(xiàng),并嘗試運(yùn)行未修復(fù)同步問題的《Nuclear Rush》版本,會經(jīng)常發(fā)現(xiàn)自己變成了斗雞眼。這實(shí)際上就是前文提到的問題:游戲本身并沒有什么機(jī)制來阻止雙眼顯示畫面不同步。
為了盡量逼真的再現(xiàn)世嘉VR頭顯體驗(yàn),我會在雙眼畫面掃描生成后立刻更新緩沖區(qū)數(shù)據(jù),也正是因?yàn)殡p眼的畫面全部都會立即傳輸給VR頭顯,如果這些畫面并不是來自游戲的同一幀,就會導(dǎo)致畫面閃爍的情況發(fā)生。
不過只要你打開模擬器的同步選項(xiàng)并運(yùn)行游戲的同步版本,顯示就會變得一切正常!
畫面的長寬比和視角是另外兩個(gè)要處理的重要問題。默認(rèn)情況下,我會處理每只眼睛的圖像,將其調(diào)整為適合MD的寬高比,但是玩家也可以禁用默認(rèn)設(shè)置并將其更改為所需的任何相對寬高比例。
由于我并不了解世嘉VR頭顯所用屏幕或鏡片的確切物理參數(shù),因此我只能猜測世嘉VR頭顯的有效視野,失真數(shù)據(jù)。我向模擬器添加了一個(gè)名為dgen_openvr_imgpscale的選項(xiàng),該選項(xiàng)可用于更改人眼投影空間中圖像的大小。
之后,我為模擬器添加了自定義鏡片形狀的功能。通過Noesis腳本,玩家可以將最常見的模型格式文件導(dǎo)入作為鏡片的模型數(shù)據(jù),然后模擬器會將該數(shù)據(jù)用在人眼投影空間中渲染全部的畫面。上圖是對魚眼鏡頭測試,也圖中顯示了鏡片的模型。
這種方法使我們能夠在數(shù)學(xué)上生成我們想要的任何透鏡形狀而不必考慮模型的計(jì)算成本。除了自定義鏡片外,還可以使用選項(xiàng)dgen_openvr_bilinear啟用雙線性過濾。盡管我通常不喜歡在模擬器中使用雙線性濾波進(jìn)行畫面縮放,但它在這種情況下可以幫助減輕迷失感。
到這里為止,我們終于可以使用一臺現(xiàn)代VR頭顯來模擬世嘉VR頭顯的體驗(yàn)了,還開發(fā)了一整套用于微調(diào)的工具。盡管這樣的模擬無法做到100%還原,但這確實(shí)非常接近世嘉VR的體驗(yàn)。
刷新率僅15Hz的VR游戲可玩性卻比我預(yù)期的要好得多,雖然視角轉(zhuǎn)動(dòng)有所限制,但也不會太令人討厭。這種體驗(yàn)不是所有人都能夠接受,不過我在其中度過了一段愉快的時(shí)光!即使游戲本身的采樣率有限,我們也可以從低延遲的頭部跟蹤中受益匪淺。
從這里開始,我們還可以繼續(xù)進(jìn)行更多有趣的后續(xù)開發(fā)。我可能會嘗試基于現(xiàn)有的世嘉VR代碼來增加對房間規(guī)模跟蹤的支持,或創(chuàng)建一個(gè)接口用來在真實(shí)的硬件上模擬世嘉VR頭顯。目前的模擬相較真實(shí)的世嘉VR頭顯也仍然還有很大的提升空間,通過調(diào)整不同的視角和運(yùn)動(dòng)濾鏡,我們可能得到更逼近真實(shí)的體驗(yàn)。
源代碼和下載
我為這個(gè)項(xiàng)目在GitHub上創(chuàng)建了兩個(gè)倉庫,你可以在其中找到源代碼和二進(jìn)制文件。
支持世嘉VR的模擬器
《Nuclear Rush》可直接構(gòu)建的源代碼和所有必需的工具
上面的游戲ROM可以在大多數(shù)模擬器或MD實(shí)機(jī)上運(yùn)行,但必須使用提供的模擬器才能獲得真正的世嘉VR體驗(yàn)。該模擬器還增加了很多選項(xiàng),你可以仔細(xì)閱讀配置文件來探索更多模擬器配置。你可能還需要對配置進(jìn)行一些更改才能選擇匹配VR頭顯的旋轉(zhuǎn)參數(shù)。
如果您想要使用自己的VR頭顯進(jìn)行體驗(yàn),可以在這方面進(jìn)行多種調(diào)整嘗試。如果你發(fā)現(xiàn)自己感到迷失方向或在默認(rèn)設(shè)置下很難跟蹤運(yùn)動(dòng),可以嘗試減小int_openvr_imgpscale的值,直到感覺VR體驗(yàn)更舒服為止。
【93913原創(chuàng)內(nèi)容,轉(zhuǎn)載請注明及回鏈】
]]>