作者歸檔:szszss

OpenShader月(nian)報 #2

咕咕,咕咕咕
(ge le, qi keng le)


時隔了很久之後...終於有了第三篇月報,在上一篇中我說到5月要肝畢設,那麼,之後的時間? 之後的時間確實沒有認真肝OpenShader,有很長一段時間在摸魚,所以這段時間的進度怎麼看都不像半年時間該做出來的...

  • Shadow Caster的裁剪
    在之前,陰影渲染也佔據了不少性能開銷,就像以前我提到在早期版本測試時使用3個陰影圖的CSM時性能就急劇下降了,雖然池化VBO有效降低了渲染時的Drawcall開銷,但在MC準備渲染數據時的開銷依然不少,因此最好的辦法就是在Shadow Caster中也進行一次裁剪,去除掉不能對玩家的視角形成陰影的物體和地形.

    關於裁剪體的計算,先將玩家鏡頭的視錐體朝向光源鏡頭的近裁面做一次投影,在投影得到的二維圖形中找出最外層的邊,每一個邊沿着光源鏡頭的視線方向延伸得到面後,這些面就成了構成裁剪體的裁面.在這個裁剪體範圍之內的物體大部分都能對玩家視角內行成陰影.如果說還有什麼不完善的話,主要是剛才得到的裁剪體不包括近裁面和遠裁面,對太陽光來說近裁面是沒有必要的,而不計算遠裁面就導致了一些本來不會貢獻陰影的物體也被渲染了,會增加一些不必要的開銷,一個未來的改進是選擇玩家鏡頭的視錐體中,正面朝向光源的那些裁剪面,用這些裁面再做一次裁剪,就能去除掉那些false positive了.


    舉例,比如如果這張圖是一個渲染場景,中間高亮的橙色椎體是玩家鏡頭的視錐體,左上方為環境光源及其方向,圓錐、圓柱、稜角球和方塊分別是4個可渲染的物體,那麼在無Shadow Caster的裁剪時,這4個物體全部需要渲染.

    使用現在的視錐構建方法時,圓柱被剔除掉了,因為它即不在玩家視線內,也不會給玩家視線內的物體貢獻陰影,然而方塊仍然會被渲染,儘管它不會貢獻陰影.

    最終目標是製作一個這樣的裁剪體,保留下的物體只有圓錐 - 它會給玩家視線內貢獻陰影,以及稜角球 - 它在玩家視線內.


  • 重新設計的多線程渲染流程
    顯然,在OpenGL中不會有真正意義上的高性能多線程渲染(瘋狂切context確實可以多線程,但它不夠高性能),因此我們設計的多線程渲染流程也和其他的同類產品一樣,努力壓榨出更多的可在CPU端並行處理的東西,比如裁剪、收集渲染信息等,然後將它們分派到子線程上執行,主線程只保留最必要的工作:執行渲染指令.
    那麼,我們先來看看上次月報(17年5月,應該叫半年報了?)時的渲染流程:


    在舊的渲染流程中,整個渲染被分為準備階段和渲染階段,在準備階段程序會收集並配置渲染信息,主要工作分為鏡頭(計算每一個攝像機的位置和MVP矩陣)、地形(統計哪些RenderChunk需要被渲染)和實體(統計哪些實體和TileEntity需要被渲染)這三部分,此三部分有順序依賴,不可調換順序(地形需要鏡頭的信息來進行視錐裁剪,實體需要知道哪些區塊要被渲染). 在舊設計中這三部分都會在準備階段進行,儘管主線程在處理到每個部分時都會將工作分發給工作線程來進行,從而實現了並行化,但這裡還有一個問題 - 主線程被完全浪費了,事實上渲染階段其實無需等待準備工作全部完成時才進行,有些渲染操作並不涉及到地形和實體,比如天空背景和雲朵;此外渲染階段多線程的利用率非常低 - 只用到了兩個線程. 因此我們重構的目標時儘可能縮短準備階段,並盡量提高渲染階段的多線程化程度,最終重新設計的渲染流程是這樣的:

    在新的渲染流程中,只有鏡頭配置必須在準備階段進行,其餘的工作都是在渲染階段進行,在啟動渲染階段時會有三個線程開始工作,主線程等待並處理渲染指令,一個工作線程生成渲染指令,另一個工作線程會串行處理地形的可見性判斷,為什麼這一步要單線程處理? 這涉及到它的工作方式,地形的可見性判斷是用來找出哪些RenderChunk需要被渲染,主要的手段一是視錐裁剪,二是以玩家所在的RenderChunk為起點做一次廣度優先搜索,為了防止搜索時重複搜索已處理過的節點,MC會在RenderChunk中標記一個時間戳,每次搜索到節點時都會嘗試更新時間戳,如果更新成功(節點時戳比系統時戳舊)那麼這個RenderChunk就是未被處理過的,如果更新失敗(節點時戳與系統時戳一致)那麼這個節點就是已經被處理過的節點,在多遍渲染時程序需要多次進行可見性判斷,比如在渲染陰影時,需要有一個pass處理ShadowMap繪製的地形,一個pass處理玩家視角繪製的地形,如果想在此步驟並行處理的話,就需要把時戳換成一個原子整數,然後為每個pass分配一個bitflag,當此pass處理完一個節點時,就給那個原子整數標記上bitflag,顯然,當所有的pass處理完畢之後還得再將那個原子整數清零,在舊的渲染流程中我就是這樣做的,其結果呢? 嗯...確實是並行化了,但是在性能測試中發現更新原子變量的操作非常的慢,特別是最後重新清零的那個步驟,相比之下,之前串行處理時就不需要這個步驟,因為時戳始終是遞增的,在處理完一個pass後,只要將系統時戳再喜+1就能繼續處理下一個pass. 因此再三斟酌後,我將這一步換回了由一個線程串行處理所有pass的可見性判斷. 但是之後的工作依然是並行處理,每當一個pass完成可見性判斷後,程序就會再給線程池分配兩個新任務 - 生成這個pass的地形渲染指令,以及準備實體的渲染信息.

  • 幀緩衝的Pingpong
    以前我在Shadersmod教程中提到過不要在一個着色器中對一個RenderTarget即讀即寫,因為在Texture Barrier出現之前對一個紋理在讀出的同時進行寫入屬於未定義的行為,其結果是不可預料的,因此一般採用的策略是為一個需要即讀即寫的RenderTarget準備兩個紋理,一個紋理作為讀紋理,一個作為寫紋理,每次運行完渲染後交換兩個紋理的職責,原本讀的下次變成寫的,原本寫的下次變成讀的,這個方法被稱為Pingpong. Shadersmod沒有提供Pingpong功能,因此我們只能自行迴避即讀即寫操作(其實迴避的是異處讀寫,在片元着色器中原地讀寫的話在大部分顯卡上實際上是允許的). 而OpenShader提供了Pingpong功能,在默認配置下程序會自動檢測每個stage的幀緩衝和綁定的紋理,如果檢測到一個紋理即被綁定到輸入又被綁定到輸出的話,就會自動啟用Pingpong.

  • 圖像統計 (WIP)
    圖像統計之前我們已經介紹過了,主要是給像HDR之類的後處理特效用,不過...現在的OpenShader的圖像統計功能非常菜雞,僅僅只是達到了Shadersmod的水平,換句話說,就是只提供了獲取屏幕中央深度的功能... 這個功能其實很雞肋,想知道深度緩衝中央的值,直接給我texture(gdepth, vec(0.5, 0.5)).r不就得了嘛! 將這個值做成一個uniform除了卡流水線以外沒任何用處,儘管中央深度可以通過將其替換為紋理採樣來規避(怎麼替換將是下一期月報的內容(豹笑)),但像統計全屏幕平均亮度這樣的操作就只能靠實打實的圖像統計來實現了,因此圖像統計這方面還有不少工作要做...

  • RenderChunk渲染參數緩存 (WIP)
    這個...應該是最讓我崩潰的部分,大部分功能在去年8月末就完成了,但由於兩個折磨人的Bug導致它一直到現在都沒徹底完工.

    簡單地說,在之前準備渲染參數時需要程序逐一獲取每一個RenderChunk的參數,這涉及到了大量的內存訪問,以及隨之而來的cache miss,即使是原本很簡單的操作也會花費大量的時間,渲染參數緩存就是提前將這些參數緩存在一個整數數組中,將所有操作都簡化成數組的複製和整數操作 -- 利用了OpenGL所有對象都以整數句柄的形式存在的特點. 比如渲染一個池化的RenderChunk時需要知道它在哪個VBO中、它的首地址偏移量和頂點數量,這些參數都可以緩存在整數數組中.

    這個優化被證明為是成功的,確實能提高不少性能,然而卻帶來了兩個新bug.

    第一個bug是當玩家向任意一個方向移動一定距離時,就會發現反方向消失的區塊會在面前出現...事先說明,這個bug已經被解決了,某天在我肝了幾宿HOI4後決定再看看能不能解決這個讓我崩潰了兩個多月的bug,結果在靈光一現後(我一直篤信debug的時候靈感是最重要的 233)幾個騷斷點找出原因了,原因是我對MC自帶的RenderChunk表(我對它的稱呼,MCP把那個類稱為ViewFrustum...咳)的理解不充分,我原本以為它會完整地利用所有空間,也就是表中每一項都會是一個可見的RenderChunk,然而實際上MC並沒有充分利用每一個空間,在某些情況下,比如遊戲載入之後玩家向某一個方向持續移動時,會出現某些項中的RenderChunk存在但卻不該被渲染的情況. 原版MC中通過一個RenderChunk中的標記來判斷是否該被渲染,而我在編寫渲染參數緩存時沒能正確緩存這個屬性,結果導致了這個問題.

    遠處那一排非常突兀的地形就是bug所導致的情況,實際上它們應該是在我背後過來的地方的某排區塊.

    而第二個bug...則把我困惑到懷疑人生. 簡單地說,在設計上,當內存池發生變化時,變化的內容(VBO的id、指針偏移量、數據長度)會被立刻刷新入緩存,然而現在存在的問題是在罕見的情況下緩存和實際內容會有一幀的不同步,結果導致因使用了錯誤的參數而渲染出錯.

    出錯的一幀的截屏

    這個bug一直到現在我都沒能解決...最近倒是想出了一個workaround,暫時還沒有實現,看看下一次更新前能不能做完吧 (鴿?)

  • 一些...雜七雜八的東西
    因為沒有changelog,所以更新了什麼東西都是靠翻git的變更記錄來一點點查的(笑),主要是修bug和一些優化,其實現在OpenShader的狀態是三十六拜後就差一哆嗦,然而這一哆嗦就是幾個月都沒哆嗦出來 ? 這段時間一直都在忙別的,看看這倆月能不能把最後一點肝出來吧,下一篇日誌我們會介紹一些native方面的工作--
