這裏有Surface Shader的一些例子。下面的這些例子關注使用內建的光照模型;關於如何使用自定義光照模型的例子參見Surface Shader Lighting Examples。html
咱們將會以一個很是簡單的shader做爲開始,並在此基礎上逐漸完善。下面這個shader會把表面顏色置成「白色」。它使用內建的Lambert(漫反射)光照模型。app
Shader "Example/Diffuse Simple" {
SubShader {
Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float4 color : COLOR; }; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = 1; } ENDCG } Fallback "Diffuse" }
如下是使用兩個 lights 做用在某個模型上的樣子:函數
一個全白的對象太無聊了,因此讓咱們添加一個紋理。咱們將會在shader中添加一個 Properties block,這樣我門就能在材質中得到一個紋理選擇器。如下粗體爲其餘改變:優化
Shader "Example/Diffuse Texture" {
Properties {
_MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; }; sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
讓咱們添加些法線貼圖:動畫
Shader "Example/Diffuse Bump" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; }; sampler2D _MainTex; sampler2D _BumpMap; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }
如今,試着添加一些邊緣光照(Rim Lighting),來使一個對象的邊緣高亮。咱們將會添加一些基於表面法線和視線方向夾角的散射光線。爲此,咱們將會使用內建的surface shader變量viewDir。
spa
Shader "Example/Rim" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0) _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0 } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 viewDir; }; sampler2D _MainTex; sampler2D _BumpMap; float4 _RimColor; float _RimPower; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal)); o.Emission = _RimColor.rgb * pow (rim, _RimPower); } ENDCG } Fallback "Diffuse" }
爲了有不一樣的效果,讓咱們添加一個與基礎紋理結合的細節紋理。細節紋理在材質中使用相同的UV,可是一般使用不一樣的Tiling。因此咱們必須使用不一樣的UV座標來做爲輸入。3d
Shader "Example/Detail" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} _Detail ("Detail", 2D) = "gray" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float2 uv_Detail; }; sampler2D _MainTex; sampler2D _BumpMap; sampler2D _Detail; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }
使用一個棋盤格子紋理並無多大的實際意義,但能說明效果:code
屏幕空間的細節紋理是什麼樣的?這對士兵的頭部模型來講並無太大意義,可是能說明內建screenPos
輸入是怎麼用的:orm
Shader "Example/ScreenPos" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _Detail ("Detail", 2D) = "gray" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float4 screenPos; }; sampler2D _MainTex; sampler2D _Detail; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; float2 screenUV = IN.screenPos.xy / IN.screenPos.w; screenUV *= float2(8,6); o.Albedo *= tex2D (_Detail, screenUV).rgb * 2; } ENDCG } Fallback "Diffuse" }
上面移除了法線貼圖,僅僅是爲了使代碼更短。htm
這裏有一個使用內建輸入worldRefl的shader實現立方體貼圖反射。它實際上和內建的反射/漫反射shader很是像:
Shader "Example/WorldRefl" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _Cube ("Cubemap", CUBE) = "" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float3 worldRefl; }; sampler2D _MainTex; samplerCUBE _Cube; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5; o.Emission = texCUBE (_Cube, IN.worldRefl).rgb; } ENDCG } Fallback "Diffuse" }
一旦將反射顏色賦值到Emission中,咱們就能獲得一個很是閃亮的士兵:
若是你想實現由法線貼圖做用的反射效果,只需稍微再加點內容:INTERNAL_DATA
須要被添加到輸入結構中,並且在你寫入法線輸出後,WorldReflectionVector函數用來計算每一個頂點反射向量。
Shader "Example/WorldRefl Normalmap" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} _Cube ("Cubemap", CUBE) = "" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 worldRefl; INTERNAL_DATA }; sampler2D _MainTex; sampler2D _BumpMap; samplerCUBE _Cube; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); o.Emission = texCUBE (_Cube, WorldReflectionVector (IN, o.Normal)).rgb; } ENDCG } Fallback "Diffuse" }
如下是一個法線貼圖做用的光亮的士兵:
這裏的shader經過在接近水平的環上丟棄像素的方式,「切割」對象。它經過對單個世界座標下的像素,使用Cg/HLSL函數clip()來實現。咱們將會使用內建surface shader變量worldPos。
Shader "Example/Slices" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} } SubShader { Tags { "RenderType" = "Opaque" } Cull Off CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; float3 worldPos; }; sampler2D _MainTex; sampler2D _BumpMap; void surf (Input IN, inout SurfaceOutput o) { clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5); o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }
使用頂點修改器的法線擠壓(Normal Extrusion)
在頂點shader中,使用用來修改輸入頂點數據的「頂點修改器」函數是可能的。這能用於程序動畫、法線擠壓等狀況。surface shader的編譯指令 vertex:functionName用來實現它,其函數參數爲
appdata_full
。
如下的shader用來在材質中隨法線方向將頂點移動給定的量:
Shader "Example/Normal Extrusion" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _Amount ("Extrusion Amount", Range(-1,1)) = 0.5 } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; }; float _Amount; void vert (inout appdata_full v) { v.vertex.xyz += v.normal * _Amount; } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
隨法線方向移動頂點,生成了一個胖士兵:
使用頂點修改器也能用來計算頂點shader中的自定義數據,而後會被逐頂點傳入到表面shader函數中。須要使用一樣的編譯指令vertex:functionName,但該函數在這裏要接受兩個參數inout appdata_full
和out Input。你還能夠加入任意非內建的輸入參數。
注意:這種方式下的自定義成員名不能以「uv」開頭,不然將會無效。
下面的例子定義了自定義成員float customColor,在頂點函數中用於計算:
Shader "Example/Custom Vertex Data" {
Properties {
_MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; float3 customColor; }; void vert (inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input,o); o.customColor = abs(v.normal); } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Albedo *= IN.customColor; } ENDCG } Fallback "Diffuse" }
在這個例子中,customColor
被設置成法線的絕對值:
更實際的用途能夠是用來計算非內建輸入變量所提供的任意每頂點數據;或者優化shader計算。好比,用對象頂點上,而不是表面shader裏逐像素,計算邊緣光照。
可使用「最終顏色修改器」函數修改由shader計算出的最終顏色。surface shader的編譯指令 finalcolor:functionName
用來實現它,其函數參數爲
Input IN, SurfaceOutput o, inout fixed4 color
。
這裏有一個簡單地shader將染色(tint)應用到最終顏色中。這和僅僅把染色應用到表面Albedo顏色中是不一樣的:這個染色也會影響任何來自光照貼圖、光照探測和相似外源的顏色。
Shader "Example/Tint Final Color" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _ColorTint ("Tint", Color) = (1.0, 0.6, 0.6, 1.0) } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor struct Input { float2 uv_MainTex; }; fixed4 _ColorTint; void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { color *= _ColorTint; } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
最終顏色修改器(見上文)一般用於徹底實現自定義霧。霧須要影響最終計算出的像素shader顏色,這正是最終顏色修改器作的。
這裏的shader應用了基於離屏幕中心距離的霧染色。它聯合了擁有自定義頂點數據(霧)的頂點修改器和最終顏色修改器。當霧用在正向渲染的附加通道中,須要消隱成黑色。這個例子處理這種狀況,並很好地對UNITY_PASS_FORWARDADD作了檢查。
Shader "Example/Fog via Final Color" {
Properties {
_MainTex ("Texture", 2D) = "white" {} _FogColor ("Fog Color", Color) = (0.3, 0.4, 0.7, 1.0) } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor vertex:myvert struct Input { float2 uv_MainTex; half fog; }; void myvert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input,data); float4 hpos = mul (UNITY_MATRIX_MVP, v.vertex); data.fog = min (1, dot (hpos.xy, hpos.xy) * 0.1); } fixed4 _FogColor; void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { fixed3 fogColor = _FogColor.rgb; #ifdef UNITY_PASS_FORWARDADD fogColor = 0; #endif color.rgb = lerp (color.rgb, fogColor, IN.fog); } sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
Shader "Example/Linear Fog" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert finalcolor:mycolor vertex:myvert sampler2D _MainTex; uniform half4 unity_FogColor; uniform half4 unity_FogStart; uniform half4 unity_FogEnd; struct Input { float2 uv_MainTex; half fog; }; void myvert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input,data); float pos = length(mul (UNITY_MATRIX_MV, v.vertex).xyz); float diff = unity_FogEnd.x - unity_FogStart.x; float invDiff = 1.0f / diff; data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0); } void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { fixed3 fogColor = unity_FogColor.rgb; #ifdef UNITY_PASS_FORWARDADD fogColor = 0; #endif color.rgb = lerp (fogColor, color.rgb, IN.fog); } void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }