閱讀217 返回首頁    go 技術社區[雲棲]


【Aladdin Unity3D Shader編程】之一 基本入門

OpenGL、DirectX以及GLSL、HLSL、CG

  • OpenGL和DirectX是圖像應用編程接口,用於渲染二維或者三維圖形。
  • GLSL著色語言是用來在OpenGL中著色編程的語言,有點在於跨平台性,可以再Windows、Linux、Mac甚至移動平台上工作。
  • HLSL是微軟控製著色的編譯,幾乎隻支持微軟自己的產品,如Windows,XBox等,其他平台沒有可編譯HLSL的編譯器。
  • CG是有英偉達公司出的真正意義上的跨平台著色器語言。

GPU渲染管線概述

1.頂點著色器

頂點著色器是流水線的第一個階段,它的輸入來自於CPU。頂點著色器的處理單位是頂點,也就是說輸入進來的每個頂點都會調用一次頂點著色器。

頂點著色器需要完成的工作主要有:坐標變換和逐頂點光照。當然,除了這兩個主要任務外,頂點著色器還可以輸出後續階段所需的數據。
坐標變換,顧名思義,就是對頂點的坐標進行某種變換。例如我們可以通過改變頂點位置來模擬水麵,布料等。

一個最基本的頂點著色器必須完成的一個工作是:把頂點坐標從模型空間轉換到齊次剪裁空間。類似下麵代碼:

2.裁剪

由於我們的場景可能會很大,而攝像機的視野範圍很有可能不會覆蓋所有的場景物體,一個很自然的想法就是,那些不在攝像機視野範圍內的物體不需要被處理,而裁剪就是為了完成這個目的而被提出來的。
一個圖元和攝像機的關係有3種:

  • 完全在視野裏
  • 部分在視野裏
  • 完全在是野外 部分在視野內的圖元需要裁剪,例如一條線段的一個頂點在視野內,而另一個頂點在視野外,那麼視野外部的頂點應該使用一個新的頂點來代替,這個新的頂點位於這條線段和視野邊界的交點處。

3.屏幕映射

這一步輸入的坐標仍然是三維坐標係。屏幕映射的任務是把每個圖元的x和y坐標轉換到屏幕坐標係下,屏幕坐標係是一個二維坐標係,它和我們用於顯示畫麵的分辨率有很大關係。

屏幕映射得到的屏幕坐標決定了這個頂點對應屏幕上哪個像素以及距離這個像素有多遠。

opengl的屏幕坐標原點是左下角,而directx是左上角,如果你發現你得到的圖像是倒轉的,那麼很有可能就是這個原因造成的。

4.三角形設置

由這一步就進入了光柵化階段,從上一個階段輸出的信息是屏幕坐標下的頂點位置以及和它們相關的額外信息,如深度值、法線方向、視角方向等,光柵化有兩個最重要的目標:計算每個圖元覆蓋了哪些像素,以及為這些像素計算他們的顏色。光柵化的第一個流水線階段是三角形設置,這個階段會計算光柵化一個三角網格所需的信息。

具體來說,上一個階段輸出的都是三角網格的頂點,即我們得到的是三角網格每條邊的兩個端點。但如果要得到整個三角網格對像素的覆蓋情況,我們就必須計算每條邊上的像素坐標。為了能夠計算邊界像素的坐標信息,我們就需要得到三角形邊界的表示方式。這樣一個計算三角形網格表示數據的過程就叫做三角形設置,它的輸出是為了下一個階段做準備。

5.三角形遍曆

三角形遍曆階段將會檢查每個像素是否被一個三角形網格所覆蓋。如果被覆蓋的話,就會生成一個片元,而這樣一個找到哪些像素被三角網格覆蓋的過程就是三角形遍曆,這個階段也被稱為掃描變換。

三角形遍曆階段會根據上一個階段的計算結果來判斷一個三角網格覆蓋了哪些像素,並使用三角網格3個頂點的頂點信息對整個覆蓋區域的像素進行插值。

6.片元著色器

片元著色器是另一個非常重要的可編程著色器階段,片元著色器的輸入是上一個階段對頂點信息插值得到的結果,更具體來說,是根據那些從頂點著色器輸出的數據插值得到的。而它的輸出是一個或多個顏色值。