閱讀全文 [...]

FGOW與FMM停止維護

準確說這並不意外,最近幾個月如果你使用過FGOW和FMM鏡像的話,肯定已經察覺到各種問題諸如無法下載之類的,事實上,FMM最後一次維護是在今年3月左右;而FGOW? 我都忘了上一次更新FGOW是什麼時候了...

FGOW(ForgeGradle on Wall)與FMM(Forge Maven Mirror)開發於2014年5月,前者是用於解決一些ForgeGradle的缺陷,比如無法設置Maven源和其他必要文件的下載鏈接,無法像MCP那樣將MC源碼部署到開發目錄;後者是一個非官方的Forge Maven源,搭建在SAE上. 這兩個的組合曾經是很有效的,那麼為什麼突然就藥丸了呢?
  • FGOW最初的設計並沒有什麼可擴展性,只是一個編碼粗暴的針對FG1.1的小工具,而現在FG已經更新到了2.3...在許許多多的底層變動後,要更新FGOW可能跟重寫一遍差不多了...
  • FMM搭建在SAE上,用過SAE的都知道它有一些坑爹限制,比如FetchUrl的8MB大小限制,最初這個問題不嚴重,畢竟鮮有哪個庫能超過8MB,但後來FG打包附帶了原本要獨立下載的第三方庫,體積直接猛增到了13MB,SAE的文檔也誠不欺我,說8MB上限就是8MB上限,一字節也不帶多下的,所以FMM的大文件(準確的說,只有FG的各個快照)一直都是在後台手動更新的...
  • 缺乏存在的意義,FGOW除了重設下載鏈接外,還有個重要功能是部署MC源碼,然而後來有小夥伴指出其實可以直接在項目中建一個同包同名同內容的類,這樣根據類加載順序,實際載入遊戲中的是項目中可供我們隨意修改的那個類,由此一來,FGOW的那個功能就沒什麼用了.
  • 同上,既然FMM沒了,那麼FGOW也卵用了,思前想後,我覺得還是掛代理能一勞永逸地解決所有問題...而zzzz正好又提供了一個公用的SS賬號用來構建Forge工作目錄,所以我就可以棄坑了 (逃)
  • 缺乏足夠的精力去繼續維護--- 呸,直說吧,就是我太懶了 ?
  • SAE是要花錢的,當初FMM剛剛上線時SAE的價格還很實惠,我2012年註冊時送的2000雲豆能用十幾年,然而自從SAE改了Mysql收費政策後,大概1000雲豆只夠用10天...當然這並不是主要原因,一個月30塊錢還是氪的起的.
  • 並沒有大量的關於紫sama藍sama幽幽sama覺sama戀sama和瑪艾露貝莉x蓮子的福利!
那麼現在FGOW和FMM沒了,又該如何配置開發環境呢,之前提到了zzzz搭建了一個公共SS賬號,賬號在他的MCMod教程中(https://fmltutor.ustc-zzzz.net/1.1-配置你的工作環境.html). SS我覺得應該是現在碼農們常備的"工具"了,假如你沒有的話...就去下一個吧! 但是不要用他的鏈接里的SSR,前幾天SSR的作者搞了個大新聞,被婊到刪庫退圈了 (滑稽) 閱讀全文 [...]

OpenShader月報 #1

TL;DR: 棄坑啦!!!

好吧這是開玩笑的,雖然沉寂了很長時間,怎麼看都像是"好難啊不做了"的樣子,但項目確實還在繼續開發中.本來說好1月或2月發一個預覽版本,然而實際上...實際上從1月初到3月初這段時間我基本沒怎麼碰OpenShader的代碼,一方面是家裡有事,另一方面當時我在摸魚兩個即興項目(還特么是兩個??),等想起來"喲我還有一個大坑沒填"時已經是3月中旬了,所以說現在寫一篇月報其實挺符合現實情況.

那麼,首先是關於之前開的那些坑填的狀況:

  • 池化VBO&MultiDraw (90%)
    池化VBO和MultiDraw已經基本完成了,目前現存的問題是內存佔用稍微有點大,似乎還存在內存顛簸的問題,不過這些都是可以優化的. 從表現來看,池化VBO是相當成功的,磚塊渲染的開銷一下子從之前的第一驟降到了第三第四,甚至低於天空渲染和雲渲染的開銷...(誰想得到天空渲染怎麼會有那麼大的開銷??),其實這也不意外,一個池化的VBO能承載16個Chunk(是的,16x256x16的那玩意,不是16x16x16的RenderChunk)的頂點數據,並用1次綁定加1到4次DrawCall(取決於這些Chunk的位置,大多數情況下是1~2次,極端情況會是4次)將它們全部渲染出來,在過去這需要16次綁定和16次DrawCall,而且實際情況會比這還要多 --- RenderChunk只有16x16x16,在山地之類的立體地形中渲染1個Chunk需要渲染多個RenderChunk --- 不過也有可能比這少,畢竟有些RenderChunk可能不可見.但無論如何,總體開銷都不可能比池化VBO+MultiDraw要低. 在實現VBO內存池時我遇到了個吔屎的問題,我忘了OpenGL中大部分DrawCall類指令的偏移量單位是頂點而不是字節...這意味着頂點大小不同的頂點數據不能存入同一個內存池,因為數據的偏移量必須是頂點大小的整倍數,否則在DrawCall中沒法指定偏移量,而頂點大小不同的數據存在一起時對齊會變得非常麻煩,因此最後我設計成使用多個內存池來存儲16個Chunk的數據,每個內存池只存儲特定一種大小的數據,問題解決了,雖然浪費的內存變得更多了... 不過還有一個小問題是現在KHR_Debug_Callback返回的Debug信息中經常會狂刷"Pixel-path performance warning: Pixel transfer is synchronized with 3D rendering." 主要發生在內存池擴容刪除舊緩存時,這是什麼鬼...
  • 指令隊列 (100%)
    指令隊列順利完工了,但是表現的卻不是很好,性能感覺提升了不到10%...而且Debug變得異常艱難,因為報錯後只知道是在哪種指令中出錯了,甚至都不知道是哪個Stage提交的哪個指令...不過也不意外,Nvidia在AZDO(Approaching Zero Driver Overhead)那篇presentation中提到他們用軟件實現的指令隊列的性能提升也只有14%. 現在只能期待後繼的優化中將儘可能多的工作塞到指令準備時並行執行了,畢竟目前渲染準備階段佔用的時間也挺多的,如果能砍掉這部分時間(無論是優化還是並行執行)那對性能也是有很大的提升.
  • Early-Clear (0% 蛤蛤蛤)
    這一個完全沒弄,主要是暫時沒有特別大的需求,但今後肯定會有的.

正在進行的工作/已完成的其他工作:

  • 清理臨時代碼 (90%)
    花了幾天清理了很多臨時代碼,主要是ASM那方面,之前在開發時遇到需要修改MC代碼的情況時,就把MC代碼複製到項目中的同名類里,利用Java類加載順序的特性來實現覆蓋原始代碼.現在用ASM徹底重寫了這部分了,為即將到來的開源做好了準備. 至於其他方面的臨時代碼(大量的TODO) 慢慢解決吧...
  • 採樣器 (100%)
    採樣器決定了着色器如何從紋理中獲取紋素,在過去(去年12月的版本)其實已經有採樣器的原型了,但是只能用來控制着色器讀取哪些紋理,而不能控制着色器怎麼讀取,現在開發者可以設置採樣器的參數了,包括紋理過濾、各向異性過濾、Wrap、Mipmap以及是否開啟比較模式.採樣器有什麼用呢? Shadersmod中啟動shadowHardwareFiltering其實就是開啟Shadow map的比較模式. 採樣器還有兩個版本的實現,一個是針對低版本的傳統模式,通過glTexParameter修改紋理的採樣參數來實現;另一個是針對擁有ARB_sampler_object擴展的高版本,通過採樣器對象來實現.
  • 自定義Uniform (50%)
    雖然OpenShader內置了不少Uniform參數,但畢竟總有我想不到的時候,因此總得有一種方法能讓光影包開發者自行設定Uniform,目前的解決方案是讓開發者可以添加一個自定義Uniform,然後監聽更新事件並手動更新Uniform內容,這樣的設計有兩個缺陷,一個是只有Mod式的光影包能寫代碼來處理事件監聽,另一個是MinecraftForge的居然要求事件總線的註冊要在Mod初始化中進行,否則就會拋一個警告信息...我在考慮要不要設計一種DSL讓開發者通過簡單的表達式來描述需要獲取的內容;或者利用Java的ScriptEngine內置JavaScript的特性,讓開發者寫JS腳本去獲取數據.不過這些都無法迴避一個問題,就是運行時Minecraft的字段都是混淆的...如何訪問這些被混淆的字段還是個問題.
  • 圖像統計 (25%)
    簡單地說,圖像統計是用來獲取渲染結果中的某些信息,比如平均亮度(用於HDR)或中央深度(用於DOF)什麼的,這個操作主要面臨的問題是如何將獲取到的結果再遞交回去,比如說將統計到的結果再注入到一個Uniform里,這是最簡單的方式,不過會涉及到CPU-GPU同步的停頓,對於這個問題,國產的開源引擎KlayGE的解決方案是使用計算着色器來統計亮度,然後將結果寫入一個紋理的固定位置(DX的左上角(0,0)),後續處理時就直接從此紋理的固定位置讀數據就行了,整個操作可以一氣呵成,無需CPU從GPU那裡讀回數據,頂多有必要的話加一個屏障保證HDR發生在亮度統計之後即可,寫入紋理在OpenGL中對應的操作是Image Load Store,不過假如讓我來實現這個的話,我可能會選擇SSBO,因為Image Load Store到4.2才進入核心擴展,而4.3就有了SSBO了... 嗯,然後你問沒有計算着色器的辣雞該怎麼辦? 把紋理慢慢讀回內存然後讓CPU慢慢跑吧,同步的停頓想着就感人...
  • 兼容Shadersmod光影包 (60%)
    聽上去像是一個很不可思議的事情,但考慮到OpenShader在設計時就是具有高度可配置性,那麼Shadersmod的光影包不就是一種特殊格式的OpenShader光影包嗎(笑),這個月我主要就是在弄這個,期間也找出並解決了OpenShader不少問題.目前的情況是已經能跑我在光影包教程中寫的那個MyFirstShader了(不過還存在一些小問題),至於別的...我試了一下SEUS,看上去能跑,但是總感覺有些差別,特別是炫光效果經常會閃爍,估計是紋理採樣參數有差別;體積雲也會有斷層,估計是噪音圖的Wrap沒有設置成REPEAT而導致;流體磚塊的Attribute注入也跪了,滑稽 23333
  • 無盡的Bug... (1%)
    還有很多問題需要修復,有些是缺乏邊界條件時的處理,正常使用沒問題,但如果使用姿勢稍有不對就 --- DUANG! 還有一些就是徹頭徹尾的Bug,比如原版MC的RenderChunk載入是使用一個優先隊列,距離玩家最近的未加載RenderChunk永遠是會被最優先處理的,這保證了玩家在移動時自己身旁的RenderChunk總是被加載出來的,而現在這個設定貌似壞掉了,變成了一個先入先出隊列,結果就是當玩家都已經跑進未加載區域了,那邊還在吭哧吭哧地加載舊區塊...
  • 開源! \o/ \o/ \o/
    說好了的開源項目,怎麼遲遲沒有開源呢? 最大的阻力:臨時代碼已經被消滅了,我打算等Shadersmod兼容差不多實現了後就傳到Github上,啊當然是創一個新倉庫了,我怎麼會把現在的私有倉庫中羞恥的舊代碼亮出來! (笑)

差不多就是這些,我去肝畢設去了(又是棄坑一段時間的節奏?),學校突然把畢設報告從6月提到了5月,藥丸藥丸啊 ?

4.26更新

基本能跑SEUS 10.1了,除了沒有DOF (因為還沒實現圖像統計) 但是有個奇怪的問題就是亮的吃屎...想不出這是什麼原因啊...

4.28更新

能跑SEUS10.2了,SEUS11.0也差不多不過水麵有些問題.10.2屏幕過亮的問題是eyeBrightness弄錯了,此外還修掉了一些奇怪的問題,不過依然有一些光影會跪的比較慘,群眾喜聞樂見的Chocapic13 V6天空渲染會莫名其妙地壞掉.

另外還有一個問題可能要在很久之後修復,甚至是不修復...就是矩陣精度問題,Minecraft一直是直接用glTranslate、glRotate之類的GL函數直接操作OpenGL矩陣,而OpenShader則提供了一個封裝(通過ASM魔改1.8新增的GlStateManager來實現,具體原理篇幅有限先跳過了 233),對矩陣的操作會先緩存在應用層,到下一次DrawCall之前再通過glLoadMatrix更新到驅動,這個可以降低GL函數調用的開銷,然而帶來的問題是OpenShader的矩陣精度好像差了一點點...

這"一點點"對常見的矩陣(模型視圖矩陣和投影矩陣)沒有影響,但對法線矩陣(gl_NormalMatrix,還記得我在Shadersmod教程的附錄里提到的計算方法嗎?模型視圖矩陣的左上3x3的逆的轉置)有着微妙的影響,由GL函數得到的模型視圖矩陣算出的法線矩陣在乘完法線後,似乎能近乎神奇地保證法線的單位長度依然保持在1左右,至少是小於等於1;而OpenShader的矩陣呢,乘完後長度稍微大了那麼一丁點...非常非常小的一丁點,但在某些魯棒性較差的算法里就會跪了,比如說我的那個Shadersmod教程里的MyFirstShader...

MyFirstShader里用到了一個將vec3格式的法線編碼成vec2格式的算法:

vec2 normalEncode(vec3 n) {
vec2 enc = normalize(n.xy) * (sqrt(-n.z*0.5+0.5));
enc = enc*0.5+0.5;
return enc;
}

...

vec2 normal = normalEncode(gl_NormalMatrix * gl_Normal);

最要命的地方在於那個sqrt(-n.z*0.5+0.5),它假定法線的長度一定不超過1,因此z一定是介於[-1,1]的,由此它才可以放心大膽地計算sqrt(-n.z*0.5+0.5)而不用擔心-n.z*0.5+0.5小於0時sqrt蹦個NaN,但是由於矩陣精度問題,這裡的法線長度超過1了,於是n.z=1.00001, -n.z*0.5+0.5=-0.000005, sqrt(-0.000005)=NAN,完.IEEE754規定NaN參與的運算都會變成NaN,就像JS中的undefined那樣毒性十足,於是整個算法就跪了,編碼出的法線在特定的角度(一般是平面垂直玩家視線時)會毫無徵兆地突然崩掉.解決的方法其實非常簡單,給gl_NormalMatrix * gl_Normal套個normalize就行了,幸運的是市面上幾乎所有光影包在獲取法線時都是用normalize(gl_NormalMatrix * gl_Normal) (你問我的那個教程光影? 它可不算"流入市面"哦?),因此基本不用擔心這個問題,所以我也不太急着修復它...(更何況我也不知道如何修復 hehe)

4.29更新

試了一下發現Chocapic13 V3也會跪掉,原因是Chocapic13 V3使用了一個Shadersmod非常罕見的特性:DRAWBUFFERS中允許空白佔位符,比如DRAWBUFFERS:NNN1N2就是gl_FragData[3]向colortex1輸出顏色,gl_FragData[5]向colortex2輸出顏色,而gl_FragData[0]、[1]、[2]和[4]不向任何RenderTarget輸出.我當時沒料到還有這種操作 233 V6跪掉的原因是Optifine內置的新版Shadersmod似乎支持在一個pass中向一個緩衝同時即讀即寫了,奇怪的是它的文檔中卻明確提到"Writing to color attachments that the composite shader also reads from will generate artifacts" 難道他自己搞了大新聞結果忘了更新文檔了嗎 ? 其實針對即讀即寫的情況啟用Ping-pong我之前已經打算把它作為OpenShader相對Shadersmod而言的新特性了,不過因為需求不大一直沒實現,現在看來是被Optifine將了一軍了 233

閱讀全文 [...]

OpenShader月報 #0

雖然沒有做paperwork的習慣,但我覺得在這時候還是寫一篇總結這個月工作進展的文章比較好,畢竟衛星放出來如果沒時不時地搞點大新聞的話總會讓人覺得棄坑了是吧 233
  • RenderChunk更新優化
    眾所周知MC的地圖是以16x256x16的Chunk - 即所謂的區塊 - 為存儲單位,但在渲染時是以16x16x16的RenderChunk為單位進行更新和渲染,問題在於RenderChunk的更新並不是很快,在i5-4590的機器上大概需要6ms每單位,雖然MC已經引入了多線程更新RenderChunk的設計,但這隻局限於由地圖加載導致的更新,由磚塊變化產生的更新依然要求單線程更新,這就導致了當發生大規模的磚塊更新時遊戲幀率會驟然下降,比如大規模流水或接入高頻紅石電路的紅石燈等.
    RenderChunk的更新操作實際上是遍歷區塊中16x16x16的部分逐磚塊地進行可見性判斷,然後將磚塊的模型面填充入頂點緩衝中,其中獲取光照度和獲取磚塊的IBlockState這兩步操作開銷莫名的大,而在原版的更新流程中它們的調用又十分頻繁,因此在此做一個簡單的緩存就能戲劇性地加快原版更新速度.
    然而Forge還增加了它自己的更新流程,被稱為ForgeLightPileline,顧名思義,它是解決在複雜模型下原版光照錯誤的問題,在默認情況下它是取代原版流程的,它的速度跟未經優化的原版差不多,然而它的可優化空間實在不大,一堆堆的三維數組像節日綵球一樣在程序里傳來傳去還要不停地拆包打包可真沒有什麼優化手段,但是沒有它又還真不行,因此這裡採用的措施是增加一個判斷器根據被渲染的磚塊的模型類型來判斷該採用哪個更新流程,毫無疑問這一步並不快,因此加入了一個LUT緩存,採用打表查表的方式來加速判斷.
    此外還有一個被臨時取消掉了的優化,是讓上文提到的發生變化的磚塊也進行多線程更新,這一項優化效果斐然,但是卻存在兩個巨大的缺陷,一個是在首次進入遊戲時有大概5%的概率因為某個競態條件而陷入卡死 - 提交入隊列的更新任務莫名其妙地沒有被執行,這個問題我調查了幾天也沒有結果;如果說它可以通過一個條件變量強制在首次更新時採用單線程來"解決"的話,下一個問題根本無法繞過,在更新區塊時,某些BlockRenderLayer的變化要到下一幀才會反饋,首先要解釋一下BlockRenderLayer(以下簡稱layer),從1.8開始MC中的磚塊被區分為4種layer: SOLID(無Alpha測試,有Mipmap)、CUTOUT_MIPPED(有Alpha測試,有Mipmap)、CUTOUT(有Alpha測試,無Mipmap)和TRANSLUCENT(開啟混合). 在渲染時MC會將按照這4個layer逐層渲染磚塊,原因很顯然,不同的layer需要不同的渲染狀態.而在引入多線程更新後遇到的問題就是,當區塊發生更新時,某些layer中的頂點變化總會慢一幀才能表現出來.
    比如,草地的layer是CUTOUT_MIPPED,石頭和泥土地是SOLID,當你敲碎一個放置在草地旁的石頭後,石頭破碎的地方不會被填上草地,而且會露出一個空洞,這個空洞直到下一幀時才會補上,然而奇怪的是設斷點會發現更新只發生了一次並且是發生在敲碎石頭的那一幀,這難以解釋為何一次更新的效果直到下一幀才會生效,在更新完畢後將數據從客戶端(CPU/內存)上傳到服務器(GPU/顯存)的操作無論是單線程版本還是多線程版本都會被留在主線程進行,或許是由於內存可見性什麼的問題,但不管怎麼說,具體原因始終未能確定,因此只能暫時取消掉多線程更新這個優化.
    圖:在擊碎磚塊的瞬間留下的空洞,這個空洞在下一幀即被填上了
  • 自定義磚塊貼圖
    Shadersmod增加了法線貼圖"_N"和高光貼圖"_S"兩種額外的磚塊貼圖,現在OpenShader也完成了新增磚塊貼圖的功能了,開發者可以自定義要載入的貼圖的後綴名以及當無貼圖時的默認值,比如法線的默認值可以採用0xFF7F7FFF(BGRA格式的(1, 0.5, 0.5, 1)). 不過顯然在切換光影包時重新載入一遍貼圖會很慢,因此Mod提供了一個選項是提前緩存_N和_S兩種後綴的貼圖並且在切換光影包時不卸載它們.
  • 界面&選項
    現在已經有了一個類似Shadersmod那樣的光影包菜單,並且實現了切換或重載,理論上講Mod現在已經可以正常使用了.
  • 多線程渲染準備
    在進行正式渲染之前,MC需要先準備渲染數據,比如計算鏡頭數據、對場景做視錐裁剪等,這一部分在之前是單線程完成的,現在這一步是以Pass為單位進行多線程處理,當Pass數量小於等於CPU核數時,渲染準備所需的時間就從"所有Pass準備時間之和"變成了"最慢的Pass的準備時間",也算是省出了一些時間吧...
正在進行的工作:
  • 池化VBO&MultiDraw (60%)
    當我在測試用的光影包中嘗試實現一個樸素三重CSM(不根據視錐做偏移,陰影鏡頭始終對準玩家)時發現性能急劇下降,從Profiler的結果來看一個問題是最大視距的那個陰影鏡頭所需要的準備時間太長了,另一個問題是磚塊繪製過慢,在10視距時,玩家視角與3個ShadowMapping過程總共需要繪製1000+次RenderChunk,繪製一個RenderChunk時還要為每個layer綁定一次VAO再做一次Drawcall,這樣算下來每幀會需要數千次Drawcall和等量的VAO綁定,性能瓶頸妥妥地是在驅動上,事實上確實當顯卡佔用沒增加多少而CPU卻有一個核心已經跑滿了. 因此問題在於如何降低調用數量,N卡有個BindlessMultiDrawIndirect,可以在不綁定VAO/VBO的情況下進行單指令多渲染,但顯然我們不能指望一個供應商獨佔擴展...因此我把注意力集中到了最簡單的MultiDraw上,MultiDraw是個很早的特性,我記得好像是GL1.5時代就有了...它可以一次指令繪製一個幾何體中多個不連續的部分,而缺點是中途不能切換紋理等,顯然這對MC來說不是問題,關鍵在於如何將多個RenderChunk中的幾個layer的頂點數據都整個在一個VBO中.
    我的解決方案是以4x16x4個RenderChunk為一組(也就是覆蓋4x4個Chunk)使用一個手動維護的內存池來共同存儲所有layer的頂點數據,這個內存池的實體是在顯卡顯存中的一個VBO,在客戶端程序會負責空間分配以及數據傳輸等工作,當RenderChunk向內存池首次提交一次數據更新時,內存池會根據數據體積分配一段略大於數據體積的空間,然後返回給提交者一個指針(有意思的是,這個指針是可變值(Mutable),我知道一個可變值指針聽上去即不可思議又噁心,但這確實能省下不少事),同時會在一個SortedSet中記錄已分配的指針,有個例外是空數據提交產生的指針,這種指針不會被插入到那個SortedSet中.
    每一個內存池都要面臨的問題是碎片整理和擴容,理論上這個內存池可以通過glCopySubBuffer來整理碎片,只要將末尾的內存複製到前面的空隙即可(glCopySubBuffer有個特性是複製源範圍和目標範圍不能重合,這導致了複製空隙緊後面的數據到前面來,以"冒泡"的形式整理碎片的方法不能用了),但我暫時還沒實現,現在的做法是在擴容的同時順便進行碎片整理,glBufferData在擴容的時候不會保留舊的數據,因此得先分配一個新的VBO,然後把數據複製過去,再銷毀舊的.在複製時如果遇到空隙的話就會跳過去,同時糾正之後的指針的偏移量(這也是為什麼它是可變值)
  • 指令隊列 (30%)
    鑒於OpenGL要求必須在主線程渲染的特點,壓榨性能的方式之一就是將渲染數據的整理工作通過多線程來處理,主線程只負責指令提交工作,指令隊列響應了這一思想,所有渲染操作都被分類為一些指令,一個阻塞隊列負責緩存這些指令,協線程負責收集數據和生成渲染指令,並將指令送入隊列,主線程從隊列取出指令並執行,這應該可以一定程度上提高性能,畢竟擁有更高的並行度,並且主線程執行的工作更單一,不會受CPU緩存污染的困擾. 現在,唯一的問題在於,從隊列中讀取指令並執行的開銷會抵消這些提升嗎...
  • Early-Clear (0% :P)
    上文提到了渲染和數據準備是分開的,但事實上有一種渲染操作"基本"無需數據 - 清除緩衝,說它"基本"是因為主視角的清除顏色緩衝還依賴於霧顏色,而霧顏色的計算是在相機計算中進行的,因此可以在完成相機計算後主線程提前開始為各個Pass進行緩衝清除,而協線程繼續準備其餘的數據.
  • 很...很多... (紫asdfghjkqwertyuiozxcvbnm%)
    顯然今年沒Demo了! 看看明年1月衛星能不能落地吧! 新年快樂 🙂
閱讀全文 [...]

[新坑預告] Yet another shadersmod implementation

最近閉(zhuang)關(si)了幾個月,我並沒有被Hello Games幹掉(順便在這裡祝賀他們沒有跑路而堅持在Steam黑五打折期間出了個主要更新,以及敢於向玩家們彈小窗的勇氣 - 他們是我見過的第二個向玩家彈窗發送更新通知的遊戲廠商,第一個是製作MachineCraft的G2CREW) 這4個月我基本都在過着6點睡2點起(嗯,早晨6點,下午2點)每天見不到幾個小時的太陽的生活,在肝膩了文明V(沒有I)、艦R、崩3、WT後我終於決定該寫點什麼,正好手頭這個秘密開發了5個月的坑終於有些眉目了,因此決定放個預(wei)告(xing),有人說把坑公開出來就能督促自己不棄坑,然而對我這樣的臉皮略厚的人來說好像並沒有什麼用...2014年我公布了AsmEventBus(基於ASM的Java事件總線系統,比Guava那個基於反射的系統要快很多)和一個沒公開名字的Java模塊系統(如果稱它是"類OSGI"顯然有些裝逼,但它做的事確實和OSGI差不多) 然後理所當然地坑了,其實對於前者我挺耿耿於懷的,明明只要再完善一點就是個很好的庫...今年年初在日狗的軟工作業要求下不得不放了個某文字H遊戲引擎的Java開源復刻衛星,然後,嗯,然後沒下文了.寫到這時我自己都忍不住掩面笑了一下,看來羞恥柱這種東西對我這樣倒錯的人來說並沒有什麼監督作用.其實我挺懷念11~12年的那段時光,那段不知失敗為何物敢於寫任何自己想寫的代碼的日子,在11年的最後一個月我在這個房間的同一個角落半生不熟地用C#寫一個文字獵奇遊戲,用慢的吔翔的GDI+在WinForm上畫文字(現在一想其實挺像ERA啊,當時我要知道ERA的存在的話是不是就給ERA寫腳本去了?) 最吃精(?)的是當時我居然在試圖寫一個自己設計的腳本語言的解析器,幸好當時沒做出來,不然這足以讓現在的我感到自愧不如,有人說好的程序員應當在看到自己6個月以前寫的代碼時能發覺自己現在的進步,這麼說我應該儘快刪IDE退圈了. (笑) 不過我倒真的挺懷念那個項目,畢竟能親手(即使是只有文字)肢解幻想鄉的女孩子怎麼想也是一件刺激又有趣的事情,有機會的話我一定要把它復刻出來. (啊呸)

懷古傷今的時間到此為止了,現在說說手頭上這個坑,當時提出做MC光影Mod的復刻這個概念是在什麼時候已經無從考證了,印象中是14年7月我和ici2cc給CustomSteve做光影Mod兼容時第一次提出了自己做光影的想法,不過當時由於很"容易"地完成了CS與光影的兼容工作,因此這個念頭就被打消了.一年半後的16年2月我寫完了光影包教程後和ici侃大山時聊到了光影Mod的種種不足,當時我開玩笑地提出自己也打算寫一個光影Mod,ici沉默半響後問道你是認真的嗎,這時我才開始真正考慮這件事,具體的討論過程記不清了,結論大概是坑太大填不起,而且當時我剛完稿光影Mod教程很累,也並不想就這麼立刻廢掉自己的工作成果,更重要的是我想去填一個自己之前挖出來的大坑(這個坑不想提了...也不用猜,"基本上"從未公布過) 因此這件事就被放下了,時間到了6月,那時不知在哪我看到了一個消息,Continuum光影包的作者在用C++給MC寫一個渲染器,不用說也知道它會支持第三方光影包,當時就把正在補伊里野的天空的我嚇得把播放器關了,為什麼呢,因為當初我被Continuum的作者肛過一次(大霧) 2月初我在撰寫光影包教程的最後兩章時ici彈小窗告訴我"被搶先了",當時嚇得我差點提前去見幽幽子,ici趕在我失神之前發來條鏈接,我趕緊緩過來點開一看是個油土鱉視頻列表,上面三四片Continuum的作者錄製的光影包教程視頻,簡單地看了一下後我半自我安慰地得出了個結論:(局座臉)這教程,飛不起來! 本着公平競爭的原則,這裡貼出視頻列表地址,為什麼那麼說呢,我感覺他過度死扣光照、ToneMapping和PBR,而忽略了對光影Mod特性的介紹,最簡單的例子,除了我的那篇附錄以外還能從哪找到對光影Mod的技術規範文檔呢,恐怕官方Wiki都沒有這麼詳細,然而話是這麼說,但畢竟人家已經搶先發出來了,"第一個光影Mod教程"這個頭銜是搶不到了,只好奮筆疾書去搶"第一個成文的光影Mod教程" (笑) 這也是為什麼我的教程中最後兩章明顯的很潦草的原因 (當然,我寫煩了也是一個原因...) 後來我的教程發出來了,而他的教程棄坑了,我還順手打了兩發對他的黑槍 (誒嘿,我這人咋就這麼愛打黑槍呢) 一個是他那個"號稱世界最強卻實際只是又一個C13衍生品的光影包",一個是他開的"MC的Vulkan渲染器"坑.故事就看似告一段落了,然而我萬萬沒想到的是他那個Vulkan渲染器在知難而退後又蛻變成了"C++寫的OpenGL4.5渲染器",然後又把我肛了一次!被一個人肛兩次這事能忍嗎!

然而當時是考試周,我只能忍下去了 (笑) 考試結束後我開始探究技術可行性(後來證明這幾乎是無用的,所有遇到的問題這時都沒發現) 並於7月的實習第一天在那間位於商住一體樓里的破房子中建了項目的Git倉庫,版本記錄顯示前三天我寫的代碼除了Mod主類和Coremod的LoadingPlugin以外沒有一行保留到了現在 (手動滑稽) 一方面說明了那個"百分之多少(記不清了)的代碼是要在一年內被重構掉"的理論是正確的,另一方面說明了當時我是有多麼低估了問題,雖然那次糟糕的實習讓我失去了去CJ和Jeb見面(對此ici可以吹一輩子(笑))以及勾搭上養豬場的機會並且錯過了魔都THO,但如果說那一個月實習有什麼用的話,那就是讓我塌下心能用當初徒手肝解析器的勁頭從零製作"Yet another shadersmod implementation",不,它不叫"Yasi"或什麼的(雖然現在看起來還挺酷!) 也不叫ShaderCraft云云,我將其命名為OpenShader,因為Open這個詞對於開源程序員來說就如同貓薄荷對貓一樣充滿魅力,顯然這是開源的,不過我將它暫時託管到了私有倉庫中,因為當初我也實在不敢確定它可以完成,畢竟我之前失敗過的太多了,事實上,直到11月初時它甚至還沒法正常運行,而我最初的計劃是10月初給出一個Demo...當初我寫光影包教程時也曾"計劃"在10月初完稿,不過現在看來這次我似乎不必拖到次年2月了,然而Mod維護是一件長期的工作,不是嗎?主要的技術突破都是在11月中完成的,現在它已經實現了:

  • 高度自定義的渲染管線,可自定義每一幀(Frame)渲染時採用的Pass,以及每一Pass包含哪些繪製階段(Stage)
  • 着色器加載系統,包含一個簡單的Includer實現(無需那個ARB擴展).
  • 可自定義的幀緩衝和幀緩衝的掛件(顏色、深度、模板),包括掛件的尺寸和格式.
  • 頂點着色器的頂點屬性(Attribute)注入.
  • 一致變量(Uniform)注入.
  • 優化的GlStateManager,儘可能地減少OpenGL調用.
  • 特性(Feature)系統,用於開啟、關閉或改變一些MC的功能與屬性,比如設置太陽偏斜(光影Mod的sunAngle)
  • 使用VAO渲染區塊 (WOW!) 理論上講90%+的顯卡都支持ARB_vertex_array_object和APPLE_vertex_array_object中的一個,如果有哪個辣雞卡兩個都不支持,那它也不見得跑得動着色器.
  • 支持Mod式和外部文件式的光影包(其實只實現了前者)

當然,它還有一個不短的TODO List,由於沒有寫TODO List的習慣(??)這裡先隨手寫上一些能想到的:

  • 紋理系統,比如光影Mod的載入法線和高光紋理,以及載入外部紋理文件,可以搞基於LUT的顏色校正啦.
  • 用戶界面.
  • 外部文件式的光影包的解析.
  • 資源管理...
  • 條件允許時使用UBO更新一致變量.
  • 世界上有兩群人需要人間會社程序員的人間關懷:烏干達的可憐兒童,和蘋果機用戶.
  • 還有一些瘋狂的念頭,不過都是要在前面這些完成的前提下才有...
  • 大量的,關於紫sama藍sama幽幽sama覺sama戀sama秦心醬和瑪艾露貝莉x蓮子的福利

想說的暫時是這麼多,如果成了的話喜大普奔,再一次棄坑了的話那就又是一次喜聞樂見的自掛城牆,我先睡覺(jue)去了...這裡貼一個Mod式光影包的光影包初始化代碼和渲染管線構建代碼的一部分 (群眾:有毛用啊!) 可以大概了解一下它的API風格,畢竟將來有了外部配置文件式的光影包後,基本上也會跟它差不多:

53057998_p0

不好意思,貼錯了...



20161203071732 20161203065401

這個才對 √

閱讀全文 [...]

實習結束,與對無人深空的黑槍

前幾天實習結束了,開始的工作居然是做盜版書...(OCR把掃描版的字扣出來,然後手動糾錯並錄成Latex格式) 最後一周老闆找我去談♂話,說他去年培訓的基本不會編程的本科生(??大叔你招的真的是本科生嗎)出來可以月薪上萬,勸我留下來繼續跟他做學(jiao)♂習(yi),淦,你還想再讓我做6個月盜版書?大叔見我一臉冷漠,還真讓我先做一周PY交易再來考慮 - 他翻出了個以前的"實習生"做的產品,一個拿Python寫的Wifi嗅探器,唔不過不是演示給我看,而是要我去調通了...它現在居然還運行不了,老闆你的實習生這麼忽悠你真的沒問題嗎...我看了下附帶的文檔,居然是寫給評委求好評的,看來不是畢設就是什麼競賽的參賽作品,帶着敬畏的心情我按照文檔上的指示裝好了依賴庫,順便糾正了源程序里幾個硬編碼的問題,看來他們是真沒打算讓這程序在除開發機以外的任何地方運行,然後啟動了程序,等了半分鐘沒有任何響應後,正當我想問候Konqueror開發者們的家人時終於彈出了個VB風格的窗口,看上去還有模有樣,然而我點了幾個按鈕卻發現沒有任何反應,就連主界面上的熱點參數也是驢唇不對馬嘴.我查了一下源碼中的其他部分,結果感覺自己的智商收到了致命的打擊...整個程序唯一有實際功能的部分只有啟動系統時的熱點嗅探,其他的部分只有個界面,包括主界面的那些參數都是事先填上去的...而且他們連子窗口的打開都沒有做,只用PyQT畫了個界面,看來是打算糊弄過文檔中的截圖部分就了事了,我把這個令人尷尬症發作的消息彙報給老闆後老闆仍不甘心,在聯繫了一通當事人後又找我說他們抗議說明明還製作了一個顯示熱點信息的功能 - 哦我知道,就是那個內容是事先填好的那東西?我又重新分析了一遍源碼,發現有一段從未被調用的代碼倒還真是跟將Wifi信息顯示到主界面有關的代碼,哎哎看來剛才冤枉人家了,不過為什麼會從來沒調用呢?在源碼里搜了一夏發現調用被注釋掉了,取消掉注釋後程序再一運行,duang,掛掉了,原來那段代碼是沒寫完的,就兩個功能還有一個沒寫完,你們當時deadline可踩得夠緊啊.當我把第二個噩耗彙報過去時他們已經把(前)老闆拉黑了,可喜可賀的結局啊.老闆也沒再找我什麼事,最後幾天在班上乾乾私活肝肝艦(?)也就過去了,要說實習收穫了什麼,一個是學了Py交易--哦不,是學了Python和Latex,第二個是染上了艦癮(!),第三個是幹了些私活,就這三點微小的工作,謝謝.另外那大叔還想再留我干200天白工...謝謝您嘞,您還是專心去折磨下一波實習生去吧...

另外這幾天無人深空終於發售了,這一個月無人深空的負面消息幾乎接連不斷,在我印象中似乎是在偷跑視頻曝出之後才有的一波波的黑槍,排除故意抹黑和牆倒眾人推的跟風黑以外,在發售後無人深空的表現確實是有些對不起當年的宣傳,於是發售當天我就寫了篇差評,順便一提我看官方的Supoort提到遊戲需要支持OpenGL4.5的顯卡並且明確聲明不支持I卡(什麼東西需要OpenGL4.5並且永遠與I卡無緣?對了,DSA) 於是我想看看一個用到了OpenGL包括DSA在內的全部先進特性的3A級遊戲渲染是怎麼寫的,便掛上了調試器,然後...然後...我看到了這些,我就發個圖,懂的秒轉(開玩笑) 別忘了這不是OpenGL2.1時代的遊戲,這是一個寫明了要求OpenGL4.5(不是客服吹逼,着色器腳本中的#version可是寫着要求450的(笑)) 把I卡全家直接打死在門外,寫着AMD與狗不得入內的3A級遊戲.

nmsr
Hello Games你他娘的找不出個會算線性代數的程序員就算了,用第三方的數學庫也行啊,你TM glGetFloatv+glUniformMatrix4fv是個毛意思?是不是U3D出來後市場上連個會寫引擎的程序員的招不到了? (U3D:媽的日常招黑)

另外附上我在Steam評測上的黑槍 🙂



開始我一直不明白為什麼按E才能進入遊戲.
後來當一架太空艇映入我的眼帘時,我才明白:
按E,方可賽艇!
-------------禁忌的分割線-------------
當初第一次看到無人深空的宣傳片感覺內心受到了深深的震撼,這不正是我一直期盼的一款遊戲嗎?集沙盒遊戲、過程生成、太空探險等大成者於一體的神作,於是我一直在關注這個遊戲,只可惜官方放出的消息實在太少,很長一段時間官網唯一的內容只有封面上那個黑色八面體,但是每一部宣傳片或宣傳圖都吊足了觀眾的胃口,從無人深空出現的那一天起,無限星辰一直在跳票,星際公民一直在攬錢,精英危險一直在放星球登陸的衛星,好像所有跟太空和星球沾邊的遊戲,都被無人深空的影子給蓋上了.因此當它剛剛登陸Steam時我就將它加到了願望單中,今年3月剛剛開放預訂時我就砍了手.原定6月發售的遊戲跳票到了8月,沒有關係,我們都等了兩年了,還在乎這兩個月?
時間到了12號的晚上,小夥伴們盼星星盼月亮地等着0點遊戲解鎖,群里有人吐槽"這一刻我們都成了麥克雷",隨着桌面上的時鐘跳到00:00,我低吟一聲"午時已到",重啟了Steam,然而依然沒有下載選項,跳到商店頁一看,"將在大約less than one hour後解鎖"?難道這遊戲的13號解鎖指的是UTC+7的13號?算了反正已經等了幾年了,不在乎這一個小時.我看了一會Metacritic上主機玩家們對無人深空的評價,又刷了會知乎上對遊戲的黑槍,1點很快就到了,我隨手掛上4發大建,4個1:25:00,靠看來今天的運氣現在就用光了,我重開了Steam,這一次終於有下載了.
我忘了遊戲下載也需要時間這個問題,以現在3A級遊戲的容量,幾十個G的東西起碼要下個一兩天,這意味着這段時間我除了對群里那些百兆光纖的土豪羨慕嫉妒恨之外什麼都做不了,在鬱悶中我點開了下載,Steam告訴我需要下載2600MB的東西.
等等,2.6G?
我仔細數了一遍數字和單位,確定不是26G或2600G什麼的東西.這遊戲確實只有2.6G,你看隔壁塞歐弟和太太掉下來幾十G的東西都只局限於地球或某個鳥不拉屎的外行星球上那麼巴掌大的一小片空間,就連以沙盒著稱的雞踢誒5也不過是模擬了一個小島就花了67G,之前聽說塞歐弟:WIFI戰爭終於可以上太空了覺得因吹絲挺,現在看來無人深空用2.6G就能囊括整個宇宙,簡直把它們完爆到媽都不認識了!我感動得淚流滿面,我不知道什麼不到1G的SpaceEngine,更不知道不到300MB的Pioneer,也絕不知道PS4版有6G,在這個靠塞無壓縮的高清紋理、1080P CG影片和多國語音來惡意強撐遊戲容量的時代,無人深空這個最後的良心為遊戲圈帶來一股清風,向世人宣告遊戲大小不代表深度!8年前的這一天,在遊民深空上有個孢子的無腦吹說"從此以後世界上不再需要別的遊戲了,因為孢子就能模擬它們全部",正如那句老話"鐵打的遊民流水的噴子",那人就像千千萬萬遊民噴子一樣很快就消失在歷史長河但中,但8年後的現在,我將繼承他的衣缽,舉起無腦吹的大旗,宣布"從此以後世界上不再需要別的遊戲了,因為無人深空就吊打他們全部"!
家裡水管雖小,但下2.6G的東西已足以,我上B站二周目了一遍秘封活動記錄,然後用小號在下面評論區釣了個魚"這破動畫吃棗藥丸",遊戲已經下好了,上一次我感受到遊戲在召喚我還是去年輻射4出的時候,這一次 - 我相信是最後一次,因為今後將不會有遊戲再感動我了 - 我又感受到了無人深空的召喚!我來了!
進入遊戲,啟動果然很快,隔壁Minecraft點開遊戲後先得點上支煙才能等到彈出啟動界面,而無人深空幾乎在我鼠標鬆開的那一刻就進入遊戲了!製作商的Logo過後,是一片耀眼的群星,想必那一個個標籤就是星系的名字吧,宇宙雖大,但與人類的野心相比仍太小,如果我不早點探幾個星系,想必整個本星系群都要被中國人和掛機軟件佔滿了吧,到時候看着滿銀河的"dajiba666"、"baoweinanhai"、"lajiyouxi"這樣的國人風格星系名或"pan"、"1c0xr8Hy"、"h0yx"、"SSTM"這樣Bot自動生成的星球名字,豈不噁心?群星很快就消失了,取而代之的是一個純白的界面,上面只有一行字"初始化",嗯我明白,遊戲總得預處理一些數據,我切出了遊戲,群里管理說他卡在了初始化界面二十分鐘了,我笑他那是什麼破機器,然後我被口球了,我想切回遊戲--
但卻發現切不回去了.
我能理解這種情況,很多遊戲在載入數據時都是沒有響應的,因為就是有那麼多半路出家的遊戲程序員不知道要把資源加載放在另一個線程,或者在主線程中加一個單次資源加載的閾值超過這個閾值就要先去poll一下Windows事件再繼續"少女祈禱中".然而此時我已經能聽見遊戲中BGM的聲音了,載入時有BGM的遊戲我還沒見過幾個,所以憑着多年遊戲經驗,我知道遊戲不是卡在加載中,只是純粹地切出去就不能切回來了,我打開了任務管理器,10分鐘,這大概是我玩的Steam遊戲中,最快的一個需要叫任務管理器來救場的遊戲吧.(Update:我開始以為因為這是主機遊戲所以開放商沒考慮切出遊戲的情況,後來小夥伴告訴我主機也能切出遊戲的,啊露怯了← ← 姨夫:"這鍋我不背!")
這一次我進入遊戲後學乖了,我在"初始化"界面老老實實地等着,生怕它再一言不合死給我看,我打開手機收了剛才的建造,高雄、愛宕、中二病的摩耶和一臉想做的小婊子鳥海,靠真是一家人整整齊齊,我把她們全打包喂提爾比茨了.又收了一波遠征後再看屏幕,依然是白的刺眼的屏幕和一行黑字"初始化",以及下面一個寫着E的小圓圈,這遊戲怎麼載入這麼久?我不敢切出遊戲,只好在手機上Q去群里看看,我的口球還沒下來,所以沒法發問,只好慢慢翻他們聊天記錄,我看到一個人提到進遊戲要按E,我就笑了,勞資打了這麼多年遊戲日系美系歐系俄系甚至東南亞系遊戲都玩過,見過按ESC按Z按回車按空格進遊戲的,還真沒聽說過按E進遊戲的,帶着一臉的不屑,我隨手按了一下E鍵,心裡想着這要有用的話我直播口乞--
寫着E的小圓圈多了一道黑邊.
我哭了,這個"初始化"不是初始化遊戲的,而是"初始化"我的智商的,我怎麼就沒想到那個E是要我按E鍵?姨夫的形象浮現在我眼前,手柄上的按鍵都是圓圈,你在屏幕上畫個圓圈裡面寫個△那主機玩家的第一反應就是按下△,這是顯而易見的.然而電腦上可不存在按鍵是圓的的鍵盤啊!幸虧這遊戲不是按O進遊戲,不然電腦玩家看着大圓中套一個小圓恐怕就是腦補出咪咪頭也想不出是要按O進入遊戲,我淚流滿面地按住了E,進入了遊戲,耳邊傳來電子音的女聲,居然是中文,而且是標準的普通話,看來商店頁那個"中文語音"不是製作商手滑寫錯了,這一點我要給一千個贊!我滿懷激動的心情憧憬着我在異星球上的生活,腦中浮現出一幕幕宣傳片中的世外桃源,然而映入我眼帘的是一個冰雪的世界,無垠的雪地與淡綠色的天空在遠處混為一體,屈指可數的幾株植物散落在腳下這片綠地上,HUD上-41°C、1.6Rad、15.4Tox提醒着我,這不是生機盎然的山中秘境,而是一個接近生命邊緣的死亡世界,是的,探索、看畫那是次要的,生存才是我們這些出生在惡劣地形的非洲玩家需要在意的事情.然而我就要在這個破星球上待一輩子嗎?這時我看到了一架破損的太空船,於是便有了本文開頭的那一幕.
遇險信標上一個紅色的球球告訴我它叫阿特拉斯,它要指引我做完新手任務,我笑了,我見過的阿特拉斯是大西洋下的極樂城中勇敢地反抗萊恩暴政的自由戰士,是穿越於寰宇之間充當著霸主帝國的經濟主動脈的星際貨輪,是站在內天體與氏族之間的第一道與最後一道防線的巨型機甲,而不是哪個面數低到可憐的幾何球體,我點"對指引嗤之以鼻",然而什麼也沒發生,看來遊戲的製作者沒料到會有像我這樣作死的,沒製作這個對話分支,我決定虛心接受它的指導,點了"接受阿特拉斯的指引",然而卻依然什麼也沒發生,WTF!?抱着試一試的態度,我長按鼠標,結果光標周圍出現一個圓圈型的進度條,卧槽我第一次見到有哪個遊戲是要長按鼠標來選擇選項的!我TM這不是觸屏啊!阿特拉斯在"接受了我的效忠"後就消失了,沃日!說好的新手任務呢!?
好在右下邊有遊戲提示,提醒着我要先修好飛船離開這個鬼地方,說起來我還沒有給這個星球取名字呢,我調出了主菜單,選擇為星系改名並上傳,我做了一件我期盼了幾個月的事,打開輸入法,輸入"huanxiangxiang"
然而什麼都沒有發生,這個遊戲雖然支持中文,但並不支持輸入法,於是我只好退而求其次,敲入了"Gensokyo",這也好,讓老外也能看得懂,這個冰雪交加的死亡世界與大多數人眼中的幻想鄉不太一樣,不過倒也挺符合東方無限螺旋中3013年的那個永遠處於核冬天的幻想鄉.而至於星球的名字,我不知道幻想鄉中那些地名的羅馬音是什麼,也不敢切出遊戲去查;又覺得"Yuyuko my lover"有些太俗,於是我給它起了個很科幻的名字,"Gensokyo Prime",嗯,幻想鄉主星,很好,肥腸科幻.
想修船沒有資源怎麼能行,我掏出了手中的多用途工具槍,撫摸着這個又硬、又粗、頭還圓乎乎的東西,我找到了一塊石頭,HUD告訴我這個石頭蘊含著豐富的鐵,我假裝自己是從2001年穿越來的,不懂它那自帶打碼的低清材質,將工具槍對準了那個石頭,扣下了扳機,一束綠瑩瑩的光線噴涌而出,雖然這一幕我早已在偷跑視頻中看了一遍遍了,但我還是為了配合這不可思議的景象,故意裝出了吃驚的表情.從隔壁鐵拳史蒂夫用手拍木頭拍石頭拍黑曜石,到屎大棒用物質操縱槍拆得多少土著家破人亡,修理地球變得原來越簡單!老毛說過與天斗與地斗不如與人斗,那是因為他沒見過這工具槍,如果他知道修理地球也是一件如此簡單有趣的事情的話,估計他當年也就--
突然我的角色虎軀一震,拿着工具槍的那隻手像觸了電似的伸了回來,難道我被水表爆破了!?並不是,上一次我見到類似的一幕,還是我最後一次玩Halo:CE時在追殺對方的奪旗手,將他打到大概還剩最後一點血時突然像癲癇發作一樣捧着冒着藍焰的電漿步槍雙手發顫;是我玩WH40K桌面戰棋時一回合有6個勇敢的帝國衛隊戰士因為機械神教粗製濫造的電漿槍過熱炸膛而殉國;是我玩機甲戰士僱傭兵時打競技場在最後一個對手只剩下最後一絲血時PPC一輪齊射沒秒掉對方然後自己過熱停機,硬生生被對面翻盤打死...沒錯,這就是科幻遊戲中臭名昭著的過熱!人類31世紀的智慧結晶,居然在零下41度的環境射一發就過熱!?你TM屬機甲戰士的啊!?(這是BattleTech被黑的最慘的一次)
礦物采完了,該製作修復起落架的裝甲了,然而遊戲中卻沒有任何地方提示該如何合成物品,我在群里的口球已經被摘下來了,於是我趕快問了一下這個問題,一個小夥伴告訴我是把鼠標移到空格子上按E,卧槽原來E鍵如此重要!難怪遊戲開頭要玩家長按E鍵才能進入遊戲啊.修好了起落架,然後需要去挖一個製作者臉滾鍵盤滾出來的名字的元素,掃描儀告訴我距離我最近的這種礦物走過去需要4分鐘的路程,如果是在風和日麗的世界這一趟短途旅行並沒有什麼問題,然而我現在是在一個極地世界,防護服一邊報告說維生系統只剩下一半的能量,一邊報告說即將進入零下70度的極夜,群里管理員哭訴說他出生在了一個零上60多度的星球,我吐槽他可以當這是風之旅人來玩,然後又被口球了,靠,我這已經是冰雪奇緣了好不好.一路蹣跚到了礦區,路上還遇見了一個蘋果垃圾桶,心想蘋果真TM是土豪公司,到了未來已經可以滿宇宙亂扔MacPro了,走過去一看居然是一個遠古遺迹,遺迹教給我外星語中的一個詞"給",不知道今後見到外星人後叫他們"給"會不會當我是基佬把我打死,等我挖完礦時我的防護服已經開始報警了,25%的能量肯定不夠支撐我走回墜落點的,難道我的一周目就要這樣結束了嗎?我這才發現維生系統可以充能,而能量源可以是任何同位素,我瞅了眼旁邊富含鈈元素的植物,不禁感嘆造物的神奇,也暗自吐槽難怪這個星球有那麼高的本底輻射.
凌晨三點,我終於能離開這個終日被積雪覆蓋的冰封星球了,飛船騰空而起,終於飛上天了!然而太空船在低空似乎沒法隨意調整高度,也沒法將武器對準地面,看來我所屬的文明的軌道轟炸政策是禁止,很好,這很聖母,我依依不捨地繞着墜落點繞了一個圈,望着地上已經幾乎看不見的遇險信標,我相信總有一天我還是能再次遇見阿特拉斯,抬起機頭,打開加力,腦補出的G力將我緊緊壓在椅背上,我的耳邊響起了無限螺旋中八雲橙在離別時的贈言"願你所在的世界的幻想鄉,能夠幸福常在",再見了!冰雪的幻想鄉!在激動之餘我隨手按了一下E鍵.
飛船急停,極速下墜,在即將親吻大地時又突然止住下降,緩緩地降落在茫茫的雪地上,艙門打開,將一臉懵逼的我再一次彈出到31世紀幻想鄉永恆的極寒當中.
願各位玩家的出生地,春色常在.
-------------禁忌的邊境線-------------
總體來說,這遊戲質量屬於中等偏上,主要是當初畫餅畫得太大,衛星放得太高了,14年的採訪當中製作人在聊到多人遊戲時一臉燦爛的笑容,當時我們以為這是自信,現在我們明白這是騙煞筆時發自內心的歡樂.
那些舔無人深空的人,你們看看商店頁上的宣傳圖,覺得臉被打的疼不?
啊當然,中文語音一定要支持!一定要點贊!

如果你也滋詞這篇評測的話歡迎來點(jiao)♂贊(yi) http://steamcommunity.com/id/szszss/recommended/275850 當然如果拍磚點個差我也沒法怎樣的 ? 閱讀全文 [...]

基本更新到1.9.4?

20160707023557
尼瑪呀!本來心存僥倖覺得不會蹈覆那個推主的舊轍,結果扔柜子里沒管過了三周一看卧槽居(guo)然彎了!而且彎的姿勢跟推主的那隻一模一樣,支架根本就是擺設啊!下次再買波天宮的東西我直播食屌,popstick你媽飛了


咳,話歸正題,現在教程的最主要的部分,基礎篇的前三篇已經更新到了1.9.4了,如果你在我更新的時候正巧在讀第三篇的話,恐怕難免會感覺像一夜間穿越了一樣...這次更新也修正了一些之前的疏漏,我曾起過一個念頭就是故意寫一篇完全是按照錯的講的"教程"用來整蠱,現在發現在1.9時代(準確說已經到1.10時代了)留着一篇1.7時代的教程就已經夠誤導人了,再想到MCBBS上那篇ModLoader時代的教程...哎,為什麼產生了一種自己站在時代發展的對立面的感覺  face5 閱讀全文 [...]

挺屍預告?

真正的廚,即使知道是坑也要義無返顧地踩上去!
20160615225420
IMG_20160615_123941
以前早有耳聞這是東方圈中的邪神Saber(雖然我覺得最魔性的還是國內某東方雜誌創刊號附贈的永遠拼不上的黑白),實際到手後感覺造型不是那麼糟糕,然而不得不說這款做工真糙啊,要不是知道這是冷門貨不太可能有山寨,還真的會懷疑是祖國版...

上一篇文章是在4月份發的,熟悉我工作風格的都知道我喜歡悶聲搞大♂新♂聞,事實上我也確實是在憋文章,然而這一次大新聞還沒搞出來,電腦就(又一次)掛掉了(正所謂截稿前硬盤必亡之理,嗯哼?) 等到修好的時候已經到了期末各種deadline壓上頭的時候,因此一直拖到現在才有時間來填坑.

前幾天在E3微軟搞了個大新聞,宣布Minecraft會支持Mod...哦草,明明前不久我還在和別人討論天國的官方API墳頭草長得有多高,結果今天我就可以下崗了 ? 不過從目前已知的消息來看,應該是局限於移動版、主機版和用C++寫的Win10商店版這幾個版本,用Java的PC版不知道會不會有這個功能;Mod的形式大概是類似於腳本插件那樣,從PC版越來越向數據驅動方向發展的趨勢來看確實有可能(比如1.8到1.9最大的變化之一就是外置的掉落表).其實如果真的想要一個Moddable的高(C)性(P)能(P)的Minecraft的話...我個人認為MineTest遠比Win10MC要靠譜.

然後就到了對PC版的黑槍,這一次Mojang出人意料地提早推出了1.10,按照流行的版本始終要比最新版要落後一版的定律(?),應該可以放心大膽地跟進到1.9.4了,前文已經提到1.9多了個外置的掉落表,除此之外還有雙持武器、盾、滑翔翼等...這些除了雙持武器以外沒有什麼功能性的更新,據說渲染系統有些新內容,不過對大多數開發者來說還用不上.雖然看着MCBBS Mod版上一堆1.7Mod真心疼,但我還是趁着這幾天有時間更新一下教程吧("上學期"說要更新結果妥了一個學期也沒弄 233),不然等過一段時間課設和實習堆上來更沒時間弄了. 閱讀全文 [...]

使用SIMD+CriticalNative在Java中加速矩陣運算

對於遊戲開發來說,一個健壯高效的數學庫是必不可少的,特別是對於3D遊戲而言,動作系統在計算骨骼動畫時會進行數量可觀的矩陣乘法或求逆運算;渲染系統也需要頻繁計算變換矩陣.雖然一次矩陣運算消耗的時間可能不多,但對於分秒必爭的遊戲渲染來說想要力爭60fps、死守30fps底線就勢必不能放過任何一個免費提升性能的機會. 閱讀全文 [...]

關於新版ForgeGradle配置和FMM的一些事

半個月之前ForgeGradle更新了3月14號的2.1快照,FMM也自然抓取了更新,然而隨之而來的是在Blog上一陣井噴式的Bug報告,而且出錯原因都很奇怪,由於那段時間比較忙(lan)所以沒怎麼管,結果今天自己刷新工作環境的時候發現也中彩了,仔細調查下去沒有發現出錯根本原因但卻發現了另一個問題:FMM上緩存了一個損壞的快照...FMM在抓取3月14號的FG2.1快照(2.1-20160314.023449-32)時下載不完整,顯然用戶從FMM上下載到的該版本是無法正常使用的,如果你遇到形似"ClassNotFound net.minecraftforge.gradle.common.BasePlugin"之類的,那麼你就是遇到這個問題了,我已經手動更新了FMM上的緩存,但你可能仍需要手動刪掉本地緩存,具體位置是在"C:\Users\[用戶名]\.gradle\caches\modules-2\files-2.1\net.minecraftforge.gradle\ForgeGradle\2.1-SNAPSHOT"下的某一個文件中,損壞的文件是一個大小在11MB左右的.jar(正常的大小在13MB以上),如果懶得找的話可以把整個2.1-SNAPSHOT都刪了,反正它下載起來也快...
而剛才本來想調查的錯誤,則是在配置時在getVersionJson階段出現"xxx.json could not be parsed","FileNotFoundException: Inherited json file (null) not found"之類的,總而言之,就是在getVersionJson階段出錯的話,可能你需要使用一個較舊的FG版本,解決方法是把
classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT'
改成
classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-20160209.170057-21'
也就是手動指定一個老版本的FG2.1快照.另外,根據測試,這個問題只出現在Forge1.9之前(1.9沒有發現這個問題).如果你遇到了這個問題的話,可以在留言中報告遇到的版本(Forge版本,使用的FG的版本,有無使用FGOW),我也會繼續調查這個問題是Forge官方的鍋還是FGOW的鍋(畢竟這鍋太大我扛不動啊 233)

最後還有一個問題,就是FMM準備要遷站的事,此前FMM一直架設在SAE上,其質量嘛...差強人意,不過這個月SAE增加了噁心的固定扣費內容,每天都會扣除58(10基礎+48MySQL租費)雲豆的基礎費用,原本能用幾年的免費額度如今只夠幾周了...再加上最近抓取失敗的事,所以我準備把FMM遷到別處. 閱讀全文 [...]