国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

[Unity Shader入門精要]初級(jí)篇 代碼拆解

這篇具有很好參考價(jià)值的文章主要介紹了[Unity Shader入門精要]初級(jí)篇 代碼拆解。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

簡(jiǎn)介:

介紹了Unity Shader入門精要中初級(jí)篇包含的所有代碼,通過(guò)詳細(xì)拆解代碼,一步一步揭曉Shader的原理。

第5章 開(kāi)始Unity Shader學(xué)習(xí)之旅

5.2.1 頂點(diǎn)/片元著色器的基本結(jié)構(gòu)

        Shader  "MyShaderName"  { //著色器名字
            Properties  {
              // 屬性
            }
            SubShader  {
              // 針對(duì)顯卡A的SubShader
              Pass  {
                  // 設(shè)置渲染狀態(tài)和標(biāo)簽

                  // 開(kāi)始CG代碼片段
                  CGPROGRAM
                  // 該代碼片段的編譯指令,例如:
                  #pragma  vertex  vert
                  #pragma  fragment  frag

                  // CG代碼寫(xiě)在這里

                  ENDCG //結(jié)束CG代碼

                  // 其他設(shè)置
              }
              // 其他需要的Pass
            }
            SubShader  {
              // 針對(duì)顯卡B的SubShader
            }

            // 上述SubShader都失敗后用于回調(diào)的Unity Shader
            Fallback  "VertexLit"
        }

說(shuō)人話:

Shader  "MyShaderName" {}

?第一行是著色器的名字,用大括號(hào){} 包裹后續(xù)所有的Shader代碼,關(guān)鍵字 Shader。

 Properties  {
              // 屬性
            }

?緊接著是Shader的屬性,用大括號(hào){} 包裹后續(xù)所有的屬性代碼,關(guān)鍵字 Properties。

 SubShader  {}

?代表子著色器,每個(gè)Shader都會(huì)包含至少一個(gè)SubShader,Unity顯示物體時(shí),回去檢測(cè)這些SubShader,選擇一個(gè)能夠在當(dāng)前顯卡允許的SubShader。

Pass{}

?Pass命令包含渲染狀態(tài)設(shè)置命令的列表,可以有多個(gè)Pass。

CGPROGRAM
//CG代碼
ENDCG

?CG代碼被包含在其中。

  #pragma  vertex  vert
  #pragma  fragment  frag

?該代碼片段的編譯指令,告訴計(jì)算機(jī)哪個(gè)是頂點(diǎn)著色器(vertex),哪個(gè)是片元著色器(fragment)。vert和frag通常分別是它們的名字,也可以更改。

 Fallback  "VertexLit"

? 上述SubShader都失敗后用于回調(diào)的Unity Shader

最簡(jiǎn)單的頂點(diǎn)/片元著色器

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

// 定義一個(gè)名為 "Simple Shader" 的著色器
Shader "Unity Shaders Book/Chapter 5/Simple Shader" {
	// 定義屬性
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)  // 顏色屬性
	}
	// 定義子著色器
	SubShader {
        Pass {
            CGPROGRAM

            #pragma vertex vert  // 頂點(diǎn)著色器入口點(diǎn)
            #pragma fragment frag  // 片段著色器入口點(diǎn)
            
            uniform fixed4 _Color;  // 顏色變量

			// 定義輸入結(jié)構(gòu)體
			struct a2v {
                float4 vertex : POSITION;  // 頂點(diǎn)位置
				float3 normal : NORMAL;  // 法線
				float4 texcoord : TEXCOORD0;  // 紋理坐標(biāo)
            };
            
            // 定義輸出結(jié)構(gòu)體
            struct v2f {
                float4 pos : SV_POSITION;  // 頂點(diǎn)位置
                fixed3 color : COLOR0;  // 顏色
            };
            
            // 頂點(diǎn)著色器函數(shù)
            v2f vert(a2v v) {
            	v2f o;
            	o.pos = UnityObjectToClipPos(v.vertex);  // 將頂點(diǎn)位置轉(zhuǎn)換為裁剪空間
            	o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);  // 計(jì)算顏色
                return o;
            }

            // 片元著色器函數(shù)
            fixed4 frag(v2f i) : SV_Target {
            	fixed3 c = i.color;
            	c *= _Color.rgb;  // 使用顏色屬性調(diào)整顏色
                return fixed4(c, 1.0);  // 返回最終顏色
            }

            ENDCG
        }
    }
}

說(shuō)人話:

Shader "Unity Shaders Book/Chapter 5/Simple Shader" {}

? 第一行表示Shader的名字,”/“用來(lái)分隔層次結(jié)構(gòu)。

Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)  // 顏色屬性
	}

?Properties語(yǔ)義塊中,聲明了一個(gè)屬性_Color,它的類型是Color,初始值是(1.0,1.0,1.0,1.0),對(duì)應(yīng)白色。為了在CG代碼中可以訪問(wèn)它,我們還需要在CG代碼片段中提前定義一個(gè)新的變量,這個(gè)變量的名稱和類型必須與Properties語(yǔ)義塊中的屬性定義相匹配。

?在片元著色器中使用了該屬性。

ShaderLab屬性類型 CG變量類型
Color,Vector float4,half4,fixed4
Range,Float float,half,fixed
2D sampler2D
Cube samplerCube
3D sampler3D
SubShader { Pass {} }

?定義子著色器和Pass語(yǔ)句塊。Pass命令包含渲染狀態(tài)設(shè)置命令的列表。

  CGPROGRAM
  ENDCG

?CG代碼被包含在其中。所以 CGPROGRAM通常在Pass語(yǔ)句塊的開(kāi)頭, ENDCG在Pass語(yǔ)句塊結(jié)尾。

        #pragma  vertex  vert
        #pragma  fragment  frag

?它們將告訴Unity,哪個(gè)函數(shù)包含了頂點(diǎn)著色器的代碼,哪個(gè)函數(shù)包含了片元著色器的代碼。更通用的編譯指令表示如下: #pragma vertex name
#pragma fragment name

?其中name就是我們指定的函數(shù)名,這兩個(gè)函數(shù)的名字不一定是vert和frag,它們可以是任意自定義的合法函數(shù)名,但我們一般使用vert和frag來(lái)定義這兩個(gè)函數(shù),因?yàn)樗鼈兒苤庇^。

uniform fixed4 _Color;  // 顏色變量

?屬性中有_Color在Pass中也要聲明Pass。uniform可省略,fixed4表示是一個(gè)四維矢量。

	// 定義輸入結(jié)構(gòu)體
			struct a2v {
                float4 vertex : POSITION;  // 頂點(diǎn)位置
				float3 normal : NORMAL;  // 法線
				float4 texcoord : TEXCOORD0;  // 紋理坐標(biāo)
            };

?vertex包含這個(gè)頂點(diǎn)的位置,這是通過(guò)POSITION語(yǔ)義指定的,返回值是一個(gè)float4類型變量,它是該頂點(diǎn)在裁剪空間中的位置。這些語(yǔ)義將告訴系統(tǒng)用戶需要哪些輸入值,以及用戶的輸出是什么。

?POSITON語(yǔ)義告訴Unity,用模型空間頂點(diǎn)坐標(biāo)填充vertex變量。

?NORMAL告訴Unity,用模型空間頂點(diǎn)坐標(biāo)填充normal變量。

?TECCOORD0告訴Unity,用模型的第一套紋理坐標(biāo)填充texcoord。

?a表示應(yīng)用(application), v表示頂點(diǎn)著色器(vertex shader), a2v的意思就是把數(shù)據(jù)從應(yīng)用階段傳遞到頂點(diǎn)著色器中。

    // 定義輸出結(jié)構(gòu)體
            struct v2f {
                float4 pos : SV_POSITION;  // 頂點(diǎn)位置
                fixed3 color : COLOR0;  // 顏色
            };

?SV_POSITION將告訴Unity,pos里包含了頂點(diǎn)在裁剪空間的位置信息。

?COLORO語(yǔ)義可以用來(lái)存儲(chǔ)顏色信息。

  // 頂點(diǎn)著色器函數(shù)
            v2f vert(a2v v) {
            	v2f o;
            	o.pos = UnityObjectToClipPos(v.vertex);  // 將頂點(diǎn)位置轉(zhuǎn)換為裁剪空間
            	o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);  // 計(jì)算顏色
                return o;
            }

?聲明輸出結(jié)構(gòu)體 v2f o。 參數(shù)a2v v是輸入結(jié)構(gòu)體。

?vert函數(shù)的返回值是v2f類型的。傳入?yún)?shù)a2v v用于進(jìn)行頂點(diǎn)和片元的信息傳遞。

?o.pos = UnityObjectToClipPos(v.vertex) 將頂點(diǎn)位置轉(zhuǎn)換為裁剪空間。

?v.normal包含頂點(diǎn)的法線方向,其分量范圍在[-1.0,1.0],將法線歸一化后,法線有可能是正向和反向,所以分量范圍是[-1.0,1.0]。

?o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5); 把分量范圍映射到[0.0,1.0] ,也就是將范圍[-1.0,1.0]乘以0.5在加上0.5,映射到[0.0,1.0] 。

?return o 頂點(diǎn)著色器最重要的是返回裁剪空間中的頂點(diǎn)信息。受到結(jié)構(gòu)體v2f中的語(yǔ)義控制

float4 pos : SV_POSITION; // 頂點(diǎn)位置 fixed3 color : COLOR0; // 顏色 SV表示系統(tǒng)數(shù)值語(yǔ)義用SV_POSITION語(yǔ)義去修飾頂點(diǎn)著色器的輸出變量pos,那么就表示pos包含了可用于光柵化的變換后的頂點(diǎn)坐標(biāo)(即齊次裁剪空間中的坐標(biāo))。通常SV表示輸出。

