隔幾個(gè)月才寫好下篇。繼續(xù)介紹電子妖精和顯卡少女如何聯(lián)手工作。
上篇在此
新版小屋介面有一個(gè)改變:舊版寬度是627px,新版是756px,可以放比較寬的圖和比較長(zhǎng)的程式碼了。
因?yàn)槌^寬度的圖會(huì)被縮小,用滑鼠點(diǎn)一下才能看到原尺寸,我製作圖會(huì)儘量不超過小屋寬度
另外我寫過的程式教學(xué)散落各處,可能不好找,寫了一篇目錄出來。
Shark流程式教學(xué)一覽
上篇說的那些步驟並不是呼叫一個(gè)函式就做一步,而是像這樣:
繪圖管線像顯卡少女的辦公室有一組工作檯,先呼叫一堆函式指示顯卡少女把設(shè)定值和物件放在工作檯上,然後呼叫draw call才一口氣執(zhí)行那些步驟。draw call不止一種,依不同情況使用。
上篇Input Assembler提到的頂點(diǎn)數(shù)量,以及OpenGL的幾何形狀是在draw call裡指定。
(註:雖然上圖是那樣畫,實(shí)際上套用設(shè)定值和物件的指令也是先儲(chǔ)存在驅(qū)動(dòng)程式,draw call時(shí)才一口氣傳給GPU。)
GPU讀取顯示記憶體很迅速,將資料從主記憶體傳到顯示記憶體比較慢,所以先找時(shí)間把模型、貼圖這些量大的資料傳到顯示記憶體(可以在Now Loading時(shí)做,或是遊戲進(jìn)行時(shí)在背景處理),之後每個(gè)frame只要傳送少量的資料,如「套用3號(hào)模型,5號(hào)貼圖」的指令,或是更新constant buffer。
Direct3D 11
OpenGL
這些函式實(shí)際的參數(shù)都落落長(zhǎng),本篇先略過,之後如果我有寫到那部分,實(shí)際用到這些函式再介紹。
draw call後設(shè)定值和物件仍然擺在工作檯上,下一次叫顯卡少女更換才會(huì)改變,所以如果第二個(gè)物體只要更換一部分資料,其他設(shè)定跟前一個(gè)物體相同,只要呼叫一部分函式即可。
前篇放的流程圖是簡(jiǎn)易版,那完整版又是怎麼樣呢?
(這是筆者認(rèn)為比較好講解的版本,網(wǎng)路上其他教學(xué)可能畫得不一樣,可能把某個(gè)步驟再細(xì)分,或把好幾個(gè)步驟合成一個(gè)。
步驟名稱一樣用Direct3D的,有的步驟OpenGL用不一樣的名稱。)
多了很多步驟,shader除了vertex和pixel以外還有g(shù)eometry、hull、domain三種,而且多了個(gè)Stream output,可以把前面那些shader算出的頂點(diǎn)坐標(biāo)存起來,供下次draw call使用。
很多步驟不需要時(shí)可以省略,簡(jiǎn)易版大概就是畫一個(gè)3D物體最低限度必要的工作。
有一堆用語很難懂吧。D3D和OpenGL早期版本功能很簡(jiǎn)單,大概像簡(jiǎn)易版的圖再把shader拿掉。隨著軟體對(duì)畫面的需求越來越多,D3D、OpenGL、和顯卡少女不斷追加新功能,可以做到越來越多的事,但因此變得很複雜,提高初學(xué)門檻。
上圖是D3D11和OpenGL 4版的pipeline,兩者的後繼產(chǎn)品:D3D12和Vulkan有變更一部分。然後D3D12在2018年,Vulkan在2020年新增一種技術(shù):光線追蹤(ray tracing),這是走完全不同的pipeline。
主題講到這裡,接下來講幾個(gè)有關(guān)的項(xiàng)目。
即時(shí)運(yùn)算與預(yù)運(yùn)算
繪製3D場(chǎng)景其實(shí)不只一種方法。像是描述物體形狀的方法除了本篇介紹的三角形面,還有利用曲線稱為NURBS的方法,或是用橢球組合出物體。
在「【程式】電子妖精與顯卡少女的合作——何謂GPU、Direct3D與OpenGL?」有提過標(biāo)準(zhǔn)規(guī)格很重要,如果每家廠商各自開規(guī)格,那在每個(gè)硬體和作業(yè)系統(tǒng)上程式都要重寫一次。
規(guī)格不能一味追求功能強(qiáng)大。在即時(shí)運(yùn)算的場(chǎng)合(如遊戲需要在1/60或1/30秒內(nèi)畫完整個(gè)畫面),不能採(cǎi)用計(jì)算耗時(shí)的演算法,而且功能複雜會(huì)讓軟硬體難以製造,PC、遊戲機(jī)和手機(jī)成本要低,一般使用者才負(fù)擔(dān)得起售價(jià)。在性能和成本之間取捨之後,廠商們定出的標(biāo)準(zhǔn)規(guī)格就是現(xiàn)在的繪圖管線。
相對(duì)地在動(dòng)畫、電影等應(yīng)用,工作室可以買幾臺(tái)高性能電腦讓它們計(jì)算好幾天、輸出影片檔,發(fā)佈只要發(fā)佈影片檔就行了。這時(shí)可以採(cǎi)用高畫質(zhì)但計(jì)算複雜的技術(shù),可能也不用D3D和OpenGL,而是開發(fā)一套專用API。
以上兩種分別是「即時(shí)運(yùn)算」和「預(yù)運(yùn)算」,即時(shí)運(yùn)算容易看情況更改內(nèi)容,但不能產(chǎn)生很精緻或擬真的畫面,預(yù)運(yùn)算則相反。可以觀察遊戲裡的過場(chǎng)動(dòng)畫,奴果精緻度比遊戲其他部分高很多那應(yīng)該是預(yù)運(yùn)算,如果角色變更裝備會(huì)反映在動(dòng)畫裡那大概是即時(shí)運(yùn)算。
(如果精緻度跟其他部分一樣,且角色變更裝備不會(huì)反映在動(dòng)畫裡,……那就無法判斷。)
還有3D軟體像3ds Max和Blender為了讓使用者操作後可以即時(shí)在畫面上反映,編輯時(shí)是用D3D和OpenGL畫出比較簡(jiǎn)單的畫面,render時(shí)才用精確的演算法慢慢算。
下圖是Blender說明書裡的範(fàn)例,左邊的沒有表面紋路、光柱半透明、影子,右邊才有畫出來。
如何用D3D和OpenGL畫2D畫面?
從名稱看起來D3D和OpenGL是專門畫3D的,但現(xiàn)在2D也是用D3D和OpenGL繪製了。
如果要畫一張矩形點(diǎn)陣圖,把傳給顯卡少女的資料改一下:
就可以畫2D畫面。2D、3D統(tǒng)一使用D3D和OpenGL繪製可以簡(jiǎn)化規(guī)格和軟硬體,廠商只要做一套系統(tǒng)就可以兩者通用。
DirectDraw本來是在顯示晶片沒有3D的時(shí)代使用,現(xiàn)在已停止增加新功能和改進(jìn)效能,只是為了讓舊程式能繼續(xù)執(zhí)行而保留。
D3D和OpenGL哪個(gè)比較好?
當(dāng)然兩者最大差異是哪個(gè)作業(yè)系統(tǒng)能用,大概只有Windows桌機(jī)版可以同時(shí)用兩者,這裡根據(jù)筆者經(jīng)驗(yàn)講一下性能方面。
以Direct3D 10和OpenGL 3.2以後來說,最底層建立物件的步驟差很多,但從高階一點(diǎn)的需求來看兩者能做的事就差不多,大部分功能都能找到對(duì)應(yīng)。例如都可以建立vertex buffer和framebuffer物件,但呼叫的函式和傳入的參數(shù)差很多。
以前的版本就不一定,有一段時(shí)間是OpenGL落後於D3D,D3D持續(xù)推出新版但OpenGL遲遲沒有跟進(jìn)。
API設(shè)計(jì)方面,OpenGL的風(fēng)格比較古老,寫程式比較容易出錯(cuò)。
舉個(gè)例子,操作物件的時(shí)候,D3D11是呼叫物件的method,或把物件作為參數(shù)傳入函式,每一行都明確指出操作的物件。
OpenGL通常是在內(nèi)部設(shè)定一個(gè)全域變數(shù),之後的函式讀取這個(gè)全域變數(shù)。
如果glBindTexture()和下面函式距離有些遠(yuǎn),看程式碼有時(shí)會(huì)看不懂目前在操作哪個(gè)物件。
效能的話根據(jù)筆者經(jīng)驗(yàn),關(guān)鍵是「晶片廠商有沒有認(rèn)真寫驅(qū)動(dòng)程式」,跟API本身關(guān)係不大。
電子妖精送指令給顯卡少女實(shí)際要經(jīng)過以下途徑。
一個(gè)功能即使晶片做得到,作業(yè)系統(tǒng)和驅(qū)動(dòng)程式?jīng)]有配合製作的話,也不能使用。
以下是筆者用2012左右的硬體測(cè)試,有的問題在新版本可能已經(jīng)解決。
筆者有一臺(tái)電腦是Intel Sandy Bridge微架構(gòu),在Windows上如果把相同功能用D3D和OpenGL寫,它的內(nèi)顯在D3D的效能比較好,而且有些功能只有D3D才能用。
可能因?yàn)閃indows的遊戲幾乎都用Direct3D,廠商就花比較多心力在D3D驅(qū)動(dòng)程式,OpenGL的只求有,沒認(rèn)真debug或改進(jìn)效能。
AMD和nVidia的Windows OpenGL驅(qū)動(dòng)程式比較沒這個(gè)問題。
Linux驅(qū)動(dòng)程式的情況是這樣:AMD和nVidia是廠商自行開發(fā),closed-source。這兩家另外有非官方的driver,不過是逆向工程做出來的,性能遠(yuǎn)不及官方版本。
Intel則是把規(guī)格公開,讓網(wǎng)路上的開發(fā)者以open-source的方式開發(fā)。
同一臺(tái)電腦裝Windows和Linux來比較的話,AMD和nVidia晶片在Linux表現(xiàn)比較差,可能因?yàn)楸容^少人做Linux的遊戲,Linux版驅(qū)動(dòng)程式就沒有認(rèn)真寫。有時(shí)候裝了原廠驅(qū)動(dòng)程式還會(huì)沒辦法開GUI,只剩黑底白字的命令列可以用。
Intel的由於有很多Linux使用者改良,驅(qū)動(dòng)程式品質(zhì)比較好,Intel晶片在Linux的表現(xiàn)比在Windows好。
2015年AMD把驅(qū)動(dòng)程式改用open-source的方式開發(fā),品質(zhì)可能有改善,但筆者手上沒有夠新的硬體可試用。
最近一個(gè)經(jīng)驗(yàn)也跟這有關(guān)。筆者的Linux開發(fā)機(jī)有Intel內(nèi)顯和nVidia獨(dú)顯,上個(gè)月在這臺(tái)安裝Mint 19.3,剛灌好只有open source版驅(qū)動(dòng)程式時(shí)還可以開GUI,安裝nVidia官方driver以後就無法開GUI了,照網(wǎng)路上的說明用命令列模式移除driver也不能復(fù)原,只好重灌。
但較早的18.2版沒這個(gè)問題。
查了一下資料,一般桌機(jī)裝兩個(gè)GPU是這樣,顯卡和主機(jī)板各有一組插座,要使用哪個(gè)GPU就把螢?zāi)痪€插到哪裡。
筆電的Optimus技術(shù)是這樣,只有內(nèi)顯輸出到螢?zāi)唬锚?dú)顯繪製時(shí)會(huì)先在獨(dú)顯畫出整個(gè)畫面,把畫面複製到內(nèi)顯記憶體,再傳給螢?zāi)弧?br>
驅(qū)動(dòng)程式要特別寫才做得到這個(gè)功能,可能nVidia的Linux版驅(qū)動(dòng)程式?jīng)]有好好寫,不是每個(gè)發(fā)行版都能正常運(yùn)作。
如果使用非官方驅(qū)動(dòng)程式,不但不能用獨(dú)顯,而且獨(dú)顯閒置時(shí)也不能節(jié)省電力,電腦會(huì)變得很燙。所以我寧願(yuàn)放棄獨(dú)顯了,修改BIOS設(shè)定調(diào)成只用內(nèi)顯。
有聽說nVidia本來就對(duì)Linux社群比較不友善。目前不急著買新電腦,這臺(tái)先將就著用,但以後Linux開發(fā)機(jī)我要用Intel或AMD的晶片。
偽3D是什麼?
這個(gè)詞沒有明確的定義,只是玩家間的俗稱,所以如果看到兩個(gè)人爭(zhēng)論某個(gè)東西是不是偽3D,吵半天得不出共識(shí),這很正常。
如果真要問筆者的看法,按照上篇「polygon或曲線 → 坐標(biāo)轉(zhuǎn)換 → rasterizer → 計(jì)算顏色 → 輸出」的流程繪製的是真3D,用其他方法產(chǎn)生遠(yuǎn)近感的是偽3D。
很多SFC和Mega Drive遊戲可見到一種效果:讓背景用較慢速率捲動(dòng)產(chǎn)生背景比較遠(yuǎn)的感覺。由於背景只是單張圖片,並沒有建模型用3D數(shù)學(xué)計(jì)算,這是一種偽3D。
紅白機(jī)的兩個(gè)遊戲:Mach Rider和Rad Racer,紅白機(jī)並沒有處理polygon的功能,也是用偽3D做出遠(yuǎn)近感。
當(dāng)時(shí)繪製畫面的方法是畫面由很多橫向掃瞄線組成,逐條畫出,每畫一條掃瞄線之後把下一條橫向偏移,就能讓圖彎曲,紅白機(jī)有這個(gè)功能。
這個(gè)技巧在日本叫raster scroll(ラスタースクロール),英文好像叫l(wèi)ine scrolling。
因?yàn)槭沁@種方法,遊戲畫面表現(xiàn)不出交差或距離很近,照理說可以看到另一條路的情況。也只能把圖橫向變形,紅白機(jī)看不到用這個(gè)技巧把圖縱向變形的遊戲。
找到這篇舉了幾個(gè)Mega Drive的例子:Line Scrolling - Raster Scroll Books
鐵桶之類的障礙物也不是建個(gè)圓柱形的模型,而是準(zhǔn)備數(shù)張大小不同的圖片看時(shí)機(jī)切換(紅白機(jī)是沒有縮放功能的)。
關(guān)於插圖:
很多教學(xué)裡畫暗處的常用方法是用乘法(multiply)疊上顏色,這張圖改用線性加深(linear burn)試試看。
乘法的算式:A×B。
A、B代表RGB分量,範(fàn)圍是0~1。
假如底色是(1, 0.75, 0.5),max-min=0.5
疊上灰色(0.6, 0.6, 0.6)
會(huì)變成(0.6, 0.45, 0.3),max-min=0.3
RGB差異減少代表彩度下降,所以乘法模式容易讓整張圖變得灰暗,避免的方法是針對(duì)每個(gè)底色的特性各別選擇重疊的顏色。
線性加深的算式:A+B-1,如果<0就設(shè)為0。
底色(1, 0.75, 0.5),max-min=0.5
疊上灰色(0.6, 0.6, 0.6)
變成(0.6, 0.35, 0.1),max-min=0.5
彩度比較不易下降,所以試試看這個(gè)模式,看能不能畫暗處一個(gè)顏色用到底,只有少數(shù)部位各別選色。
但這個(gè)模式可能讓分量變成0,容易讓整張圖一片黑,還是免不了要看底色各別選色。試一試覺得還是乘法模式比較易用。
另外試用一下GIMP 2.99。這版把依存的GTK 2和Python 2換成GTK 3和Python 3,Inkscape之前就已經(jīng)把這兩個(gè)函式庫(kù)換新,GIMP也更新之後,我的電腦上就不再需要GTK 2和Python 2了。
不過發(fā)現(xiàn)2.99版的Python API改變,script必須重寫,所以暫時(shí)還是用2.10,等這張圖畫完再改用2.99。
右下的遊戲畫面放大
因?yàn)榈谒母竦倪[戲邏輯寫的是RPG的戰(zhàn)鬥,這一格就這樣畫了。
角色是把目前設(shè)計(jì)出的顯卡少女挑三個(gè)換服裝,但角色在圖中很小,不容易看出誰是誰。
電子妖精和顯卡少女的工作室長(zhǎng)怎麼樣、工作時(shí)用什麼工具等等的,這張圖畫得還比較隨便,但如果之後要?jiǎng)?chuàng)作一系列作品,要把這些背景設(shè)定定個(gè)規(guī)格,以免不同作品之間有矛盾。
上篇在此
新版小屋介面有一個(gè)改變:舊版寬度是627px,新版是756px,可以放比較寬的圖和比較長(zhǎng)的程式碼了。
因?yàn)槌^寬度的圖會(huì)被縮小,用滑鼠點(diǎn)一下才能看到原尺寸,我製作圖會(huì)儘量不超過小屋寬度
另外我寫過的程式教學(xué)散落各處,可能不好找,寫了一篇目錄出來。
Shark流程式教學(xué)一覽
上篇說的那些步驟並不是呼叫一個(gè)函式就做一步,而是像這樣:
繪圖管線像顯卡少女的辦公室有一組工作檯,先呼叫一堆函式指示顯卡少女把設(shè)定值和物件放在工作檯上,然後呼叫draw call才一口氣執(zhí)行那些步驟。draw call不止一種,依不同情況使用。
上篇Input Assembler提到的頂點(diǎn)數(shù)量,以及OpenGL的幾何形狀是在draw call裡指定。
(註:雖然上圖是那樣畫,實(shí)際上套用設(shè)定值和物件的指令也是先儲(chǔ)存在驅(qū)動(dòng)程式,draw call時(shí)才一口氣傳給GPU。)
GPU讀取顯示記憶體很迅速,將資料從主記憶體傳到顯示記憶體比較慢,所以先找時(shí)間把模型、貼圖這些量大的資料傳到顯示記憶體(可以在Now Loading時(shí)做,或是遊戲進(jìn)行時(shí)在背景處理),之後每個(gè)frame只要傳送少量的資料,如「套用3號(hào)模型,5號(hào)貼圖」的指令,或是更新constant buffer。
Direct3D 11
//設(shè)定input assembler context->IASetInputLayout(...); //頂點(diǎn)格式 context->IASetPrimitiveTopology(...); //幾何形狀 context->IASetVertexBuffers(...); //頂點(diǎn)資料 //設(shè)定vertex shader context->VSSetShader(...); context->Map(...); //將矩陣放入constant buffer context->Unmap(...); context->VSSetConstantBuffers(...); //設(shè)定要使用的constant buffer //設(shè)定rasterizer context->RSSetViewports(...); context->RSSetState(...); //culling在此設(shè)定 //設(shè)定pixel shader context->PSSetShader(...); context->PSSetShaderResources(...); //設(shè)定貼圖 context->PSSetSamplers(...); //設(shè)定取樣器(sampler),讀取貼圖要用到這個(gè)東西 //設(shè)定depth test context->OMSetDepthStencilState(...); //設(shè)定blend context->OMSetBlendState(...); //設(shè)定畫布和depth buffer context->OMSetRenderTargets(...); //draw call,到這裡才開始畫 context->Draw(vertexNumber, 0); //頂點(diǎn)數(shù)量在此才設(shè)定 //填入新矩陣 context->Map(...); context->Unmap(...); //切換模型和貼圖 context->IASetVertexBuffers(...); context->PSSetShaderResources(...); //畫第二個(gè)物體 context->Draw(vertexNumber2, 0); |
OpenGL
//設(shè)定input assembler glBindVertexArray(...); //頂點(diǎn)格式和頂點(diǎn)資料 //設(shè)定shader glUseProgram(...); //一個(gè)函式同時(shí)設(shè)定vertex shader和fragment shader glBindTexture(...); //設(shè)定貼圖 glBindSampler(...); //設(shè)定取樣器 glBindBuffer(GL_UNIFORM_BUFFER, ...); //設(shè)定要使用的uniform buffer glBufferSubData(GL_UNIFORM_BUFFER, ...); //將矩陣放入uniform buffer //設(shè)定rasterizer glViewport(...); glEnable(GL_CULL_FACE); glFrontFace(GL_CW); //設(shè)定depth test glEnable(GL_DEPTH_TEST); //設(shè)定blend glEnable(GL_BLEND); glBlendFunc(GL_ONE,GL_ZERO); //設(shè)定畫布和depth buffer glBindFramebuffer(...); glFramebufferTexture2D(...); glFramebufferRenderbuffer(...); //draw call,到這裡才開始畫 glDrawArrays(GL_TRIANGLES,0, vertexNumber); //頂點(diǎn)數(shù)量和幾何形狀在此才設(shè)定 //填入新矩陣 glBufferSubData(GL_UNIFORM_BUFFER, ...); //切換模型和貼圖 glBindVertexArray(...); glBindTexture(...); //畫第二個(gè)物體 glDrawArrays(GL_TRIANGLES,0, vertexNumber); |
這些函式實(shí)際的參數(shù)都落落長(zhǎng),本篇先略過,之後如果我有寫到那部分,實(shí)際用到這些函式再介紹。
draw call後設(shè)定值和物件仍然擺在工作檯上,下一次叫顯卡少女更換才會(huì)改變,所以如果第二個(gè)物體只要更換一部分資料,其他設(shè)定跟前一個(gè)物體相同,只要呼叫一部分函式即可。
前篇放的流程圖是簡(jiǎn)易版,那完整版又是怎麼樣呢?
(這是筆者認(rèn)為比較好講解的版本,網(wǎng)路上其他教學(xué)可能畫得不一樣,可能把某個(gè)步驟再細(xì)分,或把好幾個(gè)步驟合成一個(gè)。
步驟名稱一樣用Direct3D的,有的步驟OpenGL用不一樣的名稱。)
多了很多步驟,shader除了vertex和pixel以外還有g(shù)eometry、hull、domain三種,而且多了個(gè)Stream output,可以把前面那些shader算出的頂點(diǎn)坐標(biāo)存起來,供下次draw call使用。
很多步驟不需要時(shí)可以省略,簡(jiǎn)易版大概就是畫一個(gè)3D物體最低限度必要的工作。
有一堆用語很難懂吧。D3D和OpenGL早期版本功能很簡(jiǎn)單,大概像簡(jiǎn)易版的圖再把shader拿掉。隨著軟體對(duì)畫面的需求越來越多,D3D、OpenGL、和顯卡少女不斷追加新功能,可以做到越來越多的事,但因此變得很複雜,提高初學(xué)門檻。
上圖是D3D11和OpenGL 4版的pipeline,兩者的後繼產(chǎn)品:D3D12和Vulkan有變更一部分。然後D3D12在2018年,Vulkan在2020年新增一種技術(shù):光線追蹤(ray tracing),這是走完全不同的pipeline。
主題講到這裡,接下來講幾個(gè)有關(guān)的項(xiàng)目。
即時(shí)運(yùn)算與預(yù)運(yùn)算
繪製3D場(chǎng)景其實(shí)不只一種方法。像是描述物體形狀的方法除了本篇介紹的三角形面,還有利用曲線稱為NURBS的方法,或是用橢球組合出物體。
在「【程式】電子妖精與顯卡少女的合作——何謂GPU、Direct3D與OpenGL?」有提過標(biāo)準(zhǔn)規(guī)格很重要,如果每家廠商各自開規(guī)格,那在每個(gè)硬體和作業(yè)系統(tǒng)上程式都要重寫一次。
規(guī)格不能一味追求功能強(qiáng)大。在即時(shí)運(yùn)算的場(chǎng)合(如遊戲需要在1/60或1/30秒內(nèi)畫完整個(gè)畫面),不能採(cǎi)用計(jì)算耗時(shí)的演算法,而且功能複雜會(huì)讓軟硬體難以製造,PC、遊戲機(jī)和手機(jī)成本要低,一般使用者才負(fù)擔(dān)得起售價(jià)。在性能和成本之間取捨之後,廠商們定出的標(biāo)準(zhǔn)規(guī)格就是現(xiàn)在的繪圖管線。
相對(duì)地在動(dòng)畫、電影等應(yīng)用,工作室可以買幾臺(tái)高性能電腦讓它們計(jì)算好幾天、輸出影片檔,發(fā)佈只要發(fā)佈影片檔就行了。這時(shí)可以採(cǎi)用高畫質(zhì)但計(jì)算複雜的技術(shù),可能也不用D3D和OpenGL,而是開發(fā)一套專用API。
以上兩種分別是「即時(shí)運(yùn)算」和「預(yù)運(yùn)算」,即時(shí)運(yùn)算容易看情況更改內(nèi)容,但不能產(chǎn)生很精緻或擬真的畫面,預(yù)運(yùn)算則相反。可以觀察遊戲裡的過場(chǎng)動(dòng)畫,奴果精緻度比遊戲其他部分高很多那應(yīng)該是預(yù)運(yùn)算,如果角色變更裝備會(huì)反映在動(dòng)畫裡那大概是即時(shí)運(yùn)算。
(如果精緻度跟其他部分一樣,且角色變更裝備不會(huì)反映在動(dòng)畫裡,……那就無法判斷。)
還有3D軟體像3ds Max和Blender為了讓使用者操作後可以即時(shí)在畫面上反映,編輯時(shí)是用D3D和OpenGL畫出比較簡(jiǎn)單的畫面,render時(shí)才用精確的演算法慢慢算。
下圖是Blender說明書裡的範(fàn)例,左邊的沒有表面紋路、光柱半透明、影子,右邊才有畫出來。
如何用D3D和OpenGL畫2D畫面?
從名稱看起來D3D和OpenGL是專門畫3D的,但現(xiàn)在2D也是用D3D和OpenGL繪製了。
如果要畫一張矩形點(diǎn)陣圖,把傳給顯卡少女的資料改一下:
- CPU將矩形四個(gè)頂點(diǎn)傳給GPU,位置坐標(biāo)只有XY,沒有Z分量。
- CPU和vertex shader裡使用2D數(shù)學(xué)來計(jì)算坐標(biāo)。
只有4個(gè)頂點(diǎn),移動(dòng)和變形可以乾脆完全由CPU算,不靠vertex shader。 - vertex shader輸出的W值設(shè)為1,讓這個(gè)值不起作用。
- 關(guān)閉depth test改用手動(dòng)排序,或者乾脆不建立Z buffer,因?yàn)?D很常用到半透明。
就可以畫2D畫面。2D、3D統(tǒng)一使用D3D和OpenGL繪製可以簡(jiǎn)化規(guī)格和軟硬體,廠商只要做一套系統(tǒng)就可以兩者通用。
DirectDraw本來是在顯示晶片沒有3D的時(shí)代使用,現(xiàn)在已停止增加新功能和改進(jìn)效能,只是為了讓舊程式能繼續(xù)執(zhí)行而保留。
D3D和OpenGL哪個(gè)比較好?
當(dāng)然兩者最大差異是哪個(gè)作業(yè)系統(tǒng)能用,大概只有Windows桌機(jī)版可以同時(shí)用兩者,這裡根據(jù)筆者經(jīng)驗(yàn)講一下性能方面。
以Direct3D 10和OpenGL 3.2以後來說,最底層建立物件的步驟差很多,但從高階一點(diǎn)的需求來看兩者能做的事就差不多,大部分功能都能找到對(duì)應(yīng)。例如都可以建立vertex buffer和framebuffer物件,但呼叫的函式和傳入的參數(shù)差很多。
以前的版本就不一定,有一段時(shí)間是OpenGL落後於D3D,D3D持續(xù)推出新版但OpenGL遲遲沒有跟進(jìn)。
API設(shè)計(jì)方面,OpenGL的風(fēng)格比較古老,寫程式比較容易出錯(cuò)。
舉個(gè)例子,操作物件的時(shí)候,D3D11是呼叫物件的method,或把物件作為參數(shù)傳入函式,每一行都明確指出操作的物件。
//貼圖物件為ID3D11Texture2D* textureObj1; context->UpdateSubresource(textureObj1, 0, NULL, ……); ID3D11ShaderResourceView* shaderResourceView; device->CreateShaderResourceView(textureObj1, NULL, &shaderResourceView); textureObj1->Release(); |
OpenGL通常是在內(nèi)部設(shè)定一個(gè)全域變數(shù),之後的函式讀取這個(gè)全域變數(shù)。
//設(shè)定「GL_TEXTURE_2D」這個(gè)全域變數(shù) glBindTexture(GL_TEXTURE_2D, textureObj1); …… //這些函式讀取GL_TEXTURE_2D得知要操作的貼圖是textureObj1 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ……); |
效能的話根據(jù)筆者經(jīng)驗(yàn),關(guān)鍵是「晶片廠商有沒有認(rèn)真寫驅(qū)動(dòng)程式」,跟API本身關(guān)係不大。
電子妖精送指令給顯卡少女實(shí)際要經(jīng)過以下途徑。
一個(gè)功能即使晶片做得到,作業(yè)系統(tǒng)和驅(qū)動(dòng)程式?jīng)]有配合製作的話,也不能使用。
以下是筆者用2012左右的硬體測(cè)試,有的問題在新版本可能已經(jīng)解決。
筆者有一臺(tái)電腦是Intel Sandy Bridge微架構(gòu),在Windows上如果把相同功能用D3D和OpenGL寫,它的內(nèi)顯在D3D的效能比較好,而且有些功能只有D3D才能用。
可能因?yàn)閃indows的遊戲幾乎都用Direct3D,廠商就花比較多心力在D3D驅(qū)動(dòng)程式,OpenGL的只求有,沒認(rèn)真debug或改進(jìn)效能。
AMD和nVidia的Windows OpenGL驅(qū)動(dòng)程式比較沒這個(gè)問題。
Linux驅(qū)動(dòng)程式的情況是這樣:AMD和nVidia是廠商自行開發(fā),closed-source。這兩家另外有非官方的driver,不過是逆向工程做出來的,性能遠(yuǎn)不及官方版本。
Intel則是把規(guī)格公開,讓網(wǎng)路上的開發(fā)者以open-source的方式開發(fā)。
同一臺(tái)電腦裝Windows和Linux來比較的話,AMD和nVidia晶片在Linux表現(xiàn)比較差,可能因?yàn)楸容^少人做Linux的遊戲,Linux版驅(qū)動(dòng)程式就沒有認(rèn)真寫。有時(shí)候裝了原廠驅(qū)動(dòng)程式還會(huì)沒辦法開GUI,只剩黑底白字的命令列可以用。
Intel的由於有很多Linux使用者改良,驅(qū)動(dòng)程式品質(zhì)比較好,Intel晶片在Linux的表現(xiàn)比在Windows好。
2015年AMD把驅(qū)動(dòng)程式改用open-source的方式開發(fā),品質(zhì)可能有改善,但筆者手上沒有夠新的硬體可試用。
最近一個(gè)經(jīng)驗(yàn)也跟這有關(guān)。筆者的Linux開發(fā)機(jī)有Intel內(nèi)顯和nVidia獨(dú)顯,上個(gè)月在這臺(tái)安裝Mint 19.3,剛灌好只有open source版驅(qū)動(dòng)程式時(shí)還可以開GUI,安裝nVidia官方driver以後就無法開GUI了,照網(wǎng)路上的說明用命令列模式移除driver也不能復(fù)原,只好重灌。
但較早的18.2版沒這個(gè)問題。
查了一下資料,一般桌機(jī)裝兩個(gè)GPU是這樣,顯卡和主機(jī)板各有一組插座,要使用哪個(gè)GPU就把螢?zāi)痪€插到哪裡。
筆電的Optimus技術(shù)是這樣,只有內(nèi)顯輸出到螢?zāi)唬锚?dú)顯繪製時(shí)會(huì)先在獨(dú)顯畫出整個(gè)畫面,把畫面複製到內(nèi)顯記憶體,再傳給螢?zāi)弧?br>
驅(qū)動(dòng)程式要特別寫才做得到這個(gè)功能,可能nVidia的Linux版驅(qū)動(dòng)程式?jīng)]有好好寫,不是每個(gè)發(fā)行版都能正常運(yùn)作。
如果使用非官方驅(qū)動(dòng)程式,不但不能用獨(dú)顯,而且獨(dú)顯閒置時(shí)也不能節(jié)省電力,電腦會(huì)變得很燙。所以我寧願(yuàn)放棄獨(dú)顯了,修改BIOS設(shè)定調(diào)成只用內(nèi)顯。
有聽說nVidia本來就對(duì)Linux社群比較不友善。目前不急著買新電腦,這臺(tái)先將就著用,但以後Linux開發(fā)機(jī)我要用Intel或AMD的晶片。
偽3D是什麼?
這個(gè)詞沒有明確的定義,只是玩家間的俗稱,所以如果看到兩個(gè)人爭(zhēng)論某個(gè)東西是不是偽3D,吵半天得不出共識(shí),這很正常。
如果真要問筆者的看法,按照上篇「polygon或曲線 → 坐標(biāo)轉(zhuǎn)換 → rasterizer → 計(jì)算顏色 → 輸出」的流程繪製的是真3D,用其他方法產(chǎn)生遠(yuǎn)近感的是偽3D。
很多SFC和Mega Drive遊戲可見到一種效果:讓背景用較慢速率捲動(dòng)產(chǎn)生背景比較遠(yuǎn)的感覺。由於背景只是單張圖片,並沒有建模型用3D數(shù)學(xué)計(jì)算,這是一種偽3D。
紅白機(jī)的兩個(gè)遊戲:Mach Rider和Rad Racer,紅白機(jī)並沒有處理polygon的功能,也是用偽3D做出遠(yuǎn)近感。
當(dāng)時(shí)繪製畫面的方法是畫面由很多橫向掃瞄線組成,逐條畫出,每畫一條掃瞄線之後把下一條橫向偏移,就能讓圖彎曲,紅白機(jī)有這個(gè)功能。
這個(gè)技巧在日本叫raster scroll(ラスタースクロール),英文好像叫l(wèi)ine scrolling。
因?yàn)槭沁@種方法,遊戲畫面表現(xiàn)不出交差或距離很近,照理說可以看到另一條路的情況。也只能把圖橫向變形,紅白機(jī)看不到用這個(gè)技巧把圖縱向變形的遊戲。
找到這篇舉了幾個(gè)Mega Drive的例子:Line Scrolling - Raster Scroll Books
鐵桶之類的障礙物也不是建個(gè)圓柱形的模型,而是準(zhǔn)備數(shù)張大小不同的圖片看時(shí)機(jī)切換(紅白機(jī)是沒有縮放功能的)。
關(guān)於插圖:
很多教學(xué)裡畫暗處的常用方法是用乘法(multiply)疊上顏色,這張圖改用線性加深(linear burn)試試看。
乘法的算式:A×B。
A、B代表RGB分量,範(fàn)圍是0~1。
假如底色是(1, 0.75, 0.5),max-min=0.5
疊上灰色(0.6, 0.6, 0.6)
會(huì)變成(0.6, 0.45, 0.3),max-min=0.3
RGB差異減少代表彩度下降,所以乘法模式容易讓整張圖變得灰暗,避免的方法是針對(duì)每個(gè)底色的特性各別選擇重疊的顏色。
線性加深的算式:A+B-1,如果<0就設(shè)為0。
底色(1, 0.75, 0.5),max-min=0.5
疊上灰色(0.6, 0.6, 0.6)
變成(0.6, 0.35, 0.1),max-min=0.5
彩度比較不易下降,所以試試看這個(gè)模式,看能不能畫暗處一個(gè)顏色用到底,只有少數(shù)部位各別選色。
但這個(gè)模式可能讓分量變成0,容易讓整張圖一片黑,還是免不了要看底色各別選色。試一試覺得還是乘法模式比較易用。
另外試用一下GIMP 2.99。這版把依存的GTK 2和Python 2換成GTK 3和Python 3,Inkscape之前就已經(jīng)把這兩個(gè)函式庫(kù)換新,GIMP也更新之後,我的電腦上就不再需要GTK 2和Python 2了。
不過發(fā)現(xiàn)2.99版的Python API改變,script必須重寫,所以暫時(shí)還是用2.10,等這張圖畫完再改用2.99。
右下的遊戲畫面放大
因?yàn)榈谒母竦倪[戲邏輯寫的是RPG的戰(zhàn)鬥,這一格就這樣畫了。
角色是把目前設(shè)計(jì)出的顯卡少女挑三個(gè)換服裝,但角色在圖中很小,不容易看出誰是誰。
電子妖精和顯卡少女的工作室長(zhǎng)怎麼樣、工作時(shí)用什麼工具等等的,這張圖畫得還比較隨便,但如果之後要?jiǎng)?chuàng)作一系列作品,要把這些背景設(shè)定定個(gè)規(guī)格,以免不同作品之間有矛盾。