Shader從入門到跑路:顏色自定義輸出、紋理取樣

遊資網發表於2020-03-27
Shader從入門到跑路:顏色自定義輸出、紋理取樣


前言

我們來加深一下目前理解到的內容,並且著手解決一些在學習過程中遇到的疑惑(如果有疑惑但我沒提到歡迎在評論留下我會隨後加上)。

首先,以往我們寫程式是在CPU上跑,但寫shader則是在GPU上跑,我們會用兩對好兄弟,分別是vertex shader和fragment shader。

vertex shader是每個頂點都會呼叫一次的程式,它可以訪問頂點的位置、法線、紋理等資訊,這些值在經過一些處理之後,會先跑一個叫做“光柵化(Rasterization)”的過程。我們都知道,顯示器是由紅綠藍 LED 組成的顯示單元,因此單元之間是離散的,而“連續”的三維影象要渲染到螢幕上,就要經過一系列的修改,稱之為光柵化。

此時得到就是片元(fragment),我之前說過片元與最終的影象單位是非常接近的(我甚至鼓勵大家以圖形的方式來理解它們實在是抱歉),但它們之間仍存在著微妙的差別。這是因為片元需要先通過一些測試,比如深度測試、透明度測試等,以決定它們是需要被使用還是被拋棄。其實在這裡並不需要太糾結片元到底是不是畫素,因為這不是這個系列的重點。

而由每個片元分別呼叫一次的就是fragment shader,它可以獲取片元的二維、深度、顏色等資訊。注意,fragment shader獲取的片元顏色是根據片元的三個頂點顏色插值計算後得到的,也就是說如果三個頂點顏色並不相同,那麼最終顏色便是漸變的。

shader的計算是在CGPROGRAM內完成的,並且應該符合以下結構

  1. struct appdata{};  // 獲取模型資料
  2. struct v2f{};  // 存放計算結果
  3. v2f vert(appdata v){}  // 拿取模型資料計算後傳給fragment shader
  4. float4 frag(v2f i) : SV_Target{}  // 拿取計算結果作用於目標
複製程式碼

注意,在結構體內定義的輸入輸出是由語義限定的,舉個栗子:

  1. struct appdata{
  2.      float4 vertex : POSITION;  // 把模型頂點座標填充到vertex中
  3.      float2 uv : TEXCOORD1;  // 第一組紋理座標
  4.      float3 normal : NORMAL;  // 把法線方向填充到normal中
  5. };
  6. struct v2f{
  7.      float4 vertex : SV_POSITION;  // 輸出到裁剪空間中的頂點座標
  8.      float3 color : COLOR; // 輸出顏色
  9. };
  10. float4 frag(v2f i) : SV_Target{ // frag輸出值直接用於渲染
  11.      return float4(i.color, 1.0);  
  12. }
複製程式碼

語義是很聰明的一種設計,它能夠讓Shader知道從哪裡獲取資料,並且還知道把輸出的資料放到哪裡(或者怎麼放)

問題1:頂點位置是一種座標系,uv也是一種座標系,既然都是座標為什麼不能只算一個多方便啊?

好問題!(拍大腿)這兩種座標系的應用是不一樣的,頂點是一個和網格(mesh)相關的區域性點,以xyz為座標軸;而uv則是貼圖對映到模型表面的依據,以uvw為座標軸,或者可以將uv看作一張紋理查詢表格。因為xyz軸已經被網格佔用了,所以貼圖用的座標軸就只能用uvw了,簡稱uv。很多同學看了一些shader教程經常見到它們兩一齊出現,自己寫的時候不知所以索性就一齊計算了。

Shader從入門到跑路:顏色自定義輸出、紋理取樣
5.4 魯迅名言

問題2:有些shader程式的frag函式輸出的COLOR,而另一些則是SV_TARGET,有什麼區別啊?

沒啥不同,SV開頭的是Direct3D定義的一種System Value語義,SV_TARGET對應的就是COlOR,但有些平臺支援SV語義,而有些則不支援。但這並不重要,因為編譯器會自動幫我們做轉換,它們兩的功能是一樣的。類似還有SV_POSITION與POSITION,SV_DEPTH與DEPTH等等。反正無腦寫SV_Target就沒錯了(狗頭)

自我小測:

在寫過一些shader之後,我們已經是個shader程式設計師了,當然要學會自己寫shader咯,下面是一些可以努力的方向,並獲取圖中的渲染效果(因為是讀者自我測驗,因此筆者不會提供答案,但如果實在想不明白可以私信筆者問思路)

練習1 : 新增一個_Color屬性,並將其與主紋理的顏色相乘

Shader從入門到跑路:顏色自定義輸出、紋理取樣

注:顏色在屬性區塊內是以Color型別存在,在CGPROGRAM使用內需被預定義為float4型別

練習2 : 用第二章講到,用uv值計算顏色值的方法,將其結果與主紋理的顏色想乘,獲得下面的效果

Shader從入門到跑路:顏色自定義輸出、紋理取樣

練習3 : 使用兩張紋理,以及第五章講到的拖動條,當移動滑桿時在兩張紋理中進行插值

Shader從入門到跑路:顏色自定義輸出、紋理取樣

練習4 : 在圖片的設定皮膚內將圖片的wrap mode設為repeat。在對紋理取樣時,將uv乘2

Shader從入門到跑路:顏色自定義輸出、紋理取樣

練習5 : 使用明亮度(luminance)公式來算出一個明亮度值,然後將紅綠藍通道都設定為明亮度值,透明度通道則用對圖片取樣獲得的透明度,最終獲得灰度效果

Shader從入門到跑路:顏色自定義輸出、紋理取樣

注: luminance = 0.2125 * Red + 0.07154 * Green + 0.0721 * Blue

練習6 : 將練習5的返回顏色與自定義的顏色屬性相乘

Shader從入門到跑路:顏色自定義輸出、紋理取樣


相關閱讀:
Shader從入門到跑路:樸實無華的圖形學基礎
Shader從入門到跑路:自定義紋理輸入
Shader從入門到跑路:螢幕後處理效果

Shader從入門到跑路:實作螢幕扭曲效果

作者:俊銘
專欄地址:https://zhuanlan.zhihu.com/p/86192364

相關文章