語(yǔ)義 描述
POSITION 模型空間中的頂點(diǎn)位置,通常是float4類型
NORMAL 頂點(diǎn)法線,通常是float3類型
TANGENT 頂點(diǎn)切線,通常是float4類型
TEXCOORDn,TEXCOORD0,TEXCOORD1 該頂點(diǎn)的紋理坐標(biāo),TEXCOORD0表示第一組紋理坐標(biāo)。
COLOR 頂點(diǎn)顏色,通常是fixed4或float4類型
      // 片元著色器函數(shù)
            fixed4 frag(v2f i) : SV_Target {
            	fixed3 c = i.color;
            	c *= _Color.rgb;  // 使用顏色屬性調(diào)整顏色
                return fixed4(c, 1.0);  // 返回最終顏色
            }

?參數(shù) v2f i,輸入結(jié)構(gòu)體 i。同時(shí)也是頂點(diǎn)著色器的輸出結(jié)構(gòu)體。

?SV_Target也是HLSL中的一個(gè)系統(tǒng)語(yǔ)義,它等同于告訴渲染器,把用戶的輸出顏色存儲(chǔ)到一個(gè)渲染目標(biāo)(render target)中,這里將輸出到默認(rèn)的幀緩存中。

?fixed3 c = i.color 用fixed類型的變量c獲得輸出結(jié)構(gòu)體的顏色。

?c *= _Color.rgb 使用顏色屬性調(diào)整顏色。rgb表示紅綠藍(lán),是fixed 3類型的。

?return fixed4(c, 1.0) ,返回值是一個(gè)fixded4類型變量。受到SV_Target語(yǔ)義的控制,表示渲染的顏色。

第6章 Unity中的基礎(chǔ)光照

6.4.1 逐頂點(diǎn)漫反射光照

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};
			
			v2f vert(a2v v) {
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Transform the normal from object space to world space
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
				// Get the light direction in world space
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
				
				o.color = ambient + diffuse;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				return fixed4(i.color, 1.0);
			}
			
			ENDCG
		}
	}
	FallBack "Diffuse"
}

說(shuō)人話:

        Shader  "Unity  Shaders  Book/Chapter  6/Diffuse  Vertex-Level"  {}

?為這個(gè)Shader起一個(gè)名字。

        Properties  {
            _Diffuse  ("Diffuse",  Color)  =  (1,  1,  1,  1)
        }

?為了得到并且控制材質(zhì)的漫反射顏色,我們首先在Shader的Properties語(yǔ)義塊中聲明了一個(gè)Color類型的屬性,并把它的初始值設(shè)為白色。

        SubShader  {
            Pass  {
              Tags  {  "LightMode"="ForwardBase"  }
              }
              }

?在SubShader語(yǔ)義塊中定義了一個(gè)Pass語(yǔ)義塊。這是因?yàn)轫旤c(diǎn)/片元著色器的代碼需要寫(xiě)在Pass語(yǔ)義塊,而非SubShader語(yǔ)義塊中。而且,我們?cè)赑ass的第一行指明了該P(yáng)ass的光照模式。

        CGPROGRAM

        #pragma  vertex  vert
        #pragma  fragment  frag

?使用CGPROGRAM和ENDCG來(lái)包圍CG代碼片,以定義最重要的頂點(diǎn)著色器和片元著色器代碼。首先,我們使用#pragma指令來(lái)告訴Unity,我們定義的頂點(diǎn)著色器和片元著色器叫什么名字。在本例中,它們的名字分別是vert和frag。

        #include  "Lighting.cginc"

?為了使用Unity內(nèi)置的一些變量,如后面要講到的_LightColor0,還需要包含進(jìn)Unity的內(nèi)置文件Lighting.cginc。

        fixed4  _Diffuse;

?為了在Shader中使用Properties語(yǔ)義塊中聲明的屬性,我們需要定義一個(gè)和該屬性類型相匹配的變量:

?由于顏色屬性的范圍在0到1之間,因此我們可以使用fixed精度的變量來(lái)存儲(chǔ)它。

        struct  a2v  {
            float4  vertex  :  POSITION;
            float3  normal  :  NORMAL;
        };

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            fixed3  color  :  COLOR;
        };

?然后,我們定義了頂點(diǎn)著色器的輸入和輸出結(jié)構(gòu)體(輸出結(jié)構(gòu)體同時(shí)也是片元著色器的輸入結(jié)構(gòu)體)。

        v2f  vert(a2v  v)  {
            v2f  o;
            //  Transform  the  vertex  from  object  space  to  projection  space
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);

            //  Get  ambient  term
            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz;

            //  Transform  the  normal  fram  object  space  to  world  space
            fixed3  worldNormal  =  normalize(mul(v.normal,  (float3x3)_World2Object));
            //  Get  the  light  direction  in  world  space
            fixed3  worldLight  =  normalize(_WorldSpaceLightPos0.xyz);
            //  Compute  diffuse  term
            fixed3   diffuse   =   _LightColor0.rgb   *   _Diffuse.rgb   *   saturate(dot(worldNormal,
        worldLight));

            o.color  =  ambient  +  diffuse;

            return  o;
        }

?在第一行,我們首先定義了返回值o。我們已經(jīng)重復(fù)過(guò)很多次,頂點(diǎn)著色器最基本的任務(wù)就是把頂點(diǎn)位置從模型空間轉(zhuǎn)換到裁剪空間中。

? o.pos = mul(UNITY_MATRIX_MVP, v.vertex) 因此我們需要使用Unity內(nèi)置的模型世界投影矩陣UNITY_MATRIX_MVP來(lái)完成這樣的坐標(biāo)變換?;蛘呤褂胦.pos = UnityObjectToClipPos(v.vertex) 將頂點(diǎn)位置轉(zhuǎn)換為裁剪空間

? fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz 接下來(lái),我們通過(guò)Unity的內(nèi)置變量UNITY_LIGHTMODEL_AMBIENT得到了環(huán)境光部分。

?fixed3 worldNormal = normalize(mul(v.normal, (float3x3)World2Object)) 因?yàn)閍2v得到的頂點(diǎn)法線是位于模型空間下的,World2Object是模型到空間的逆矩陣。通過(guò)模型空間法線和模型到空間的逆矩陣相乘將法線轉(zhuǎn)換到世界空間中。法線是一個(gè)三維矢量,因此我們只需要截取_World2Object的前三行前三列即可。

?在得到了世界空間中的法線和光源方向后,我們需要對(duì)它們進(jìn)行歸一化操作。

?fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz) 獲得指向光源的單位矢量。

[Unity Shader入門精要]初級(jí)篇 代碼拆解,unity,游戲引擎,圖形渲染

? 漫反射計(jì)算公式 clight:入射光線顏色和強(qiáng)度 mdiffuse:材質(zhì)的漫反射系數(shù) n:表面法線 I:光源方向

?fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,
worldLight)) 在得到它們點(diǎn)積的結(jié)果后,我們需要防止這個(gè)結(jié)果為負(fù)值。為此,我們使用了saturate函數(shù)。saturate函數(shù)是CG提供的一種函數(shù),它的作用是可以把參數(shù)截取到[0, 1]的范圍內(nèi)。最后,再與光源的顏色和強(qiáng)度以及材質(zhì)的漫反射顏色相乘即可得到最終的漫反射光照部分。

6.4.2 逐像素漫反射光照

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel-Level" {
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
			};
			
			v2f vert(a2v v) {
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);

				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Get the normal in world space
				fixed3 worldNormal = normalize(i.worldNormal);
				// Get the light direction in world space
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				
				fixed3 color = ambient + diffuse;
				
				return fixed4(color, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

說(shuō)人話:

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            float3  worldNormal  :  TEXCOORD0;
        };

?修改頂點(diǎn)著色器的輸出結(jié)構(gòu)體v2f。從fixed3 color : COLOR修改為float3 worldNormal : TEXCOORD0。

        v2f  vert(a2v  v)  {
            v2f  o;
            //  Transform  the  vertex  from  object  space  to  projection  space
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);

            //  Transform  the  normal  fram  object  space  to  world  space
            o.worldNormal  =  mul(v.normal,  (float3x3)_World2Object);

            return  o;
        }

?頂點(diǎn)著色器不需要計(jì)算光照模型,只需要把世界空間下的法線傳遞給片元著色器即可。在片元著色器中計(jì)算光照模型。只有頂點(diǎn)著色器可以得到頂點(diǎn)的法線以及頂點(diǎn)相關(guān)的位置信息,片元著色器得不到頂點(diǎn),所以需要頂點(diǎn)著色器中進(jìn)行計(jì)算然后傳遞。

?v2f o 聲明輸出結(jié)構(gòu)體。

?o.pos = mul(UNITY_MATRIX_MVP, v.vertex) 計(jì)算世界空間下頂點(diǎn)位置。

? o.worldNormal = mul(v.normal, (float3x3)_World2Object) 計(jì)算頂點(diǎn)世界法線位置。

        fixed4  frag(v2f  i)  :  SV_Target  {
            //  Get  ambient  term
            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz;

            //  Get  the  normal  in  world  space
            fixed3  worldNormal  =  normalize(i.worldNormal);
            //  Get  the  light  direction  in  world  space
            fixed3  worldLightDir  =  normalize(_WorldSpaceLightPos0.xyz);

            //  Compute  diffuse  term
            fixed3   diffuse   =   _LightColor0.rgb   *   _Diffuse.rgb   *   saturate(dot(worldNormal,
        worldLightDir));

            fixed3  color  =  ambient  +  diffuse;

            return  fixed4(color,  1.0);
        }

?片元著色器需要計(jì)算漫反射光照模型。

?fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz 通過(guò)內(nèi)置函數(shù)獲得自發(fā)光。

?fixed3 worldNormal = normalize(i.worldNormal) 歸一化世界法線。

?fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz) 歸一化世界光源方向。

? fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,
worldLightDir)) 根據(jù)光照模型計(jì)算漫反射。

?fixed3 color = ambient + diffuse 得到自發(fā)光和漫反射的顏色。

?逐頂點(diǎn)和逐像素的區(qū)別:

逐頂點(diǎn)光照是在 頂點(diǎn)shader的時(shí)候計(jì)算頂點(diǎn)光照顏色,然后到片元著色的時(shí)候通過(guò)插值,得到片元的光照計(jì)算的結(jié)果。逐像素光照是在片元著色的時(shí)候做光照計(jì)算,逐像素光照性能開(kāi)銷大,但是效果好,逐頂點(diǎn)光照,性能開(kāi)銷小,效果比逐像素差。