這一階段可以完成很多重要的的渲染技術,其中最重要的技術之一就是紋理采樣。為了在片元著色器中進行紋理采樣,我們通常會在頂點著色器階段輸出每個頂點對應的紋理坐標,然和經過光柵化階段對三角網格的3個頂點對應的紋理坐標進行插值後,就可以得到其覆蓋的片元的紋理坐標了。

7.逐片元操作

逐片元操作是opengl中的說法,在directx中,這一階段被稱為輸出合並階段。

這一階段有幾個主要任務:

  • 決定片元的可見性。涉及很多測試工作,如深度測試,模版測試等。
  • 如果一個片元通過了所有的測試,就需要把這個片元的顏色值和已經存儲在顏色緩衝區中的顏色進行合並

Unity Shader的分類

  • 表麵著色器 Surface Shader
  • 頂點/片元著色器 Vertex/Fragment Shader
  • 固定函數著色器 Fixed Function Shader (已棄用) 在一些低端設備使用 表麵著色器是對頂點/片元著色器做的一層封裝。

Unity Shader的基本結構

Shader "Aladdin/01 First Shader" //指定Shader路徑和名字
{
    Properties //屬性
    {
        _Color("Color",Color)=(1,1,1,1)
    }
    SubShader //SubShader可以寫很多個 顯卡運行效果的時候 從第一個開始,如果第一個SubShader裏麵的效果都可以實現就使用第一個SubShader,如果顯卡這個SubShader有的實現不了會往下找支持的SubShader
    {
        //至少含有一個Pass
        Pass {
            //在這裏編寫Shader代碼 HLSLPROGRAM
            CGPROGRAM
            //使用CG語言編寫Shader代碼
            ENDCG
        }
    }
    FallBack  "VertexLit" //如果上麵SubShader都不支持 則執行默認的Shader效果
}

Unity Shader屬性和使用

屬性

常用屬性

Color("Color",Color)=(1,1,1,1)
_Vector("Vector",Vector)=(1,2,3,4)
_Int("Int",Int)=2
_Float("Float",Float)=12.3 //不用加f
_Range("Range",Range(1.0,10.0))=1.0 //範圍類型
_2D("Texture",2D)="white"{} //white是默認值,如果不選圖的話 就是默認白色的圖
_Cute("Cute",Cube)="red"{} //如果用天空盒就用Cube 立方體紋理
_3D("Texture",3D)="black"{} //3D紋理

這裏寫圖片描述

使用


