第四章 開始Unity Shader學習之旅(3)

2021-09-19 12:00:50 字數 4290 閱讀 6973

除錯(debug),大概是所有程式設計師的噩夢。而不幸的是,對乙個shader進行除錯更是噩夢中的噩夢。這也是造成shader難寫的原因之一——如果發現得到的效果不對,我們就可能花非常多的時間來找到問題所在。造成這種現狀的原因就是在shader中可以選擇的除錯方法非常有限,甚至連簡單的輸出都不行。

unity5除了帶來全新的ui系統外,還為我們帶來了乙個新的針對渲染的偵錯程式——幀偵錯程式(frame debugger)。與其它除錯工具的複雜性相比,unity原生的幀偵錯程式非常簡單快捷。我們可以用它來看到遊戲影象的某一幀是如何一步步渲染出來的。

要使用幀偵錯程式,我們首先需要在window->frame debugger中開啟幀偵錯程式視窗,如下圖所示:

幀偵錯程式可以用於檢視渲染該幀時進行的各種渲染事件(event),這些事件包含了draw call序列,也包括了類似清空快取等操作。幀偵錯程式視窗大致可分為3個部分:最上面的區域可以開啟/關閉(單擊enable按鈕)幀除錯功能,當開啟了幀除錯時,通過移動視窗最上方的滑動條(或單擊前進和後退按鈕),我們就可以重放這些渲染事件;左側區域顯示了所有事件的樹狀圖,在這個樹狀圖中,每個葉子節點就是乙個事件,而每個父節點的右側顯示了該節點下的事件數目。我們可以從事件的名字了解這個事件的操作,例如以draw開頭的事件通常就是乙個draw call;當單擊了某個事件時,在右側的視窗就會顯示該事件的細節,例如幾何圖形的細節以及使用了哪個shader等。同時在game檢視中我們也可以看到它的效果。如果該事件是乙個draw call並且對應了場景中的乙個gameobject,那麼這個gameobject也會在hierarchy檢視中被高亮顯示出來,下圖顯示了單擊渲染某個物件的深度圖事件的結果。

如果被選中的draw call是乙個對渲染紋理(rendertexture)的渲染操作,那麼這個渲染紋理就會顯示在game檢視中。而且,此時右側面板上方的工具欄中也會出現更多的選項,例如在game檢視中單獨顯示r、g、b和a通道。

unity5提供的幀偵錯程式實際上並沒有實現乙個真正的幀拾取(frame capture)的功能,而是僅僅使用了停止渲染的方法來檢視渲染事件的結果。例如我們想要檢視第4個draw call的結果,那麼幀偵錯程式就會在第4個draw call呼叫完畢後停止渲染。這種方法雖然簡單,但得到的資訊也有限。

我們以前提到過opengl和directx的螢幕空間座標的差異,如下圖所示:

需要注意的是,我們不僅可以把渲染結果輸出到螢幕上,還可以輸出到不同的渲染目標(render target)中。這時,我們需要使用渲染紋理(render texture)來儲存這些渲染結果。我們將在後面學習如何實現這樣的目的。

大多數情況下,這樣的差異並不會對我們造成任何影響。但當我們要使用渲染到紋理技術,把螢幕影象渲染到一張渲染紋理時,如果不採取任何措施的話,就會出現紋理翻轉的情況。幸運的是,unity在背後為我們處理了這種翻轉問題——當在directx平台使用渲染到紋理技術時,unity會為我們翻轉螢幕影象紋理,以便在不同平台上達到一致性。

在一種特殊情況下unity不會為我們進行這個翻轉操作,這種情況就是我們開啟了抗鋸齒(在edit->project setting->quality->anti aliasing中開啟)並在此時使用了渲染到紋理技術。在這種情況下,unity首先渲染得到螢幕影象,再由硬體進行抗鋸齒處理後,得到一張渲染紋理來供我們進行後續處理。此時,在directx平台下,我們得到的輸入螢幕影象並不會被unity翻轉,也就是說,此時對螢幕影象的取樣座標是需要符合directx平台規定的。如果我們的螢幕特效只需要處理一張渲染影象,我們仍然不需要在意紋理的翻轉問題,這是因為我們在呼叫graphics.blit函式時,unity已經為我們對螢幕影象的取樣座標進行了處理,我們只需要按照正常的取樣過程處理螢幕影象即可。但如果我們需要同時處理多張渲染影象(前提是開啟了抗鋸齒),例如需要同時處理螢幕影象和法線紋理,這些影象在豎直方向的朝向可能是不同的(只有在directx這樣的平台上才有這樣的問題)。這種時候,我們就需要自己在頂點著色器中翻轉某些渲染紋理(例如深度紋理或其它由指令碼傳遞過來的紋理)的縱座標,使之都符合directx平台的規則。例如:

#if unity_uv_starts_at_top

if(_maintex_texelsize.y<0)

uv.y = 1- uv.y;

#endif

其中,unity_uv_starts_at_top用於判斷當前平台是否是directx型別的平台,而在這樣的平台下開啟了抗鋸齒後,主紋理的紋理大小在豎直方向上會變成負值,以方便我們對主紋理進行取樣。因此,我們可以通過判斷_maintex_texelsize.y是否小於0來檢驗是否開啟了抗鋸齒。如果是,我們就需要對主紋理外的其他紋理的取樣進行豎直方向上的翻轉。

我們使用cg/hlsl來編寫unityshader中的**。而在cg/hlsl中,有三種精度的數值型別:float,half和fixed。這些精度將決定計算結果的數值範圍。下表給出了這3種精度在通常情況下的數值範圍。

cg/hlsl中3種精度的數值型別 型別

精度float

最高精度的浮點值。通常用32位來儲存

half

中精度的浮點值。通常用16位來儲存,精度範圍是-60000~+60000

fixed

最低精度浮點值。通常用11位來儲存,精度範圍是-2.0~+2.0

temporary register limit of 8 exceeded

arithmetic  instruction limit of 64 exceeded;65  arithmetic instructions needed  to compile program
出現這些錯誤資訊大多是因為我們在shader中進行了過多的運算,使得需要的臨時暫存器數目或指令數目超過了當前可支援的數目。讀者需要知道,不同的shader target、不同的著色器階段,我們可以使用的臨時暫存器和指令數目都是不同的。

通常,我們可以通過制定更高階的shader target來消除這些錯誤。下表給出了unity目前支援的一些

shader target。

unity支援的shader target 指令

描述#pragma target 2.0

預設的shader target等級。相當於direct3d 9上的shader model1.0,不支援對頂點紋理的取樣,不支援顯示的lod紋理取樣等

#pragma target 3.0

相當於direct3d上的shader model 3.0,支援對頂點紋理的取樣等

#pragma target4.0

相當於direct3d10上的shader model4.0,支援幾何著色器等

#pragma target 5.0

相當於direct3d 11上的shader model5.0

需要注意的是,由於unity版本的不同,unity支援的shader target種類也不同,讀者可以在官方手冊上找到更為詳細的介紹。 讀者:什麼是shader model呢? 我們:shader model是由微軟提出的一套規範,通俗的理解就是它們決定了shader中各個特性(feature)的能力(capability)。這些特性和能力體現在shader能使用的運算指令數目、暫存器數目等各個方面。shader model等級越高,shader的能力越大。 雖然更高等級的shader target可以讓我們使用更多的臨時暫存器和運算指令,但乙個更好的方法是盡可能減少shader中的運算,或者通過預計算的方式來提供更多的資料。 ### 4.3 慎用分支和迴圈語句 在最開始,gpu是不支援在頂點著色器和片元著色器中使用流程控制語句的。隨著gpu的發展,我們現在已經可以使用if-else、for和while這種流程控制指令了。大體來說,gpu使用了不同於cpu的技術來實現分支語句,在最壞的情況下,我們花在乙個分支語句的時間相當於執行了所有分支語句的時間。因此,我們不鼓勵在subshader中使用流程控制語句,因為它們會降低gpu的並行處理操作。 如果我們在shader中使用了大量的流程控制語句,那麼這個shader的效能會成倍下降。乙個解決方法是,我們應該盡量把計算向流水線上方移動,例如把放在片元著色器中的計算放到頂點著色器中去,或者直接在cpu中進行預計算,再把結果傳遞給shader。當然,有時我們不可避免的要使用分支語句來進行計算,那麼一些建議是: (1)分支判斷語句中使用的條件變數最好是常數,即在shader執行過程中不會發生變化; (2)每個分支中包含的操作指令數盡可能少; (3)分支的巢狀層數盡可能少。

第四章 繼承

一 為什麼要繼承 在物件導向中我們將具有很多重複內容的類中的內容提取出來,寫成乙個單獨的類 其他類只需要繼承就能取得這些功能,同時可以在自己類中寫入獨特的自定義方法 二 繼承語法 inte ce circle nsobject 繼承是在介面中定義的 冒號後的類名是要整合的類,nsobject 是co...

第四章 物件

三個特性 身份 型別 值 每個物件都有唯一的身份來標識自己,使用內建函式id 得到。例子 usr bin env python coding utf 8 a 32 print a b a print id a id b 結果 d python27 python.exe e workp python ...

第四章 其他

sizeof和strlen 區別sizeof以位元組為單位給出資料的大小,strlen 函式以字元為單位給出字串的長度。使用strlen 函式要加 include標頭檔案。sizeof計算字元時會將標誌字串結束的不可見的空字元計算在內。定義符號常量 方法一 define name value 優點 ...