6.5.1 逐頂點(diǎn)高光反射光照

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 6/Specular Vertex-Level" {
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
			
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};
			
			v2f vert(a2v v) {
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Transform the normal from object space to world space
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
				// Get the light direction in world space
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				
				// Get the reflect direction in world space
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
				// Get the view direction in world space
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
				
				// Compute specular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
				
				o.color = ambient + diffuse + specular;
							 	
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				return fixed4(i.color, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

?高光反射計(jì)算公式

[Unity Shader入門精要]初級(jí)篇 代碼拆解,unity,游戲引擎,圖形渲染

clight:入射光線的顏色和強(qiáng)度 mspecluar:材質(zhì)的高光反射系數(shù) v:視角方向 r:反射方向

?反射方向r由表面法線n和光源方向I計(jì)算得到。可推導(dǎo)而成

[Unity Shader入門精要]初級(jí)篇 代碼拆解,unity,游戲引擎,圖形渲染

?CG提供了計(jì)算反射方向的函數(shù)reflect 函數(shù):reflect(i, n)

參數(shù):i,入射方向;n,法線方向??梢允莊loat、float2、float3等類型。

描述:當(dāng)給定入射方向i和法線方向n時(shí),reflect函數(shù)可以返回反射方向。圖6.9給出了參數(shù)和返回值之間的關(guān)系。

說(shuō)人話:

        Shader  "Unity  Shaders  Book/Chapter  6/Specular  Vertex-Level"  { }

?首先,我們需要為這個(gè)Shader起一個(gè)名字。

        Properties  {
            _Diffuse  ("Diffuse",  Color)  =  (1,  1,  1,  1)
            _Specular  ("Specular",  Color)  =  (1,  1,  1,  1)
            _Gloss  ("Gloss",  Range(8.0,  256))  =  20
        }

?為了在材質(zhì)面板中能夠方便地控制高光反射屬性,我們?cè)赟hader的Properties語(yǔ)義塊中聲明了三個(gè)屬性。其中,新添加的Specular用于控制材質(zhì)的高光反射顏色,而Gloss用于控制高光區(qū)域的大小。

        SubShader  {
            Pass  {
              Tags  {  "LightMode"="ForwardBase"  }
              }
              }

?我們?cè)赟ubShader語(yǔ)義塊中定義了一個(gè)Pass語(yǔ)義塊。這是因?yàn)轫旤c(diǎn)/片元著色器的代碼需要寫(xiě)在Pass語(yǔ)義塊,而非SubShader語(yǔ)義塊中。而且,我們?cè)赑ass的第一行指明了該P(yáng)ass的光照模式。

        CGPROGRAM

        #pragma  vertex  vert
        #pragma  fragment  frag

?使用CGPROGRAM和ENDCG來(lái)包圍CG代碼片,以定義最重要的頂點(diǎn)著色器和片元著色器代碼。首先,我們使用#pragma指令來(lái)告訴Unity,我們定義的頂點(diǎn)著色器和片元著色器叫什么名字。在本例中,它們的名字分別是vert和frag。

        #include  "Lighting.cginc"

?為了使用Unity內(nèi)置的一些變量,如_LightColor0,還需要包含進(jìn)Unity的內(nèi)置文件Lighting.cginc。

        fixed4  _Diffuse;
        fixed4  _Specular;
        float  _Gloss;

?為了在Shader中使用Properties語(yǔ)義塊中聲明的屬性,我們需要定義和這些屬性類型相匹配的變量

?由于顏色屬性的范圍在0到1之間,因此對(duì)于_Diffuse和_Specular屬性我們可以使用fixed精度的變量來(lái)存儲(chǔ)它。而_Gloss的范圍很大,因此我們使用float精度來(lái)存儲(chǔ)。

        struct  a2v  {
            float4  vertex  :  POSITION;
            float3  normal  :  NORMAL;
        };

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            fixed3  color  :  COLOR;
        };

?然后,我們定義了頂點(diǎn)著色器的輸入和輸出結(jié)構(gòu)體(輸出結(jié)構(gòu)體同時(shí)也是片元著色器的輸入結(jié)構(gòu)體)。

        v2f  vert(a2v  v)  {
            v2f  o;
            //  Transform  the  vertex  from  object  space  to  projection  space
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);

            //  Get  ambient  term
            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz;

            //  Transform  the  normal  fram  object  space  to  world  space
            fixed3  worldNormal  =  normalize(mul(v.normal,  (float3x3)_World2Object));
            //  Get  the  light  direction  in  world  space
            fixed3  worldLightDir  =  normalize(_WorldSpaceLightPos0.xyz);

            //  Compute  diffuse  term
            fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

            //  Get  the  reflect  direction  in  world  space
            fixed3  reflectDir  =  normalize(reflect(-worldLightDir,  worldNormal));
            //  Get  the  view  direction  in  world  space
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(_Object2World, v.vertex).xyz);

            //  Compute  specular  term
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

            o.color  =  ambient  +  diffuse  +  specular;

            return  o;
        }

?在頂點(diǎn)著色器中,我們計(jì)算了包含高光反射的光照模型。

?漫反射部分的計(jì)算和6.4節(jié)中的代碼完全一致。

? fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal)) 我們首先計(jì)算了入射光線方向關(guān)于表面法線的反射方向reflectDir。由于CG的reflect函數(shù)的入射方向要求是由光源指向交點(diǎn)處的,因此我們需要對(duì)worldLightDir取反后再傳給reflect函數(shù)。worldLightDir是交點(diǎn)處指向光源。

?fixed3 viewDir = normalize(WorldSpaceCameraPos.xyz - mul(Object2World, v.vertex).xyz) 然后,我們通過(guò)_WorldSpaceCameraPos得到了世界空間中的攝像機(jī)位置,再通過(guò)和頂點(diǎn)世界空間位置相減即可得到世界空間下的視角方向,這是單位視角方向。

? fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss) 得到了所有的4個(gè)參數(shù),代入公式即可得到高光反射的光照部分。最后,再和環(huán)境光、漫反射光相加存儲(chǔ)到最后的顏色中。

        fixed4  frag(v2f  i)  :  SV_Target  {
            return  fixed4(i.color,  1.0);
        }

?片元著色器的代碼非常簡(jiǎn)單,我們只需要直接返回頂點(diǎn)顏色即可。

        Fallback  "Specular"

?)最后,我們需要把這個(gè)Unity Shader的回調(diào)Shader設(shè)置為內(nèi)置的Specular。

6.5.2 逐像素高光反射光照

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 6/Specular Pixel-Level" {
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};
			
			v2f vert(a2v v) {
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				// Transform the vertex from object spacet to world space
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				
				// Get the reflect direction in world space
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
				// Get the view direction in world space
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
				// Compute specular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

說(shuō)人話:

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            float3  worldNormal  :  TEXCOORD0;
            float3  worldPos  :  TEXCOORD1;
        };

?修改頂點(diǎn)著色器的輸出結(jié)構(gòu)體v2f。修改為世界法線和世界坐標(biāo)。語(yǔ)義為第一組紋理坐標(biāo)和第二組紋理坐標(biāo)。

        v2f  vert(a2v  v)  {
            v2f  o;
            //  Transform  the  vertex  from  object  space  to  projection  space
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);

            //  Transform  the  normal  fram  object  space  to  world  space
            o.worldNormal  =  mul(v.normal,  (float3x3)_World2Object);
            //  Transform  the  vertex  from  object  space  to  world  space
            o.worldPos  =  mul(_Object2World,  v.vertex).xyz;

            return  o;
        }

?頂點(diǎn)著色器只需要計(jì)算世界空間下的法線方向和頂點(diǎn)坐標(biāo),并把它們傳遞給片元著色器即可。

        fixed4  frag(v2f  i)  :  SV_Target  {
            //  Get  ambient  term
            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz;

            fixed3  worldNormal  =  normalize(i.worldNormal);
            fixed3  worldLightDir  =  normalize(_WorldSpaceLightPos0.xyz);

            //  Compute  diffuse  term
            fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

            //  Get  the  reflect  direction  in  world  space
            fixed3  reflectDir  =  normalize(reflect(-worldLightDir,  worldNormal));
            //  Get  the  view  direction  in  world  space
            fixed3  viewDir  =  normalize(_WorldSpaceCameraPos.xyz  -  i.worldPos.xyz);
            //  Compute  specular  term
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

            return  fixed4(ambient  +  diffuse  +  specular,  1.0);
        }

?片元著色器需要計(jì)算關(guān)鍵的光照模型。

6.5.3 Blinn-Phong 光照模型

?Blinn模型沒(méi)有使用反射方向,而是引入一個(gè)新的矢量h,它是通過(guò)對(duì)視角方向v和光照方向I相加后再歸一化得到的。即

[Unity Shader入門精要]初級(jí)篇 代碼拆解,unity,游戲引擎,圖形渲染

而B(niǎo)linn模型計(jì)算高光反射的公式如下:

[Unity Shader入門精要]初級(jí)篇 代碼拆解,unity,游戲引擎,圖形渲染

將逐像素高光反射SHader中的片元著色器中對(duì)高光反射部分的計(jì)算代碼進(jìn)行修改。

        fixed4  frag(v2f  i)  :  SV_Target  {
            ...

            //  Get  the  view  direction  in  world  space
            fixed3  viewDir  =  normalize(_WorldSpaceCameraPos.xyz  -  i.worldPos.xyz);
            //  Get  the  half  direction  in  world  space
            fixed3  halfDir  =  normalize(worldLightDir  +  viewDir);
            //  Compute  specular  term
            fixed3 specular=_LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

            return  fixed4(ambient  +  diffuse  +  specular,  1.0);
        }

?fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz) 得到攝像機(jī)視角。

?fixed3 halfDir = normalize(worldLightDir + viewDir) 得到新的矢量h。通過(guò)將世界光照方向和攝像機(jī)視角方向歸一化得到的。

