多年前nvidia就發布了3d vision技術,能提供多種立體渲染的效果。隨著2023年的電影阿凡達所帶來的世界性3d狂潮,你是否也想在自己的程式中加入立體渲染呢?
根據 vision的原理如下:
注意加粗的幾個詞所透露出來的資訊。首先,你的每乙個draw call都被驅動變成了兩個draw call;其次,立體化的過程是自動的,無法自由控制;第三,它只能處理典型的vertex shader,而不是任意的vertex shader,比如sky box的vertex shader,就往往是個「非典型」的。nvidia的思路就是,把一切都封裝起來,只有個別引數可以讓開發人員和使用者調整。程式能做的事就只能是把一切 交給驅動,祈禱最終結果正確,極其被動。
其實,立體渲染沒有那麼stupid。比如,圖形引擎可以分別從左右眼主動生成2張影象,都是用正確的vertex shader,然後交給圖形api。這麼做就可以保證整條流水線都是支援立體的,包括視錐裁剪,結果就是所有物體均能100%渲染正確。而不會像3d vision的方法,不但vertex shader必須「典型」,還得沒法處理被場景管理器裁掉的物體(比如左眼能看到,右眼看不到的物體)。再比如crytek在cryengine3裡的立 體渲染方法,把生成的單一影象通過image warp的方式分別得到左右眼的結果,不必渲染2遍,就能在效能基本不降低的情況下進行立體渲染。這些方式都不能使用自動的3d vision,必須要通過生成2張image的方式才能實現。
既然放棄了自動的3d vision,我們就來開始探索如何手動把兩張圖提交給驅動,控制驅動產生立體渲染的方法。
3d vision並沒有提供提交2張影象的方法,那就看看師出同門的nvapi。nvapi是nvidia提供的乙個sdk,可以直接訪問gpu和驅動的功能。本來我滿懷欣喜地認為nvapi一定給出了手動控制立體渲染的方法,結果發現公開版本的nvapi最多也就提供了開啟和關閉立體渲染這樣的功能,並不能實現我們想要的。根據 在專有版本的nvapi(也就是簽署了nda的版本)包含有顯式控制的功能。但這個專有的版本很難申請到,對於大公司還好,對於小作坊、業餘開發的愛好者 之流,就根本沒機會了(經常是提交申請後什麼反應都沒有)。對於開源開發就成了噩夢,nda的東西無法隨著開源軟體一起發布,所以這條路成了死胡同。難道 就沒有辦法了嗎?
opengl本身就提供了gl_left_back、gl_right_back、gl_left_front和gl_right_front四個緩衝區,內建了立體的支援。但這種方法的缺點也是明顯的:
只支援opengl。遊戲主流的d3d均無法使用quad buffer。
在windows上,一般的opengl驅動均不支援quad buffer,只有quadro支援。
可以認為,這條路也失敗了。難道,就真的沒有辦法了嗎?
根據前面找到的材料,3d video的處理方法如下:
把左右眼影象拷入一張大紋理中,大紋理的寬為w * 2,高為h + 1(w和h分別是原影象的寬和高)。左眼在左邊,右眼在右邊。
大紋理的最後一行加入特別的標誌(此為關鍵所在)。用stretchrect把大紋理拷入back buffer。
當back buffer顯示出來的時候就是立體的了。
看來,一切玄機盡在「特別的標誌」中。正是那個標誌,讓驅動把紋理識別為立體影象,在stretchrect和present的時候做特殊處理。該標誌的定義是這樣的:
// stereo blit defines填充的方式:#define nvstereo_image_signature 0x4433564e //nv3d
typedef struct _nv_stereo_image_header
nvstereoimageheader, *lpnvstereoimageheader;
// ored flags in the dwflags fiels of the _nv_stereo_image_header structure above
#define sih_swap_eyes 0×00000001
#define sih_scale_to_fit 0×00000002
d3dlocked_rect lr;這個標誌可以在紋理建立的時候就填充上,以後每一幀只需要把左右眼的影象拷進去就行了。psurf->lockrect(&lr, null, 0);
// 填到最後一行
lpnvstereoimageheader psih = reinterpret_cast(static_cast(lr.pbits) + (lr.pitch * height));
psih->dwsignature = nvstereo_image_signature;
psih->dwbpp = 32;
psih->dwflags = sih_swap_eyes;
psih->dwwidth = gimagewidth*2;
psih->dwheight = gimageheight;
psurf->unlockrect();
前面舉得例子用的是d3d 9,而在d3d10/11中,情況又會如何呢?d3d10/11沒有了stretchrect,取而代之的是copyresource和copysubresourceregion,兩者都沒有縮放的能力。不管了,試試再說:
d3d11_box box;結果成功了!nv的驅動仍然會根據那個標誌來特殊化copysubresourceregion。box.left = 0;
box.right = w;
box.top = 0;
box.bottom = h;
box.front = 0;
box.back = 1;
d3d_imm_ctx->copysubresourceregion(backbuffer, 0, 0, 0, 0, surf, 0, &box);
至此,d3d9/d3d10/d3d11/opengl下都可以通過2張影象來控制3d vision顯示出立體效果,你的程式也可以用同樣的方法快步進入立體的行列。在klayge 3.11中,stereo模式也使用本文所述的方法。
在分別獲得左右眼影象的過程中,應該是要關閉3d vision的,否則左右眼影象也都會分別變成立體的,效能大減。通過nvapi的nvapi_stereo_deactivate確實可以關閉3d vision,但這樣的話連最後需要3d vision的stretchrect等也失效了。由於某些原因nvapi_stereo_activate這個函式出現在標頭檔案和庫檔案中,卻沒出現在 文件裡,對該函式的呼叫似乎需要等到下一幀才會啟用3d vision。結果還是不正確。暫時的解決方法是把左右眼影象略微減小,比如高度從h變成h – 2,驅動發現小於front buffer的渲染就會暫時關閉3d vision。
更多遊戲引擎、圖形程式設計、業界資訊,請見
在程式中使用NV 3D Vision
多年前nvidia就發布了3d vision技術,能提供多種立體渲染的效果。隨著2009年的電影阿凡達所帶來的世界性3d狂潮,你是否也想在自己的程式中加入立體渲染呢?根據 vision的原理如下 注意加粗的幾個詞所透露出來的資訊。首先,你的每乙個draw call都被驅動變成了兩個draw call...
在程式中使用NV 3D Vision
多年前nvidia就發布了3d vision技術,能提供多種立體渲染的效果。隨著2009年的電影阿凡達所帶來的世界性3d狂潮,你是否也想在自己的程式中加入立體渲染呢?根據 vision的原理如下 注意加粗的幾個詞所透露出來的資訊。首先,你的每乙個draw call都被驅動變成了兩個draw call...
3 在Shell程式中使用的引數
學習目標 位置引數 內部引數 如同ls命令可以接受目錄等作為它的引數一樣,在shell程式設計時同樣可以使用引數。shell程式中的引數分為位置引數和內部引數等。12 3 1 位置引數 由系統提供的引數稱為位置引數。位置引數的值可以用 n得到,n是乙個數字,如果為1,即 1。類似c語言中的陣列,li...