float4 _Color;//float4就是四個值 _Color要跟上麵屬性名字保持一致
float4 _Vector;
float _Int;
float _Range;
sampler2D _2D;
samplerCube _Cube;
sampler3D _3D;```  

#### float、half和fixed類型區別
float也可以用half和fixed代替
float 32位來存儲
half 16位來存儲 -6萬~+6萬
fixed 11位來存儲 一般都是用fixed

## 頂點、片元函數編寫
#### vert函數

//頂點函數定義 這裏隻是聲明了頂點函數的函數名
//基本作用是完成頂點坐標從模型空間到剪裁空間的轉換(從遊戲環境到視野相機屏幕上)

pragma vertex vert

//通過語義告訴係統我這個參數是幹嘛的比如POSITION是模型坐標語義,告訴係統我需要頂點參數坐標 SV_POSITION是剪裁坐標語義
float4 vert(float4 v:POSITION) :SV_POSITION
{
return mul(UNITY_MATRIX_MVP, v); //計算模型坐標轉換成剪裁坐標
}

#### fragment函數

//片源函數定義 這裏隻是聲明了片元函數的函數名
//基本作用是返回模型對應屏幕上的每一個像素顏色

pragma fragment frag

//片元函數定義
float4 frag():SV_Target //SV_target是返回顏色語義
{
return fixed4(1,1,1,1);
}

片元可以理解成一個像素,每一個像素都會經過片元函數的處理

## NORMAL、TEXCOORD0語義

//a2v application to vertex
struct a2v
{
//頂點坐標
float4 vertex:POSITION; //添加上POSITION語義,這樣操作係統才知道給這個變量賦值模型坐標變量
//法線
float3 normal:NORMAL; //告訴unity把模型空間下的法線方向向量填充給normal變量
//紋理坐標(模型坐標對應貼圖的坐標)
//紋理坐標一般都是0-1 不按照實際的像素來的
float4 texcoord:TEXCOORD0; //告訴unity把模型空間下的紋理坐標填充給texcoord變量
};

//通過語義告訴係統我這個參數是幹嘛的比如POSITION是模型坐標語義,告訴係統我需要頂點參數坐標 SV_POSITION是剪裁坐標語義
float4 vert(a2v v) :SV_POSITION
{
return mul(UNITY_MATRIX_MVP, v.vertex); //計算模型坐標轉換成剪裁坐標
}

## 片元函數和頂點函數之間傳數據
使用結構體作為參數和返回值,這樣可以添加任意多個返回值或者傳入值,會更方便一些。
為什麼要傳參?
因為有些參數隻能比如頂點、法線等隻能在頂點函數中訪問到,片元函數是訪問不到的,如果片元函數要訪問頂點法線等參數隻能從頂點函數中返回並且作為參數傳入給片元函數才行。

Shader "AladdinShader/03 Struct Shader"
{
Properties
{

}
SubShader {
    Pass 
    {
    CGPROGRAM

// Upgrade NOTE: excluded shader from DX11; has structs without semantics (struct v2f members tempNormal)

pragma exclude_renderers d3d11

pragma vertex vert

pragma fragment frag

    //a2v  application to vertex
    struct a2v 
    {
        //頂點坐標
        float4 vertex:POSITION; //添加上POSITION語義,這樣操作係統才知道給這個變量賦值模型坐標變量
        //法線
        float3 normal:NORMAL; //告訴unity把模型空間下的法線方向向量填充給normal變量
        //紋理坐標(模型坐標對應貼圖的坐標)
        //紋理坐標一般都是0-1 不按照實際的像素來的
        float4 texcoord:TEXCOORD0; //告訴unity把模型空間下的紋理坐標填充給texcoord變量
    };

    //v2f vertex to fragment
    struct v2f
    {
        float4 position:SV_POSITION;
        float3 tempNormal:COLOR0;
    };
    //通過語義告訴係統我這個參數是幹嘛的比如POSITION是模型坐標語義,告訴係統我需要頂點參數坐標  SV_POSITION是剪裁坐標語義
    v2f vert(a2v v)
    {
        v2f f;
        f.position = mul(UNITY_MATRIX_MVP, v.vertex);//計算模型坐標轉換成剪裁坐標
        f.tempNormal = v.normal;//將法線數據放到返回結構體裏麵供片元函數調用
        return f;
    }

    //SV_target是返回顏色語義
    fixed4 frag(v2f f):SV_Target
    {
        return fixed4(f.tempNormal,1);
    }

    ENDCG
    }
}
FallBack  "VertexLit"

}


效果的計算放在片元函數裏麵效果更好但計算量也大,片元函數的調用比頂點函數調用也多的多。
頂點之間的點的數值是經過插值運算得到的。
效果圖:
![這裏寫圖片描述](https://img.blog.csdn.net/20171105143810753?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGluZ3hpYW93ZWkyMDEz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
解釋:三個方向上的法線向量依次是(1,0,0)(0,1,0)(0,0,1),對應的顏色就是紅綠藍,也就是如圖所示的效果圖。

## Unity中常用語義
從應用程序傳遞到頂點函數的語義有哪些a2v
POSITION頂點坐標(模型空間下的)
NORMAL法線(模型空間下)
TARGET切線(模型空間)
TEXCOORD0~n紋理坐標
COLOR頂點顏色

從頂點函數傳遞給片元函數的時候可以使用的語義
SV_POSITION剪裁空間中的頂點坐標(一般是係統直接使用)
COLOR0可以傳遞一組值 四個值
COLOR1可以傳遞一組值 四個值
TEXCOORD0~7傳遞紋理坐標

片元函數傳遞給係統
SV_TARGET顏色值,顯示到屏幕上的顏色

最後更新:2017-11-10 18:33:45

  上一篇:go  阿裏雲產品頭條
  下一篇:go  論網站內容建設策略