? fixed3 specular=_LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss) 按照Blinn-Phong 光照模型公式進(jìn)行計(jì)算得到高光反射。

6.6.0 Unity 內(nèi)置的函數(shù)

UnityCG.cginc中一些常用的幫助函數(shù):

函數(shù)名 描述
float3 WorldSpaceViewDir (float4 v) 輸入一個(gè)模型空間中的頂點(diǎn)位置,返回世界空間中從該點(diǎn)到攝像機(jī)的觀察方向,內(nèi)部實(shí)現(xiàn)使用UnityWorldSpaceViewDir函數(shù)
float3 UnityWorldSpaceViewDir (float4 v) 輸入一個(gè)世界空間中的頂點(diǎn)位置,返回世界空間中從該點(diǎn)到攝像機(jī)的觀察方向
float3 ObjSpaceViewDir (float4 v) 輸入一個(gè)模型空間中的頂點(diǎn)位置,返回模型空間中從該點(diǎn)到攝像機(jī)的觀察方向
float3 WorldSpaceLightDir (float4 v) 僅可用于前向渲染中,輸入一個(gè)模型空間中的頂點(diǎn)位置,返回世界空間中從該點(diǎn)到光源的光照方向。內(nèi)部實(shí)現(xiàn)使用了UnityWorldSpaceLightDir函數(shù),沒(méi)有被歸一化
float3 UnityWorldSpaceLightDir (float4 v) 僅可用于前向渲染中,輸入一個(gè)世界空間中的頂點(diǎn)位置,返回世界空間中從該點(diǎn)到光源的光照方向。沒(méi)有被歸一化
float3 ObjSpaceLightDir (float4 v) 僅可用于前向渲染中,輸入一個(gè)模型空間中的頂點(diǎn)位置,返回模型空間中從該點(diǎn)到光源的光照方向。沒(méi)有被歸一化
float3 UnityObjectToWorldNormal (float3 norm) 把法線方向從模型空間轉(zhuǎn)換到世界空間中
float3 UnityObjectToWorldDir (in float3 dir) 把方向矢量從模型空間轉(zhuǎn)換到世界空間中
float3 UnityWorldToWorldDir (float3 dir) 把方向矢量從世界空間轉(zhuǎn)換到模型空間中

?需要注意的是,這些函數(shù)都沒(méi)有保證得到的方向矢量是單位矢量,因此,我們需要在使用前把它們歸一化。

6.6.1 內(nèi)置函數(shù)來(lái)改寫(xiě)B(tài)linn-Phong光照模型的Unity Shader

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 6/Blinn-Phong Use Built-in Functions" {
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(1.0, 500)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float4 worldPos : TEXCOORD1;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Use the build-in funtion to compute the normal in world space
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 worldNormal = normalize(i.worldNormal);
				//  Use the build-in funtion to compute the light direction in world space
				// Remember to normalize the result
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
				
				// Use the build-in funtion to compute the view direction in world space
				// Remember to normalize the result
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

說(shuō)人話:

v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Use the build-in funtion to compute the normal in world space
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				
				return o;
			}

?o.pos = UnityObjectToClipPos(v.vertex) 把頂點(diǎn)位置從模型空間轉(zhuǎn)換到裁剪空間。

o.worldNormal = UnityObjectToWorldNormal(v.normal) 把法線方向從模型空間轉(zhuǎn)換到世界空間中。

        fixed4  frag(v2f  i)  :  SV_Target  {
            ...

            fixed3  worldNormal  =  normalize(i.worldNormal);
            //   Use  the  build-in  function  to  compute  the  light  direction  in  world  space
            //  Remember  to  normalize  the  result
            fixed3  worldLightDir  =  normalize(UnityWorldSpaceLightDir(i.worldPos));

            ...

            //  Use  the  build-in  function  to  compute  the  view  direction  in  world  space
            //  Remember  to  normalize  the  result
            fixed3  viewDir  =  normalize(UnityWorldSpaceViewDir(i.worldPos));
            ...
        }

?在片元著色器中,我們使用內(nèi)置的UnityWorldSpaceLightDir函數(shù)和UnityWorldSpaceView Dir函數(shù)來(lái)分別計(jì)算世界空間的光照方向和視角方向。

? fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)) 輸入一個(gè)世界空間的頂點(diǎn)位置,返回世界空間中從該點(diǎn)到光源的光照方向,沒(méi)有被歸一化。

?fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)) 輸入一個(gè)世界空間中的頂點(diǎn)位置,返回世界空間中從該點(diǎn)到攝像機(jī)的觀察方向。沒(méi)有被歸一化。

第7章 基礎(chǔ)紋理

7.1.1 單張紋理Shader

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 7/Single Texture" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {		
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				// Or just call the built-in function
//				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				// Use the texture to sample the diffuse color
				fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
				
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

說(shuō)人話:

        Shader  "Unity  Shaders  Book/Chapter  7/Single  Texture"  {

?首先,我們需要為這個(gè)Unity Shader起一個(gè)名字。

        Properties  {
            _Color  ("Color  Tint",  Color)  =  (1,1,1,1)
            _MainTex  ("Main  Tex",  2D)  =  "white"  {}
            _Specular  ("Specular",  Color)  =  (1,  1,  1,  1)
            _Gloss  ("Gloss",  Range(8.0,  256))  =  20
        }

?為了使用紋理,我們需要在Properties語(yǔ)義塊中添加一個(gè)紋理屬性。

?MainTex的紋理,在3.3.2節(jié)中,我們已經(jīng)知道2D是紋理屬性的聲明方式。我們使用一個(gè)字符串后跟一個(gè)花括號(hào)作為它的初始值,“white”是內(nèi)置紋理的名字,也就是一個(gè)全白的紋理。為了控制物體的整體色調(diào),我們還聲明了一個(gè)Color屬性。

        SubShader  {
            Pass  {
              Tags  {  "LightMode"="ForwardBase"  }
              }
              }

?然后,我們?cè)赟ubShader語(yǔ)義塊中定義了一個(gè)Pass語(yǔ)義塊。而且,我們?cè)赑ass的第一行指明了該P(yáng)ass的光照模式。

?LightMode標(biāo)簽是Pass標(biāo)簽中的一種,它用于定義該P(yáng)ass在Unity的光照流水線中的角色。

        CGPROGRAM

        #pragma  vertex  vert
        #pragma  fragment  frag

?接著,我們使用CGPROGRAM和ENDCG來(lái)包圍住CG代碼片,以定義最重要的頂點(diǎn)著色器和片元著色器代碼。首先,我們使用#pragma指令來(lái)告訴Unity,我們定義的頂點(diǎn)著色器和片元著色器叫什么名字。在本例中,它們的名字分別是vert和frag。

        fixed4  _Color;
        sampler2D  _MainTex;
        float4  _MainTex_ST;
        fixed4  _Specular;
        float  _Gloss;

?我們需要在CG代碼片中聲明和上述屬性類型相匹配的變量,以便和材質(zhì)面板中的屬性建立聯(lián)系。

?與其他屬性類型不同的是,我們還需要為紋理類型的屬性聲明一個(gè)float4類型的變量MainTex_ST。其中,MainTex_ST的名字不是任意起的。在Unity中,我們需要使用紋理名ST的方式來(lái)聲明某個(gè)紋理的屬性。其中,ST是縮放(scale)和平移(translation)的縮寫(xiě)。MainTex_ST可以讓我們得到該紋理的縮放和平移(偏移)值,MainTex_ST.xy存儲(chǔ)的是縮放值,而MainTex_ST.zw存儲(chǔ)的是偏移值。這些值可以在材質(zhì)面板的紋理屬性中調(diào)節(jié)。

        struct  a2v  {
            float4  vertex  :  POSITION;
            float3  normal  :  NORMAL;
            float4  texcoord  :  TEXCOORD0;
        };

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            float3  worldNormal  :  TEXCOORD0;
            float3  worldPos  :  TEXCOORD1;
            float2  uv  :  TEXCOORD2;
        };

?接下來(lái),我們需要定義頂點(diǎn)著色器的輸入和輸出結(jié)構(gòu)體。

?在上面的代碼中,我們首先在a2v結(jié)構(gòu)體中使用TEXCOORD0語(yǔ)義聲明了一個(gè)新的變量texcoord,這樣Unity就會(huì)將模型的第一組紋理坐標(biāo)存儲(chǔ)到該變量中。然后,我們?cè)趘2f結(jié)構(gòu)體中添加了用于存儲(chǔ)紋理坐標(biāo)的變量uv,以便在片元著色器中使用該坐標(biāo)進(jìn)行紋理采樣。

        v2f  vert(a2v  v)  {
            v2f  o;
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);
            o.worldNormal  =  UnityObjectToWorldNormal(v.normal);

            o.worldPos  =  mul(_Object2World,  v.vertex).xyz;

            o.uv  =  v.texcoord.xy  *  _MainTex_ST.xy  +  _MainTex_ST.zw;
            //  Or  just  call  the  built-in  function
            //        o.uv  =  TRANSFORM_TEX(v.texcoord,  _MainTex);

            return  o;
        }

?然后,我們定義了頂點(diǎn)著色器。

? o.uv = v.texcoord.xy * MainTex_ST.xy + MainTex_ST.zw 在頂點(diǎn)著色器中,我們使用紋理的屬性值MainTex_ST來(lái)對(duì)頂點(diǎn)紋理坐標(biāo)(v.texcoord.xy )進(jìn)行變換,得到最終的紋理坐標(biāo)。計(jì)算過(guò)程是,首先使用縮放屬性MainTex_ST.xy對(duì)頂點(diǎn)紋理坐標(biāo)(v.texcoord.xy ))進(jìn)行縮放,然后再使用偏移屬性MainTex_ST.zw對(duì)結(jié)果進(jìn)行偏移。Unity提供了一個(gè)內(nèi)置宏TRANSFORM_TEX來(lái)幫我們計(jì)算上述過(guò)程。TRANSFORM_TEX是在UnityCG.cginc中定義的。也就是o.uv = TRANSFORM_TEX(v.texcoord, _MainTex)。

        //  Transforms  2D  UV  by  scale/bias  property
        #define  TRANSFORM_TEX(tex, name)  (tex.xy  *  name##_ST.xy  +  name##_ST.zw)

它接受兩個(gè)參數(shù),第一個(gè)參數(shù)是頂點(diǎn)紋理坐標(biāo),第二個(gè)參數(shù)是紋理名,在它的實(shí)現(xiàn)中,將利用紋理名_ST的方式來(lái)計(jì)算變換后的紋理坐標(biāo)。

        fixed4  frag(v2f  i)  :  SV_Target  {
            fixed3  worldNormal  =  normalize(i.worldNormal);
            fixed3  worldLightDir  =  normalize(UnityWorldSpaceLightDir(i.worldPos));

            //  Use  the  texture  to  sample  the  diffuse  color
            fixed3  albedo  =  tex2D(_MainTex,  i.uv).rgb  *  _Color.rgb;

            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz  *  albedo;

            fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

            fixed3  viewDir  =  normalize(UnityWorldSpaceViewDir(i.worldPos));
            fixed3  halfDir  =  normalize(worldLightDir  +  viewDir);
            fixed3  specular  =  _LightColor0.rgb  *  _Specular.rgb  *  pow(max(0,  dot(worldNormal,
        halfDir)),  _Gloss);

            return  fixed4(ambient  +  diffuse  +  specular,  1.0);
        }

?我們還需要實(shí)現(xiàn)片元著色器,并在計(jì)算漫反射時(shí)使用紋理中的紋素值。

?上面的代碼首先計(jì)算了世界空間下的法線方向和光照方向。然后,使用CG的tex2D函數(shù)對(duì)紋理進(jìn)行采樣。它的第一個(gè)參數(shù)是需要被采樣的紋理,第二個(gè)參數(shù)是一個(gè)float2類型的紋理坐標(biāo),它將返回計(jì)算得到的紋素值。我們使用采樣結(jié)果和顏色屬性_Color的乘積來(lái)作為材質(zhì)的反射率albedo,并把它和環(huán)境光照相乘得到環(huán)境光部分。隨后,我們使用albedo來(lái)計(jì)算漫反射光照的結(jié)果,并和環(huán)境光照、高光反射光照相加后返回。

? fixed3 worldNormal = normalize(i.worldNormal); 計(jì)算世界空間下法線方向

?fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));計(jì)算世界空間下光照方向

? fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; 使用CG的tex2D函數(shù)對(duì)紋理進(jìn)行采樣。使用采樣結(jié)果和顏色屬性Color的乘積來(lái)作為材質(zhì)的反射率albedo。

?fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; 并把它和環(huán)境光照相乘得到環(huán)境光部分

? fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));隨后,我們使用albedo來(lái)計(jì)算漫反射光照的結(jié)果

?return fixed4(ambient + diffuse + specular, 1.0); 并和環(huán)境光照、高光反射光照相加后返回。

        Fallback  "Specular"

?最后,我們?yōu)樵揝hader設(shè)置了合適的Fallback。

7.2.1 高度紋理

7.2.2 法線紋理

由于法線方向的分量范圍在[-1, 1],而像素的分量范圍為[0, 1],因此我們需要做一個(gè)映射,通常使用的映射就是

[Unity Shader入門精要]初級(jí)篇 代碼拆解,unity,游戲引擎,圖形渲染

這就要求,我們?cè)赟hader中對(duì)法線紋理進(jìn)行紋理采樣后,還需要對(duì)結(jié)果進(jìn)行一次反映射的過(guò)程,以得到原先的法線方向。反映射的過(guò)程實(shí)際就是使用上面映射函數(shù)的逆函數(shù):

normal=pixel ×2-1

1,在切線空間下計(jì)算

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_BumpMap ("Normal Map", 2D) = "bump" {}
		_BumpScale ("Bump Scale", Float) = 1.0
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _BumpMap;
			float4 _BumpMap_ST;
			float _BumpScale;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float4 uv : TEXCOORD0;
				float3 lightDir: TEXCOORD1;
				float3 viewDir : TEXCOORD2;
			};

			// Unity doesn't support the 'inverse' function in native shader
			// so we write one by our own
			// Note: this function is just a demonstration, not too confident on the math or the speed
			// Reference: http://answers.unity3d.com/questions/218333/shader-inversefloat4x4-function.html
			float4x4 inverse(float4x4 input) {
				#define minor(a,b,c) determinant(float3x3(input.a, input.b, input.c))
				
				float4x4 cofactors = float4x4(
				     minor(_22_23_24, _32_33_34, _42_43_44), 
				    -minor(_21_23_24, _31_33_34, _41_43_44),
				     minor(_21_22_24, _31_32_34, _41_42_44),
				    -minor(_21_22_23, _31_32_33, _41_42_43),
				    
				    -minor(_12_13_14, _32_33_34, _42_43_44),
				     minor(_11_13_14, _31_33_34, _41_43_44),
				    -minor(_11_12_14, _31_32_34, _41_42_44),
				     minor(_11_12_13, _31_32_33, _41_42_43),
				    
				     minor(_12_13_14, _22_23_24, _42_43_44),
				    -minor(_11_13_14, _21_23_24, _41_43_44),
				     minor(_11_12_14, _21_22_24, _41_42_44),
				    -minor(_11_12_13, _21_22_23, _41_42_43),
				    
				    -minor(_12_13_14, _22_23_24, _32_33_34),
				     minor(_11_13_14, _21_23_24, _31_33_34),
				    -minor(_11_12_14, _21_22_24, _31_32_34),
				     minor(_11_12_13, _21_22_23, _31_32_33)
				);
				#undef minor
				return transpose(cofactors) / determinant(input);
			}

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

				///
				/// Note that the code below can handle both uniform and non-uniform scales
				///

				// Construct a matrix that transforms a point/vector from tangent space to world space
				fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
				fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
				fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 

				/*
				float4x4 tangentToWorld = float4x4(worldTangent.x, worldBinormal.x, worldNormal.x, 0.0,
												   worldTangent.y, worldBinormal.y, worldNormal.y, 0.0,
												   worldTangent.z, worldBinormal.z, worldNormal.z, 0.0,
												   0.0, 0.0, 0.0, 1.0);
				// The matrix that transforms from world space to tangent space is inverse of tangentToWorld
				float3x3 worldToTangent = inverse(tangentToWorld);
				*/
				
				//wToT = the inverse of tToW = the transpose of tToW as long as tToW is an orthogonal matrix.
				float3x3 worldToTangent = float3x3(worldTangent, worldBinormal, worldNormal);

				// Transform the light and view dir from world space to tangent space
				o.lightDir = mul(worldToTangent, WorldSpaceLightDir(v.vertex));
				o.viewDir = mul(worldToTangent, WorldSpaceViewDir(v.vertex));

				///
				/// Note that the code below can only handle uniform scales, not including non-uniform scales
				/// 

				// Compute the binormal
//				float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
//				// Construct a matrix which transform vectors from object space to tangent space
//				float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
				// Or just use the built-in macro
//				TANGENT_SPACE_ROTATION;
//				
//				// Transform the light direction from object space to tangent space
//				o.lightDir = mul(rotation, normalize(ObjSpaceLightDir(v.vertex))).xyz;
//				// Transform the view direction from object space to tangent space
//				o.viewDir = mul(rotation, normalize(ObjSpaceViewDir(v.vertex))).xyz;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {				
				fixed3 tangentLightDir = normalize(i.lightDir);
				fixed3 tangentViewDir = normalize(i.viewDir);
				
				// Get the texel in the normal map
				fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
				fixed3 tangentNormal;
				// If the texture is not marked as "Normal map"
//				tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
//				tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
				
				// Or mark the texture as "Normal map", and use the built-in funciton
				tangentNormal = UnpackNormal(packedNormal);
				tangentNormal.xy *= _BumpScale;
				tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
				
				fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));

				fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

說(shuō)人話:

        Shader  "Unity  Shaders  Book/Chapter  7/Normal  Map  In  Tangent  Space"  { }

?首先,我們?yōu)樵揢nity Shader定義一個(gè)名字。

        Properties  {
            _Color  ("Color  Tint",  Color)  =  (1,1,1,1)
            _MainTex  ("Main  Tex",  2D)  =  "white"  {}
            _BumpMap  ("Normal  Map",  2D)  =  "bump"  {}
            _BumpScale  ("Bump  Scale",  Float)  =  1.0
            _Specular  ("Specular",  Color)  =  (1,  1,  1,  1)
            _Gloss  ("Gloss",  Range(8.0,  256))  =  20
        }

?然后,我們?cè)赑roperties語(yǔ)義塊中添加了法線紋理的屬性,以及用于控制凹凸程度的屬性。

?對(duì)于法線紋理BumpMap,我們使用"bump"作為它的默認(rèn)值。"bump"是Unity內(nèi)置的法線紋理,當(dāng)沒(méi)有提供任何法線紋理時(shí),"bump"就對(duì)應(yīng)了模型自帶的法線信息。

?BumpScale則是用于控制凹凸程度的,當(dāng)它為0時(shí),意味著該法線紋理不會(huì)對(duì)光照產(chǎn)生任何影響。

        SubShader  {
            Pass  {
              Tags  {  "LightMode"="ForwardBase"  }
              }
              }

?我們?cè)赟ubShader語(yǔ)義塊中定義了一個(gè)Pass語(yǔ)義塊,并且在Pass的第一行指明了該P(yáng)ass的光照模式。

?LightMode標(biāo)簽是Pass標(biāo)簽中的一種,它用于定義該P(yáng)ass在Unity的光照流水線中的角色。

        CGPROGRAM

        #pragma  vertex  vert
        #pragma  fragment  frag

?)接著,我們使用CGPROGRAM和ENDCG來(lái)包圍住CG代碼片,以定義最重要的頂點(diǎn)著色器和片元著色器代碼。首先,我們使用#pragma指令來(lái)告訴Unity,我們定義的頂點(diǎn)著色器和片元著色器叫什么名字。在本例中,它們的名字分別是vert和frag。

        #include  "Lighting.cginc"

?為了使用Unity內(nèi)置的一些變量,如_LightColor0,還需要包含進(jìn)Unity的內(nèi)置文件Lighting.cginc。

        fixed4  _Color;
        sampler2D  _MainTex;
        float4  _MainTex_ST;
        sampler2D  _BumpMap;
        float4  _BumpMap_ST;
        float  _BumpScale;
        fixed4  _Specular;
        float  _Gloss;

?為了和Properties語(yǔ)義塊中的屬性建立聯(lián)系,我們?cè)贑G代碼塊中聲明了和上述屬性類型匹配的變量。

?為了得到該紋理的屬性(平鋪和偏移系數(shù)),我們?yōu)開(kāi)MainTex和_BumpMap定義了_MainTex_ST和_BumpMap_ST變量。

        struct  a2v  {
            float4  vertex  :  POSITION;
            float3  normal  :  NORMAL;
            float4  tangent  :  TANGENT;
            float4  texcoord  :  TEXCOORD0;
        };

?我們已經(jīng)知道,切線空間是由頂點(diǎn)法線和切線構(gòu)建出的一個(gè)坐標(biāo)空間,因此我們需要得到頂點(diǎn)的切線信息。為此,我們修改頂點(diǎn)著色器的輸入結(jié)構(gòu)體a2v。

?我們使用TANGENT語(yǔ)義來(lái)描述float4類型的tangent變量,以告訴Unity把頂點(diǎn)的切線方向填充到tangent變量中。需要注意的是,和法線方向normal不同,tangent的類型是float4,而非float3,這是因?yàn)槲覀冃枰褂胻angent.w分量來(lái)決定切線空間中的第三個(gè)坐標(biāo)軸——副切線的方向性。

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            float4  uv  :  TEXCOORD0;
            float3  lightDir:  TEXCOORD1;
            float3  viewDir  :  TEXCOORD2;
        };

?我們需要在頂點(diǎn)著色器中計(jì)算切線空間下的光照和視角方向,因此我們?cè)趘2f結(jié)構(gòu)體中添加了兩個(gè)變量來(lái)存儲(chǔ)變換后的光照和視角方向。

?由于我們使用了兩張紋理,因此需要存儲(chǔ)兩個(gè)紋理坐標(biāo)。為此,我們把v2f中的uv變量的類型定義為float4類型。

        v2f  vert(a2v  v)  {
            v2f  o;
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);

            o.uv.xy  =  v.texcoord.xy  *  _MainTex_ST.xy  +  _MainTex_ST.zw;
            o.uv.zw  =  v.texcoord.xy  *  _BumpMap_ST.xy  +  _BumpMap_ST.zw;

            //  Compute  the  binormal
        //     float3   binormal   =   cross(   normalize(v.normal),   normalize(v.tangent.xyz)   )   *
        v.tangent.w;
        //   //  Construct  a  matrix  which  transform  vectors  from  object  space  to  tangent  space
        //   float3x3  rotation  =  float3x3(v.tangent.xyz,  binormal,  v.normal);
            //  Or  just  use  the  built-in  macro
            TANGENT_SPACE_ROTATION;

            //  Transform  the  light  direction  from  object  space  to  tangent  space
            o.lightDir  =  mul(rotation,  ObjSpaceLightDir(v.vertex)).xyz;
            //  Transform  the  view  direction  from  object  space  to  tangent  space
            o.viewDir  =  mul(rotation,  ObjSpaceViewDir(v.vertex)).xyz;

            return  o;
        }

? o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

其中xy分量存儲(chǔ)了MainTex的紋理坐標(biāo),而zw分量存儲(chǔ)了BumpMap的紋理坐標(biāo)(實(shí)際上,MainTex和BumpMap通常會(huì)使用同一組紋理坐標(biāo),出于減少插值寄存器的使用數(shù)目的目的,我們往往只計(jì)算和存儲(chǔ)一個(gè)紋理坐標(biāo)即可)。

? TANGENT_SPACE_ROTATION;然后,我們把模型空間下切線方向、副切線方向和法線方向按行排列來(lái)得到從模型空間到切線空間的變換矩陣rotation。需要注意的是,在計(jì)算副切線時(shí)我們使用v.tangent.w和叉積結(jié)果進(jìn)行相乘,這是因?yàn)楹颓芯€與法線方向都垂直的方向有兩個(gè),而w決定了我們選擇其中哪一個(gè)方向。Unity也提供了一個(gè)內(nèi)置宏TANGENT_SPACE_ROTATION(在UnityCG.cginc中被定義)來(lái)幫助我們直接計(jì)算得到rotation變換矩陣,它的實(shí)現(xiàn)和上述代碼完全一樣。

? o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;

o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;

然后,我們使用Unity的內(nèi)置函數(shù)ObjSpaceLightDir和ObjSpaceViewDir來(lái)得到模型空間下的光照和視角方向,再利用變換矩陣rotation把它們從模型空間變換到切線空間中。

        fixed4  frag(v2f  i)  :  SV_Target  {
            fixed3  tangentLightDir  =  normalize(i.lightDir);
            fixed3  tangentViewDir  =  normalize(i.viewDir);

            //  Get  the  texel  in  the  normal  map
            fixed4  packedNormal  =  tex2D(_BumpMap,  i.uv.zw);
            fixed3  tangentNormal;
            //  If  the  texture  is  not  marked  as  "Normal  map"
        //   tangentNormal.xy  =  (packedNormal.xy  *  2  -  1)  *  _BumpScale;
        //   tangentNormal.z  =  sqrt(1.0  -  saturate(dot(tangentNormal.xy,  tangentNormal.xy)));

            //  Or  mark  the  texture  as  "Normal  map",  and  use  the  built-in  funciton
            tangentNormal  =  UnpackNormal(packedNormal);
            tangentNormal.xy  *=  _BumpScale;
            tangentNormal.z  =  sqrt(1.0  -  saturate(dot(tangentNormal.xy,  tangentNormal.xy)));

            fixed3  albedo  =  tex2D(_MainTex,  i.uv).rgb  *  _Color.rgb;

            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz  *  albedo;

            fixed3    diffuse    =    _LightColor0.rgb    *    albedo    *    max(0,    dot(tangentNormal,
        tangentLightDir));

            fixed3  halfDir  =  normalize(tangentLightDir  +  tangentViewDir);
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal,
        halfDir)),  _Gloss);

            return  fixed4(ambient  +  diffuse  +  specular,  1.0);
        }

?)由于我們?cè)陧旤c(diǎn)著色器中完成了大部分工作,因此片元著色器中只需要采樣得到切線空間下的法線方向,再在切線空間下進(jìn)行光照計(jì)算即可。

? fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);

在上面的代碼中,我們首先利用tex2D對(duì)法線紋理BumpMap進(jìn)行采樣。正如本節(jié)一開(kāi)頭所講的,法線紋理中存儲(chǔ)的是把法線經(jīng)過(guò)映射后得到的像素值,因此我們需要把它們反映射回來(lái)。如果我們沒(méi)有在Unity里把該法線紋理的類型設(shè)置成Normal map(詳見(jiàn)7.2.4節(jié)),就需要在代碼中手動(dòng)進(jìn)行這個(gè)過(guò)程。

? tangentNormal = UnpackNormal(packedNormal);

我們首先把packedNormal的xy分量按之前提到的公式映射回法線方向。

?tangentNormal.xy *= _BumpScale;

然后乘以BumpScale(控制凹凸程度)來(lái)得到tangentNormal的xy分量。

? tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

由于法線都是單位矢量,因此tangentNormal.z分量可以由tangentNormal.xy計(jì)算而得。由于我們使用的是切線空間下的法線紋理,因此可以保證法線方向的z分量為正。

?在Unity中,為了方便Unity對(duì)法線紋理的存儲(chǔ)進(jìn)行優(yōu)化,我們通常會(huì)把法線紋理的紋理類型標(biāo)識(shí)成Normal map, Unity會(huì)根據(jù)平臺(tái)來(lái)選擇不同的壓縮方法。這時(shí),如果我們?cè)偈褂蒙厦娴姆椒▉?lái)計(jì)算就會(huì)得到錯(cuò)誤的結(jié)果,因?yàn)榇藭r(shí)_BumpMap的rgb分量并不再是切線空間下法線方向的xyz值了。在7.2.4節(jié)中,我們會(huì)具體解釋。在這種情況下,我們可以使用Unity的內(nèi)置函數(shù)UnpackNormal來(lái)得到正確的法線方向。

        Fallback  "Specular"

?最后,我們?yōu)樵揢nity Shader設(shè)置合適的Fallback。

7.3.1 漸變紋理

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 7/Ramp Texture" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_RampTex ("Ramp Tex", 2D) = "white" {}
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Use the texture to sample the diffuse color
				fixed halfLambert  = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
				fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
				
				fixed3 diffuse = _LightColor0.rgb * diffuseColor;
				
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

說(shuō)人話:

        Shader  "Unity  Shaders  Book/Chapter  7/Ramp  Texture"  {

?我們需要為這個(gè)Shader起一個(gè)名字。

        Properties  {
            _Color  ("Color  Tint",  Color)  =  (1,1,1,1)
            _RampTex  ("Ramp  Tex",  2D)  =  "white"  {}
            _Specular  ("Specular",  Color)  =  (1,  1,  1,  1)
            _Gloss  ("Gloss",  Range(8.0,  256))  =  20
        }

?在Properties語(yǔ)義塊中聲明一個(gè)紋理屬性來(lái)存儲(chǔ)漸變紋理。

        SubShader  {
            Pass  {
              Tags  {  "LightMode"="ForwardBase"  }
              }
              }

?然后,我們?cè)赟ubShader語(yǔ)義塊中定義了一個(gè)Pass語(yǔ)義塊,并在Pass的第一行指明了該P(yáng)ass的光照模式。

        CGPROGRAM

        #pragma  vertex  vert
        #pragma  fragment  frag

?然后,我們使用CGPROGRAM和ENDCG來(lái)包圍住CG代碼片,以定義最重要的頂點(diǎn)著色器和片元著色器代碼。我們使用#pragma指令來(lái)告訴Unity,我們定義的頂點(diǎn)著色器和片元著色器叫什么名字。在本例中,它們的名字分別是vert和frag。

        #include  "Lighting.cginc"

?為了使用Unity內(nèi)置的一些變量,如_LightColor0,還需要包含進(jìn)Unity的內(nèi)置文件Lighting.cginc。

        fixed4  _Color;
        sampler2D  _RampTex;
        float4  _RampTex_ST;
        fixed4  _Specular;
        float  _Gloss;

?隨后,我們需要定義和Properties中各個(gè)屬性類型相匹配的變量。

?我們?yōu)闈u變紋理_RampTex定義了它的紋理屬性變量_RampTex_ST。

        struct  a2v  {
            float4  vertex  :  POSITION;
            float3  normal  :  NORMAL;
            float4  texcoord  :  TEXCOORD0;
        };

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            float3  worldNormal  :  TEXCOORD0;
            float3  worldPos  :  TEXCOORD1;
            float2  uv  :  TEXCOORD2;
        }

?定義頂點(diǎn)著色器的輸入和輸出結(jié)構(gòu)體。

        v2f  vert(a2v  v)  {
            v2f  o;
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);

            o.worldNormal  =  UnityObjectToWorldNormal(v.normal);

            o.worldPos  =  mul(_Object2World,  v.vertex).xyz;

            o.uv  =  TRANSFORM_TEX(v.texcoord,  _RampTex);

            return  o;
        }

?定義頂點(diǎn)著色器函數(shù)。

?TRANSFORM_TEX(v.texcoord, _RampTex)使用了內(nèi)置的TRANSFORM_TEX宏來(lái)計(jì)算經(jīng)過(guò)平鋪和偏移后的紋理坐標(biāo)。

        fixed4  frag(v2f  i)  :  SV_Target  {
            fixed3  worldNormal  =  normalize(i.worldNormal);
            fixed3  worldLightDir  =  normalize(UnityWorldSpaceLightDir(i.worldPos));

            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz;

            //  Use  the  texture  to  sample  the  diffuse  color
            fixed  halfLambert   =  0.5  *  dot(worldNormal,  worldLightDir)  +  0.5;
            fixed3   diffuseColor   =   tex2D(_RampTex,   fixed2(halfLambert,   halfLambert)).rgb   *
        _Color.rgb;

            fixed3  diffuse  =  _LightColor0.rgb  *  diffuseColor;

            fixed3  viewDir  =  normalize(UnityWorldSpaceViewDir(i.worldPos));
            fixed3  halfDir  =  normalize(worldLightDir  +  viewDir);
            fixed3  specular  =  _LightColor0.rgb  *  _Specular.rgb  *  pow(max(0,  dot(worldNormal,
        halfDir)),  _Gloss);
            return  fixed4(ambient  +  diffuse  +  specular,  1.0);
        }

?定義片元著色器函數(shù)。

?fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;

在上面的代碼中,我們使用6.4.3節(jié)中提到的半蘭伯特模型,通過(guò)對(duì)法線方向和光照方向的點(diǎn)積做一次0.5倍的縮放以及一個(gè)0.5大小的偏移來(lái)計(jì)算半蘭伯特部分halfLambert。這樣,我們得到的halfLambert的范圍被映射到了[0,1]之間。

?fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb *Color.rgb;

之后,我們使用halfLambert來(lái)構(gòu)建一個(gè)紋理坐標(biāo),并用這個(gè)紋理坐標(biāo)對(duì)漸變紋理RampTex進(jìn)行采樣。由于RampTex實(shí)際就是一個(gè)一維紋理(它在縱軸方向上顏色不變),因此紋理坐標(biāo)的u和v方向我們都使用了halfLambert。然后,把從漸變紋理采樣得到的顏色和材質(zhì)顏色_Color相乘,得到最終的漫反射顏色。

?剩下的代碼就是計(jì)算高光反射和環(huán)境光,并把它們的結(jié)果進(jìn)行相加。相信讀者已經(jīng)對(duì)這些步驟非常熟悉了。

        Fallback  "Specular"

?最后,我們?yōu)樵揢nity Shader設(shè)置合適的Fallback。

7.4.1 遮罩紋理

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 7/Mask Texture" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_BumpMap ("Normal Map", 2D) = "bump" {}
		_BumpScale("Bump Scale", Float) = 1.0
		_SpecularMask ("Specular Mask", 2D) = "white" {}
		_SpecularScale ("Specular Scale", Float) = 1.0
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _BumpMap;
			float _BumpScale;
			sampler2D _SpecularMask;
			float _SpecularScale;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 lightDir: TEXCOORD1;
				float3 viewDir : TEXCOORD2;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				
				TANGENT_SPACE_ROTATION;
				o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
				o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
			 	fixed3 tangentLightDir = normalize(i.lightDir);
				fixed3 tangentViewDir = normalize(i.viewDir);

				fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv));
				tangentNormal.xy *= _BumpScale;
				tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

				fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
				
			 	fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
			 	// Get the mask value
			 	fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
			 	// Compute specular term with the specular mask
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * specularMask;
			
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

說(shuō)人話:

        Shader  "Unity  Shaders  Book/Chapter  7/Mask  Texture"  {

?首先,我們需要為這個(gè)Shader起一個(gè)名字。

        Properties  {
            _Color  ("Color  Tint",  Color)  =  (1,1,1,1)
            _MainTex  ("Main  Tex",  2D)  =  "white"  {}
            _BumpMap  ("Normal  Map",  2D)  =  "bump"  {}
            _BumpScale("Bump  Scale",  Float)  =  1.0
            _SpecularMask  ("Specular  Mask",  2D)  =  "white"  {}
            _SpecularScale  ("Specular  Scale",  Float)  =  1.0
            _Specular  ("Specular",  Color)  =  (1,  1,  1,  1)
            _Gloss  ("Gloss",  Range(8.0,  256))  =  20
        }

?需要在Properties語(yǔ)義塊中聲明更多的變量來(lái)控制高光反射。上面屬性中的SpecularMask即是我們需要使用的高光反射遮罩紋理,SpecularScale則是用于控制遮罩影響度的系數(shù)。

        SubShader  {
            Pass  {
              Tags  {  "LightMode"="ForwardBase"  }
              }
              }

?然后,我們?cè)赟ubShader語(yǔ)義塊中定義了一個(gè)Pass語(yǔ)義塊,并在Pass的第一行指明了該P(yáng)ass的光照模式。

?LightMode標(biāo)簽是Pass標(biāo)簽中的一種,它用于定義該P(yáng)ass在Unity的光照流水線中的角色。

        CGPROGRAM

        #pragma  vertex  vert
        #pragma  fragment  frag

?我們使用CGPROGRAM和ENDCG來(lái)包圍住CG代碼片,以定義最重要的頂點(diǎn)著色器和片元著色器代碼。我們使用#pragma指令來(lái)告訴Unity,我們定義的頂點(diǎn)著色器和片元著色器叫什么名字。在本例中,它們的名字分別是vert和frag。

        #include  "Lighting.cginc"

?為了使用Unity內(nèi)置的一些變量,如_LightColor0,還需要包含進(jìn)Unity的內(nèi)置文件Lighting.cginc。

        fixed4  _Color;
        sampler2D  _MainTex;
        float4  _MainTex_ST;
        sampler2D  _BumpMap;
        float  _BumpScale;
        sampler2D  _SpecularMask;
        float  _SpecularScale;
        fixed4  _Specular;
        float  _Gloss;

?隨后,我們需要定義和Properties中各個(gè)屬性類型相匹配的變量。

?我們?yōu)橹骷y理MainTex、法線紋理BumpMap和遮罩紋理SpecularMask定義了它們共同使用的紋理屬性變量MainTex_ST。這意味著,在材質(zhì)面板中修改主紋理的平鋪系數(shù)和偏移系數(shù)會(huì)同時(shí)影響3個(gè)紋理的采樣。使用這種方式可以讓我們節(jié)省需要存儲(chǔ)的紋理坐標(biāo)數(shù)目,如果我們?yōu)槊恳粋€(gè)紋理都使用一個(gè)單獨(dú)的屬性變量TextureName_ST,那么隨著使用的紋理數(shù)目的增加,我們會(huì)迅速占滿頂點(diǎn)著色器中可以使用的插值寄存器。而很多時(shí)候,我們不需要對(duì)紋理進(jìn)行平鋪和位移操作,或者很多紋理可以使用同一種平鋪和位移操作,此時(shí)我們就可以對(duì)這些紋理使用同一個(gè)變換后的紋理坐標(biāo)進(jìn)行采樣。

        struct  a2v  {
            float4  vertex  :  POSITION;
            float3  normal  :  NORMAL;
            float4  tangent  :  TANGENT;
            float4  texcoord  :  TEXCOORD0;
        };

        struct  v2f  {
            float4  pos  :  SV_POSITION;
            float2  uv  :  TEXCOORD0;
            float3  lightDir:  TEXCOORD1;
            float3  viewDir  :  TEXCOORD2;
        };

?定義頂點(diǎn)著色器的輸入和輸出結(jié)構(gòu)體。

        v2f  vert(a2v  v)  {
            v2f  o;
            o.pos  =  mul(UNITY_MATRIX_MVP,  v.vertex);

            o.uv.xy  =  v.texcoord.xy  *  _MainTex_ST.xy  +  _MainTex_ST.zw;

            TANGENT_SPACE_ROTATION;
            o.lightDir  =  mul(rotation,  ObjSpaceLightDir(v.vertex)).xyz;
            o.viewDir  =  mul(rotation,  ObjSpaceViewDir(v.vertex)).xyz;

            return  o;
        }

? TANGENT_SPACE_ROTATION;

o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;在頂點(diǎn)著色器中,我們對(duì)光照方向和視角方向進(jìn)行了坐標(biāo)空間的變換,把它們從模型空間變換到了切線空間中,以便在片元著色器中和法線進(jìn)行光照運(yùn)算。

        fixed4  frag(v2f  i)  :  SV_Target  {
            fixed3  tangentLightDir  =  normalize(i.lightDir);
            fixed3  tangentViewDir  =  normalize(i.viewDir);

            fixed3  tangentNormal  =  UnpackNormal(tex2D(_BumpMap,  i.uv));
            tangentNormal.xy  *=  _BumpScale;
            tangentNormal.z  =  sqrt(1.0  -  saturate(dot(tangentNormal.xy,  tangentNormal.xy)));

            fixed3  albedo  =  tex2D(_MainTex,  i.uv).rgb  *  _Color.rgb;

            fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz  *  albedo;

            fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));

            fixed3  halfDir  =  normalize(tangentLightDir  +  tangentViewDir);
            //  Get  the  mask  value
            fixed  specularMask  =  tex2D(_SpecularMask,  i.uv).r  *  _SpecularScale;
            //  Compute  specular  term  with  the  specular  mask
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal,
        halfDir)),  _Gloss)  *  specularMask;

            return  fixed4(ambient  +  diffuse  +  specular,  1.0);
        }

?使用遮罩紋理的地方是片元著色器。我們使用它來(lái)控制模型表面的高光反射強(qiáng)度。

?環(huán)境光照和漫反射光照和之前使用過(guò)的代碼完全一樣。

? fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;

在計(jì)算高光反射時(shí),我們首先對(duì)遮罩紋理SpecularMask進(jìn)行采樣。由于本書(shū)使用的遮罩紋理中每個(gè)紋素的rgb分量其實(shí)都是一樣的,表明了該點(diǎn)對(duì)應(yīng)的高光反射強(qiáng)度,在這里我們選擇使用r分量來(lái)計(jì)算掩碼值。然后,我們用得到的掩碼值和SpecularScale相乘,一起來(lái)控制高光反射的強(qiáng)度。

?需要說(shuō)明的是,我們使用的這張遮罩紋理其實(shí)有很多空間被浪費(fèi)了——它的rgb分量存儲(chǔ)的都是同一個(gè)值。在實(shí)際的游戲制作中,我們往往會(huì)充分利用遮罩紋理中的每一個(gè)顏色通道來(lái)存儲(chǔ)不同的表面屬性。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-773104.html

到了這里,關(guān)于[Unity Shader入門精要]初級(jí)篇 代碼拆解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Unity中的渲染優(yōu)化技術(shù) -- Shader入門精要學(xué)習(xí)(15)

    Unity中的渲染優(yōu)化技術(shù) -- Shader入門精要學(xué)習(xí)(15)

    本章中,我們將闡述一些 Unity 中常見(jiàn)的優(yōu)化技術(shù)。這些優(yōu)化技術(shù)都是和渲染相關(guān)的,例如,使用批處理、LOD 技術(shù)等。 游戲優(yōu)化不僅是程序員的工作,更需要美工人員在游戲的美術(shù)上進(jìn)行一定的權(quán)衡。例如,避免使用全屏的屏幕特效,避免使用計(jì)算復(fù)雜的 Shader,減少透明混合

    2024年01月18日
    瀏覽(22)
  • 【UnityShader入門精要學(xué)習(xí)筆記】第三章(1)Unity Shader介紹

    【UnityShader入門精要學(xué)習(xí)筆記】第三章(1)Unity Shader介紹

    本系列為作者學(xué)習(xí)UnityShader入門精要而作的筆記,內(nèi)容將包括: 書(shū)本中句子照抄 + 個(gè)人批注 項(xiàng)目源碼 一堆新手會(huì)犯的錯(cuò)誤 潛在的太監(jiān)斷更,有始無(wú)終 總之適用于同樣開(kāi)始學(xué)習(xí)Shader的同學(xué)們進(jìn)行有取舍的參考。 從本章節(jié)開(kāi)始我們要學(xué)習(xí)Shader相關(guān)的知識(shí)了,諸位看客可能有的

    2024年02月02日
    瀏覽(25)
  • 【UnityShader入門精要學(xué)習(xí)筆記】第三章(2)Unity Shader的形式,章節(jié)答疑

    【UnityShader入門精要學(xué)習(xí)筆記】第三章(2)Unity Shader的形式,章節(jié)答疑

    本系列為作者學(xué)習(xí)UnityShader入門精要而作的筆記,內(nèi)容將包括: 書(shū)本中句子照抄 + 個(gè)人批注 項(xiàng)目源碼 一堆新手會(huì)犯的錯(cuò)誤 潛在的太監(jiān)斷更,有始無(wú)終 總之適用于同樣開(kāi)始學(xué)習(xí)Shader的同學(xué)們進(jìn)行有取舍的參考。 (該系列筆記中大多數(shù)都會(huì)復(fù)習(xí)前文的知識(shí),特別是前文知識(shí)非

    2024年02月02日
    瀏覽(19)
  • Unity入門精要03---透明效果

    Unity入門精要03---透明效果

    本節(jié)知識(shí)架構(gòu)? ? ? 如果采用了透明度混合即要是實(shí)現(xiàn)半透明效果,那么就要關(guān)閉深度寫(xiě)入,那么此時(shí)渲染順序就會(huì)變得非常非常重要,不然會(huì)出現(xiàn)不正確的遮擋效果。具體的分析可見(jiàn)書(shū)中解釋 一句話概括就是因?yàn)闆](méi)有寫(xiě)入深度,會(huì)導(dǎo)致之后讀取的時(shí)候沒(méi)有讀取到深度,就可

    2024年02月04日
    瀏覽(25)
  • 《Unity 入門精要》第8章 透明效果

    《Unity 入門精要》第8章 透明效果

    在 Unity 中,我們通常使用兩種方法來(lái)實(shí)現(xiàn)透明效果: 透明度測(cè)試(Alpha Test) 和 透明度混合(Alpha Blending) 。 當(dāng)我們渲染不透明物體時(shí),我們不需要特別考慮渲染順序的問(wèn)題,因?yàn)橛?深度緩沖(depth buffer,也稱 z-buffer) 的存在,它的基本思想是:根據(jù)深度緩存中的來(lái)判斷

    2024年02月10日
    瀏覽(28)
  • Unity入門精要04(0)-更復(fù)雜的光照

    Unity入門精要04(0)-更復(fù)雜的光照

    本書(shū)知識(shí)點(diǎn)是真的多( Unity中的渲染路徑有如下選項(xiàng)? 對(duì)某一Pass指定了渲染路徑后,Unity會(huì)在過(guò)程中自動(dòng)填充系統(tǒng)的變量,便于使用。 ? ?前向渲染的偽代碼如下 一句話概括前向渲染就是:對(duì)于每個(gè)光源在其范圍能影響物體的都計(jì)算一次所有的Pass(除了Base Pass以外),計(jì)算

    2023年04月12日
    瀏覽(20)
  • Unity游戲開(kāi)發(fā)客戶端面經(jīng)——Unity(初級(jí))

    Unity游戲開(kāi)發(fā)客戶端面經(jīng)——Unity(初級(jí))

    前言:記錄了總6w字的面經(jīng)知識(shí)點(diǎn),文章中的知識(shí)點(diǎn)若想深入了解,可以點(diǎn)擊鏈接學(xué)習(xí)。由于文本太多,按類型分開(kāi)。這一篇是 Unity 常問(wèn)問(wèn)題總結(jié),有幫助的可以收藏。? ????????Awake?—?OnEnable —?Start?—?FixedUpdate?—Update??—?LateUpdate—?OnGUl?—?OnDisable?—?OnDestr

    2024年02月02日
    瀏覽(37)
  • Unity UI適配規(guī)則和對(duì)熱門游戲適配策略的拆解

    Unity UI適配規(guī)則和對(duì)熱門游戲適配策略的拆解

    本文會(huì)介紹一些關(guān)于UI適配的基礎(chǔ)概念,并且統(tǒng)計(jì)了市面上常見(jiàn)的設(shè)備的分辨率的情況。同時(shí)通過(guò)拆解目前市面上較為成功的兩款休閑游戲Royal Match和Monopoly GO(兩款均為近期游戲付費(fèi)榜前幾的游戲),大致推斷出他們的適配策略,以供學(xué)習(xí)和參考。 設(shè)計(jì)分辨率: 設(shè)計(jì)分辨率是指

    2024年03月14日
    瀏覽(27)
  • Unity游戲圖形學(xué) Shader結(jié)構(gòu)

    Unity游戲圖形學(xué) Shader結(jié)構(gòu)

    openGL:SLG跨平臺(tái) =GLSL:openGL shaderlauguge DX:微軟開(kāi)發(fā),性能很好,但是不能跨平臺(tái) =HLSL:high level shader language CG:微軟和Nvidia公司聯(lián)合開(kāi)發(fā),跨平臺(tái),基于c語(yǔ)言開(kāi)發(fā),性能很好 openGL、dx、cg都包含 CG和HLSL包括在 CGPROGRAM...ENDCG 語(yǔ)法快內(nèi) GLSL包括在 GLSLPROGRAM...ENDGLSL 語(yǔ)法快內(nèi) 自己

    2024年02月01日
    瀏覽(17)
  • Unity游戲開(kāi)發(fā)客戶端面經(jīng)——網(wǎng)絡(luò)(初級(jí))

    Unity游戲開(kāi)發(fā)客戶端面經(jīng)——網(wǎng)絡(luò)(初級(jí))

    前言:記錄了總6w字的面經(jīng)知識(shí)點(diǎn),文章中的知識(shí)點(diǎn)若想深入了解,可以點(diǎn)擊鏈接學(xué)習(xí)。由于文本太多,按類型分開(kāi)。這一篇是 網(wǎng)絡(luò)?常問(wèn)問(wèn)題總結(jié),有幫助的可以收藏。 區(qū)別 UDP TCP 是否連接 不連接 面向連接 是否可靠 不可靠 可靠傳輸(傳輸過(guò)程中會(huì)丟失,但會(huì)重發(fā))使用

    2024年02月01日
    瀏覽(19)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包