Unity ShaderLab學(xué)習(xí)總結(jié)

Why Bothers江场?

為什么已經(jīng)有ShaderForge這種可視化Shader編輯器澎办、為什么Asset Store已經(jīng)有那么多炫酷的Shader組件可下載嘲碱,還是有必要學(xué)些Shader的編寫(xiě)金砍?

2014-0718-1607-11-33.png

  • 因?yàn)樯厦孢@些Shader工具/組件最終都是以Shader文件的形式而存在。
  • 需要開(kāi)發(fā)人員/技術(shù)美術(shù)有能力對(duì)Shader進(jìn)行功能分析麦锯、效率評(píng)估恕稠、選擇、優(yōu)化扶欣、兼容鹅巍、甚至是Debug。
  • 對(duì)于特殊的需求料祠,可能還是直接編寫(xiě)Shader比較實(shí)際骆捧、高效。

總之髓绽,Shader編寫(xiě)是重要的敛苇;但至于緊不緊急,視乎項(xiàng)目需求梧宫。

涉及范圍

本文只討論Unity ShaderLab相關(guān)的知識(shí)和使用方法。但摆碉,

  • 既不討論渲染相關(guān)的基礎(chǔ)概念塘匣,基礎(chǔ)概念可參考Rendering Pipeline Overview等文章。
  • 也不討論具體的渲染技巧
  • 移動(dòng)設(shè)備GPU和桌面設(shè)備GPU硬件架構(gòu)上有較多不同點(diǎn)巷帝,詳見(jiàn)下面的“移動(dòng)設(shè)備GPU架構(gòu)簡(jiǎn)述”一章忌卤。

使用Shader

2014-0720-1007-25-36.png

如上圖,一句話總結(jié):

  1. GameObject里有MeshRenderer楞泼,
  2. MeshRenderer里有Material列表驰徊,
  3. 每個(gè)Material里有且只有一個(gè)Shader;
  4. Material在編輯器暴露該Shader的可調(diào)屬性堕阔。

所以關(guān)鍵是怎么編寫(xiě)Shader棍厂。

Shader基礎(chǔ)

編輯器

使用MonoDevelop這反人類的IDE來(lái)編寫(xiě)Shader居然是讓人滿意的。有語(yǔ)法高亮超陆,無(wú)語(yǔ)法提示牺弹。
如果習(xí)慣VisualStudio,可以如下實(shí)現(xiàn).Shader文件的語(yǔ)法高亮时呀。

  • 下載作者donaldwu自己添加的關(guān)鍵詞文件usertype.dat张漂。其包括了Unity ShaderLab的部分關(guān)鍵字,和HLSL的所有關(guān)鍵字谨娜。關(guān)鍵字以后持續(xù)添加中航攒。
  • 將下載的usertype.dat放到Microsoft Visual Studio xx.x\CommonX\IDE\文件夾下;
  • 打開(kāi)VS趴梢,工具>選項(xiàng)>文本編輯器>文件擴(kuò)展名漠畜,擴(kuò)展名里填“shader”币他,編輯器選VC++,點(diǎn)擊添加盆驹;
  • 重啟VS圆丹,Done。

Shader

Shader "ShaderLab Tutorials/TestShader"
{
    // ...
}
2014-0720-1707-17-42.png

Shader的名字會(huì)直接決定shader在material里出現(xiàn)的路徑

SubShader

Shader "ShaderLab Tutorials/TestShader" {
    SubShader
    {
        //...
    }
}

一個(gè)Shader有多個(gè)SubShader躯喇。一個(gè)SubShader可理解為一個(gè)Shader的一個(gè)渲染方案辫封。即SubShader是為了針對(duì)不同的渲染情況而編寫(xiě)的。每個(gè)Shader至少1個(gè)SubShader廉丽、理論可以無(wú)限多個(gè)倦微,但往往兩三個(gè)就足夠。
一個(gè)時(shí)刻只會(huì)選取一個(gè)SubShader進(jìn)行渲染正压,具體SubShader的選取規(guī)則包括:

  • 從上到下選取
  • SubShader的標(biāo)簽欣福、Pass的標(biāo)簽
    • 是否符合當(dāng)前的“Unity渲染路徑”
    • 是否符合當(dāng)前的ReplacementTag
  • SubShader是否和當(dāng)前的GPU兼容

按此規(guī)則第一個(gè)被選取的SubShader將會(huì)用于渲染,未被選取的SubShader在這次渲染將被忽略焦履。

SubShader的Tag

Shader "ShaderLab Tutorials/TestShader" {
    SubShader
    {
        Tags { "Queue"="Geometry+10" "RenderType"="Opaque" }
        //...
    }
}

SubShader內(nèi)部可以有標(biāo)簽(Tags)的定義拓劝。Tag指定了這個(gè)SubShader的渲染順序(時(shí)機(jī)),以及其他的一些設(shè)置嘉裤。

  • "RenderType"標(biāo)簽郑临。Unity可以運(yùn)行時(shí)替換符合特定RenderType的所有Shader。Camera.RenderWithShaderCamera.SetReplacementShader配合使用屑宠。Unity內(nèi)置的RenderType包括:
    • "Opaque":絕大部分不透明的物體都使用這個(gè)厢洞;
    • "Transparent":絕大部分透明的物體、包括粒子特效都使用這個(gè)典奉;
    • "Background":天空盒都使用這個(gè)躺翻;
    • "Overlay":GUI、鏡頭光暈都使用這個(gè)卫玖;
    • 用戶也可以定義任意自己的RenderType這個(gè)標(biāo)簽所取的值公你。
    • 應(yīng)注意,Camera.RenderWithShaderCamera.SetReplacementShader不要求標(biāo)簽只能是RenderType假瞬,RenderType只是Unity內(nèi)部用于Replace的一個(gè)標(biāo)簽而已省店,你也可以自定義自己全新的標(biāo)簽用于Replace。
      比如笨触,你為自己的ShaderA.SubShaderA1(會(huì)被Unity選取到的SubShader懦傍,常為Shader文件中的第一個(gè)SubShader)增加Tag為"Distort"="On",然后將"Distort"作為參數(shù)replacementTag傳給函數(shù)芦劣。此時(shí)粗俱,作為replacementShader實(shí)參的ShaderB.SubShaderB1中若有也有一模一樣的"Distort"="On"、且這A的材質(zhì)參數(shù)包含B所需材質(zhì)參數(shù)虚吟,則此SubShaderB1將代替SubShaderA1用于本次渲染寸认。
    • 具體可參考Rendering with Replaced Shaders
  • "Queue"標(biāo)簽签财。定義渲染順序。預(yù)制的值為
  • "Background"偏塞。值為1000唱蒸。比如用于天空盒。
  • "Geometry"灸叼。值為2000神汹。大部分物體在這個(gè)隊(duì)列。不透明的物體也在這里古今。這個(gè)隊(duì)列內(nèi)部的物體的渲染順序會(huì)有進(jìn)一步的優(yōu)化(應(yīng)該是從近到遠(yuǎn)屁魏,early-z test可以剔除不需經(jīng)過(guò)FS處理的片元)。其他隊(duì)列的物體都是按空間位置的從遠(yuǎn)到近進(jìn)行渲染捉腥。
  • "AlphaTest"氓拼。值為2450。已進(jìn)行AlphaTest的物體在這個(gè)隊(duì)列抵碟。
  • "Transparent"桃漾。值為3000。透明物體拟逮。
  • "Overlay"撬统。值為4000。比如鏡頭光暈唱歧。
  • 用戶可以定義任意值宪摧,比如"Queue"="Geometry+10"
  • "ForceNoShadowCasting"粒竖,值為"true"時(shí)颅崩,表示不接受陰影。
  • "IgnoreProjector"蕊苗,值為"true"時(shí)沿后,表示不接受Projector組件的投影。

另朽砰,關(guān)于渲染隊(duì)列和Batch的非官方經(jīng)驗(yàn)總結(jié)是尖滚,一幀的渲染隊(duì)列的生成,依次決定于每個(gè)渲染物體的:

  • Shader的RenderType tag,
  • Renderer.SortingLayerID,
  • Renderer.SortingOrder,
  • Material.renderQueue(默認(rèn)值為Shader里的"Queue"),
  • Transform.z(ViewSpace)(默認(rèn)為按z值從前到后瞧柔,但當(dāng)Queue是“Transparent”的時(shí)候漆弄,按z值從后到前)。

這個(gè)渲染隊(duì)列決定了之后(可能有dirty flag的機(jī)制造锅?)渲染器再依次遍歷這個(gè)渲染隊(duì)列撼唾,“同一種”材質(zhì)的渲染物體合到一個(gè)Batch里。

Pass

Shader "ShaderLab Tutorials/TestShader" {
    SubShader {
        Pass
        {
            //...
        }
    }
}

一個(gè)SubShader(渲染方案)是由一個(gè)個(gè)Pass塊來(lái)執(zhí)行的哥蔚。每個(gè)Pass都會(huì)消耗對(duì)應(yīng)的一個(gè)DrawCall倒谷。在滿足渲染效果的情況下盡可能地減少Pass的數(shù)量蛛蒙。

Pass的Tag

Shader "ShaderLab Tutorials/TestShader" {
    SubShader {
        Pass
        {
            Tags{ "LightMode"="ForwardBase" }
            //...
        }
    }
}

和SubShader有自己專屬的Tag類似,Pass也有Pass專屬的Tag渤愁。
其中最重要Tag是 "LightMode"牵祟,指定Pass和Unity的哪一種渲染路徑(“Rendering Path”)搭配使用。除最重要的ForwardBase抖格、ForwardAdd外诺苹,這里需額外提醒的Tag取值可包括:

  • Always,永遠(yuǎn)都渲染他挎,但不處理光照
  • ShadowCaster筝尾,用于渲染產(chǎn)生陰影的物體
  • ShadowCollector,用于收集物體陰影到屏幕坐標(biāo)Buff里办桨。

其他渲染路徑相關(guān)的Tag詳見(jiàn)下面章節(jié)“Unity渲染路徑種類”筹淫。
具體所有Tag取值,可參考ShaderLab syntax: Pass Tags呢撞。

FallBack

Shader "ShaderLab Tutorials/TestShader"{
    SubShader { Pass {} }
    
    FallBack "Diffuse" // "Diffuse"即Unity預(yù)制的固有Shader
    // FallBack Off //將關(guān)閉FallBack
}

當(dāng)本Shader的所有SubShader都不支持當(dāng)前顯卡损姜,就會(huì)使用FallBack語(yǔ)句指定的另一個(gè)Shader。FallBack最好指定Unity自己預(yù)制的Shader實(shí)現(xiàn)殊霞,因其一般能夠在當(dāng)前所有顯卡運(yùn)行摧阅。

Properties

Shader "ShaderLab Tutorials/TestShader"
{
    Properties {
    _Range ("My Range", Range (0.02,0.15)) = 0.07 // sliders
    _Color ("My Color", Color) = (.34, .85, .92, 1) // color
    _2D ("My Texture 2D", 2D) = "" {} // textures
    _Rect("My Rectangle", Rect) = "name" { }
    _Cube ("My Cubemap", Cube) = "name" { }
    _Float ("My Float", Float) = 1
    _Vector ("My Vector", Vector) = (1,2,3,4)

    // Display as a toggle.
    [Toggle] _Invert ("Invert color?", Float) = 0
    // Blend mode values
    [Enum(UnityEngine.Rendering.BlendMode)] _Blend ("Blend mode", Float) = 1
    //setup corresponding shader keywords.
    [KeywordEnum(Off, On)] _UseSpecular ("Use Specular",  Float) = 0
    }
    
    // Shader
    SubShader{
        Pass{
          //...
          uniform float4 _Color;
          //...
          float4 frag() : COLOR{ return fixed4(_Color); }
          //...
             #pragma multi_compile __ _USESPECULAR_ON
          }
    }
    
    //fixed pipeline
    SubShader   {
        Pass{
            Color[_Color]
        }
    }
}
  • Shader在Unity編輯器暴露給美術(shù)的參數(shù),通過(guò)Properties來(lái)實(shí)現(xiàn)绷蹲。
  • 所有可能的參數(shù)如上所示棒卷。主要也就Float、Vector和Texture這3類祝钢。
  • 除了通過(guò)編輯器編輯Properties比规,腳本也可以通過(guò)Material的接口(比如SetFloatSetTexture編輯)
  • 之后在Shader程序通過(guò)[name](固定管線)或直接name(可編程Shader)訪問(wèn)這些屬性拦英。
  • 在每一個(gè)Property前面也能類似C#那樣添加Attribute蜒什,以達(dá)到額外UI面板功能。詳見(jiàn)MaterialPropertyDrawer.html疤估。

Shader中的數(shù)據(jù)類型

有3種基本數(shù)值類型:float灾常、halffixed
這3種基本數(shù)值類型可以再組成vector和matrix铃拇,比如half3是由3個(gè)half組成钞瀑、float4x4是由16個(gè)float組成。

  • float:32位高精度浮點(diǎn)數(shù)慷荔。
  • half:16位中精度浮點(diǎn)數(shù)雕什。范圍是[-6萬(wàn), +6萬(wàn)],能精確到十進(jìn)制的小數(shù)點(diǎn)后3.3位。
  • fixed:11位低精度浮點(diǎn)數(shù)监徘。范圍是[-2, 2]晋修,精度是1/256。

數(shù)據(jù)類型影響性能

  • 精度夠用就好凰盔。
    • 顏色和單位向量墓卦,使用fixed
    • 其他情況,盡量使用half(即范圍在[-6萬(wàn), +6萬(wàn)]內(nèi)户敬、精確到小數(shù)點(diǎn)后3.3位)落剪;否則才使用float

ShaderLab中的Matrix

當(dāng)提到“Row-Major”尿庐、“Column-Major”忠怖,根據(jù)不同的場(chǎng)合,它們可能指不同的意思:

  • 數(shù)學(xué)上的抄瑟,主要是指矢量V是Row Vector凡泣、還是Column Vector。引用自[Game Engine Architecture 2nd Edition, 183]皮假。留意到V和M的乘法鞋拟,當(dāng)是Row Vector的時(shí)候,數(shù)學(xué)上寫(xiě)作VM惹资,Matrix在右邊贺纲,Matrix的最下面一行表示Translate;當(dāng)是Column Vector的時(shí)候褪测,數(shù)學(xué)上寫(xiě)作MtVt猴誊,Matrix在左邊并且需要轉(zhuǎn)置,Matrix最右面一列表示Translate侮措。
  • 訪問(wèn)接口上的:Row-Major即MyMatrix[Row][Column]懈叹、Column-Major即MyMatrix[Column][Row]。HLSL/CG的訪問(wèn)接口都是Row-Major萝毛,比如MyMatrix[3]返回的是第3行项阴;GLSL的訪問(wèn)接口是Column-Major滑黔,比如MyMatrix[3]返回的是第3列笆包。
  • 寄存器存儲(chǔ)上的:每個(gè)元素是按行存儲(chǔ)在寄存器中、還是按列存儲(chǔ)在寄存器中略荡。需要關(guān)注它的一般情況舉例是庵佣,float2x3的MyMatrix,到底是占用2個(gè)寄存器(Row-Major)汛兜、還是3個(gè)寄存器(Column-Major)巴粪。在HLSL里,可以通過(guò)#pragmapack_matrix設(shè)定row_major或者column_major。

上述情況肛根,互不相干辫塌。
然后,ShaderLab中派哲,數(shù)學(xué)上是Column Vector臼氨、訪問(wèn)接口上是Row-Major、存儲(chǔ)上是(尚未查明)芭届。

ShaderLab中各個(gè)Space的坐標(biāo)系

一般情況下储矩,從Vertex Buff輸入頂點(diǎn)到Vertex Shader,

  • 該頂點(diǎn)為左手坐標(biāo)系Model Space中的頂點(diǎn)vInModel褂乍,
    其用w=1的Homogenous Cooridniates(故等效于Cartesian Coordinates)表達(dá)vInModel = float4(xm, ym, zm, 1)持隧;
  • vInWrold = mul(_Object2World , vInModel)后,得出左手坐標(biāo)系World Space中的vInWorld逃片,其為w=1的Homogenous Cooridniates(故等效于Cartesian Coordinates)vInWorld = float4(xw, yw, zw, 1)屡拨;
  • vInView = mul(UNITY_MATRIX_V , vInWrold)后,得出右手坐標(biāo)系View Space中的vInView褥实,其為w=1的Homogenous Cooridniates(故等效于Cartesian Coordinates)vInWorld = float4(xv, yv, zv, 1)洁仗;
  • vInClip = mul(UNITY_MATRIX_P , vInView)后,得出左手坐標(biāo)系Clip Space中的vInClip性锭,其為w往往不等于1的Homogenous Cooridniates(故往往不等效于Cartesian Coordinates)vInClip = float4(xc, yc, zc, wc)赠潦;
    設(shè)r、l草冈、t她奥、b、n怎棱、f的長(zhǎng)度絕對(duì)值如下圖:

    注意View Space中攝像機(jī)前方的z值為負(fù)數(shù)哩俭、-z為正數(shù)。則GL/DX/Metal的Clip Space坐標(biāo)為:
    • GL:
      • xc=(2nx+rz+lz)/(r-l);
      • yc=(2ny+tz+bz)/(t-b);
      • zc=(-fz-nz-2nf)/(f-n);
      • wc=-z;
    • DX/Metal:
      • xc=(2nx+rz+lz)/(r-l);
      • yc=(2ny+tz+bz)/(t-b);
      • zc=(-fz-nf)/(f-n);
      • wc=-z;
  • vInNDC = vInClip / vInClip.w后拳恋,得出左手坐標(biāo)系Normalized Device Coordinates中的vInNDC凡资,其為w=1的Homogenous Cooridniates(故等效于Cartesian Coordinates)vInNDC = float4(xn, yn, zn, 1)
    xnyn的取值范圍為[-1,1]谬运。
    • GL: zn=zc/wc=(fz+nz+2nf)/((f-n)z);
    • DX/Metal: zn=zc/wc=(fz+nf)/((f-n)z);
    • 在Unity中隙赁,zn的取值范圍可以這樣決定:
      • 如果UNITY_REVERSED_Z已定義,zn的取值范圍是[UNITY_NEAR_CLIP_VALUE, 0]梆暖,即[1,0]
      • 如果UNITY_REVERSED_Z未定義伞访,zn的取值范圍是[UNITY_NEAR_CLIP_VALUE, 1]
        • 如果SHADER_API_D3D9/SHADER_API_D3D11_9X定義了,即[0,1]
        • 否則轰驳,即OpenGL情況厚掷,即[-1,1]
v2f vert (appdata v)
{
    v2f o;
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    // 1 弟灼、2、3是等價(jià)的冒黑,和4是不等價(jià)的
    // 因?yàn)槭荕在左田绑、V在右,所以是Column Vector
    // 因?yàn)槭荋LSL/CG語(yǔ)言抡爹,所以是訪問(wèn)方式是Row-Major
    o.rootInView = mul(UNITY_MATRIX_MV, float4(0, 0, 0, 1)); // 1
    o.rootInView = float4(UNITY_MATRIX_MV[0].w, UNITY_MATRIX_MV[1].w, UNITY_MATRIX_MV[2].w, 1); // 2                
    o.rootInView = UNITY_MATRIX_MV._m03_m13_m23_m33;  // 3
    //o.rootInView = UNITY_MATRIX_MV[3]; // 4

    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    // 因?yàn)槭荲iewSpace是右手坐標(biāo)系辛馆,所以當(dāng)root在view前面的時(shí)候,z是負(fù)數(shù)豁延,所以需要-z才能正確顯示顏色
    fixed4 col = fixed4(i.rootInView.x, i.rootInView.y, -i.rootInView.z, 1);
    return col;
}

struct appdata
{
    float4 vertex : POSITION;
};
struct v2f
{
    float4 rootInView : TEXCOORD0;
    float4 vertex : SV_POSITION;
};

Shader形態(tài)

Shader形態(tài)之1:固定管線

固定管線是為了兼容老式顯卡昙篙。都是頂點(diǎn)光照。之后固定管線可能是被Unity拋棄的功能诱咏,所以最好不學(xué)它苔可、當(dāng)它不存在。特征是里面出現(xiàn)了形如下面Material塊袋狞、沒(méi)有CGPROGRAMENDCG塊焚辅。

Shader "ShaderLab Tutorials/TestShader"
{
    Properties {
    _Color ("My Color", Color) = (.34, .85, .92, 1) // color
    }
    
    // Fixed Pipeline
    SubShader
    {
        Pass
        {
            Material{
            Diffuse [_Color]
            Ambient [_Color]
            }
            
            Lighting On
        }
    }
}

Shader形態(tài)之2:可編程Shader

Shader "ShaderLab Tutorials/TestShader"
{
    Properties {}
    
    SubShader
    {
        Pass
        {
          // ... the usual pass state setup ...
          
          CGPROGRAM
          // compilation directives for this snippet, e.g.:
          #pragma vertex vert
          #pragma fragment frag
          
          // the Cg/HLSL code itself
          float4 vert(float4 v:POSITION) : SV_POSITION{
            return mul(UNITY_MATRIX_MVP, v);
          }
          float4 frag() : COLOR{
            return fixed4(1.0, 0.0, 0.0, 1.0);
          }
          ENDCG
          // ... the rest of pass setup ...
          }
    }
}
  • 功能最強(qiáng)大、最自由的形態(tài)苟鸯。
  • 特征是在Pass里出現(xiàn)CGPROGRAMENDCG
  • 編譯指令#pragma同蜻。詳見(jiàn)官網(wǎng)Cg snippets。其中重要的包括:
編譯指令 示例/含義
#pragma vertex name
#pragma fragment name
替換name早处,來(lái)指定Vertex Shader函數(shù)湾蔓、Fragment Shader函數(shù)。
#pragma target name 替換name(為2.0砌梆、3.0等)默责。設(shè)置編譯目標(biāo)shader model的版本。
#pragma only_renderers name name ...
#pragma exclude_renderers name name...
#pragma only_renderers gles gles3咸包,
#pragma exclude_renderers d3d9 d3d11 opengl桃序,
只為指定渲染平臺(tái)(render platform)編譯
  • 引用庫(kù)。通過(guò)形如#include "UnityCG.cginc"引入指定的庫(kù)烂瘫。常用的就是UnityCG.cginc了媒熊。其他庫(kù)詳見(jiàn)官網(wǎng)Built-in shader include files
  • ShaderLab內(nèi)置值坟比。Unity給Shader程序提供了便捷的芦鳍、常用的值,比如下面例子中的UNITY_MATRIX_MVP就代表了這個(gè)時(shí)刻的MVP矩陣温算。詳見(jiàn)官網(wǎng)ShaderLab built-in values怜校。
  • Shader輸入輸出參數(shù)語(yǔ)義(Semantics)间影。在管線流程中每個(gè)階段之間(比如Vertex Shader階段和FragmentShader階段之間)的輸入輸出參數(shù)注竿,通過(guò)語(yǔ)義字符串,來(lái)指定參數(shù)的含義。常用的語(yǔ)義包括:COLOR巩割、SV_Position裙顽、TEXCOORD[n]。完整的參數(shù)語(yǔ)義可見(jiàn)HLSL Semantic(由于是HLSL的連接宣谈,所以可能不完全在Unity里可以使用)愈犹。
  • 特別地,因?yàn)閂ertex Shader的的輸入往往是管線的最開(kāi)始闻丑,Unity為此內(nèi)置了常用的數(shù)據(jù)結(jié)構(gòu):
數(shù)據(jù)結(jié)構(gòu) 含義
appdata_base vertex shader input with position, normal, one texture coordinate.
appdata_tan vertex shader input with position, normal, tangent, one texture coordinate.
appdata_full vertex shader input with position, normal, tangent, vertex color and two texture coordinates.
appdata_img vertex shader input with position and one texture coordinate.

Shader形態(tài)之3:SurfaceShader

Shader "ShaderLab Tutorials/TestShader"
{
    Properties {   }

    // Surface Shader
    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"
}
  • SurfaceShader可以認(rèn)為是一個(gè)光照Shader的語(yǔ)法糖漩怎、一個(gè)光照VS/FS的生成器。減少了開(kāi)發(fā)者寫(xiě)重復(fù)代碼的需要肺缕。
  • 在手游覆旱,由于對(duì)性能要求比較高遥缕,所以不建議使用SurfaceShader。因?yàn)镾urfaceShader是一個(gè)比較“通用”的功能叁执,而通用往往導(dǎo)致性能不高。
  • 特征是在SubShader里出現(xiàn)CGPROGRAMENDCG塊矮冬。(而不是出現(xiàn)在Pass里谈宛。因?yàn)镾urfaceShader自己會(huì)編譯成多個(gè)Pass。)
  • 編譯指令是:
    #pragma surface surfaceFunction lightModel [optionalparams]
  • surfaceFunction:surfaceShader函數(shù)胎署,形如void surf (Input IN, inout SurfaceOutput o)
  • lightModel:使用的光照模式吆录。包括Lambert(漫反射)和BlinnPhong(鏡面反射)。
    • 也可以自己定義光照函數(shù)琼牧。比如編譯指令為#pragma surface surf MyCalc
      • 在Shader里定義half4 LightingMyCalc (SurfaceOutput s, 參數(shù)略)函數(shù)進(jìn)行處理(函數(shù)名在簽名加上了“Lighting”)径筏。
      • 詳見(jiàn)Custom Lighting models in Surface Shaders
  • 你定義輸入數(shù)據(jù)結(jié)構(gòu)(比如上面的Input)、編寫(xiě)自己的Surface函數(shù)處理輸入障陶、最終輸出修改過(guò)后的SurfaceOutput滋恬。SurfaceOutput的定義為
struct SurfaceOutput {
    half3 Albedo; // 紋理顏色值(r, g, b)
    half3 Normal; // 法向量(x, y, z)
    half3 Emission; // 自發(fā)光顏色值(r, g, b)
    half Specular; // 鏡面反射度
    half Gloss; // 光澤度
    half Alpha; // 不透明度
};

Shader形態(tài)之4:Compiled Shader

點(diǎn)擊a.shader文件的“Compile and show code”,可以看到該文件的“編譯”過(guò)后的ShaderLab shader文件抱究,文件名形如Compiled-a.shader恢氯。
其依然是ShaderLab文件,其包含最終提交給GPU的shader代碼字符串鼓寺。
先就其結(jié)構(gòu)進(jìn)行簡(jiǎn)述如下勋拟,會(huì)發(fā)現(xiàn)和上述的編譯前ShaderLab結(jié)構(gòu)很相似。


// Compiled shader for iPhone, iPod Touch and iPad, uncompressed size: 36.5KB
// Skipping shader variants that would not be included into build of current scene.
Shader "ShaderLab Tutorials/TestShader"
{
    Properties {...}
    SubShader {
        // Stats for Vertex shader:
        //        gles : 14 avg math (11..19), 1 avg texture (1..2)
        //       metal : 14 avg math (11..17)
        // Stats for Fragment shader:
        //       metal : 14 avg math (11..19), 1 avg texture (1..2)
        Pass {
            Program "vp" // vertex program
            {
                SubProgram "gles" {
                    // Stats: 11 math, 1 textures
                    Keywords{...} // keywords for shader variants ("uber shader")

                    //shader codes in string
                    "
                    #ifdef VERTEX
                    vertex shader codes
                    #endif

                    // Note, on gles, fragment shader stays here inside Program "vp"
                    #ifdef FRAGMENT
                    fragment shader codes
                    #endif
                    " 
                }

                SubProgram "metal"  {
                    some setup
                    Keywords{...}

                    //vertex shader codes in string
                    "..."
                }
            }

            Program "fp" // fragment program
            {
                SubProgram "gles" {
                    Keywords{...}
                    "http:// shader disassembly not supported on gles" //(because gles fragment shader codes are in Program "vp") 
                }

                SubProgram "metal" {
                    common setup
                    Keywords{...}

                    //fragment shader codes in string
                    "..."
                }
            }
        }
    }

    ...
}

Unity渲染路徑(Rendering Path)種類

概述

開(kāi)發(fā)者可以在Unity工程的PlayerSettings設(shè)置對(duì)渲染路徑進(jìn)行3選1:

  • Deferred Lighting妈候,延遲光照路徑敢靡。3者中最高質(zhì)量地還原光照陰影。光照性能只與最終像素?cái)?shù)目有關(guān)苦银,光源數(shù)量再多都不會(huì)影響性能啸胧。
  • Forward Rendering赶站,順序渲染路徑。能發(fā)揮出Shader全部特性的渲染路徑纺念,當(dāng)然也就支持像素級(jí)光照贝椿。最常用、功能最自由陷谱,性能與光源數(shù)目*受光照物體數(shù)目有關(guān)烙博,具體性能視乎其具體使用到的Shader的復(fù)雜度。
  • Vertex Lit烟逊,頂點(diǎn)光照路徑渣窜。頂點(diǎn)級(jí)光照。性能最高宪躯、兼容性最強(qiáng)图毕、支持特性最少、品質(zhì)最差眷唉。

渲染路徑的內(nèi)部階段和Pass的LightMode標(biāo)簽

每個(gè)渲染路徑的內(nèi)部會(huì)再分為幾個(gè)階段予颤。
然后,Shader里的每個(gè)Pass冬阳,都可以指定為不同的LightMode蛤虐。而LightMode實(shí)際就是說(shuō):“我希望這個(gè)Pass在這個(gè)XXX渲染路徑的這個(gè)YYY子階段被執(zhí)行”。

Deferred Ligting

|渲染路徑內(nèi)部子階段|對(duì)應(yīng)的LightMode|描述
|-|-|
|Base Pass|"PrepassBase"|渲染物體信息肝陪。即把法向量驳庭、高光度到一張ARGB32的物體信息紋理上,把深度信息保存在Z-Buff上氯窍。|
|Lighting Pass|無(wú)對(duì)應(yīng)可編程Pass|根據(jù)Base Pass得出的物體信息饲常,在屏幕坐標(biāo)系下,使用BlinnPhong光照模式狼讨,把光照信息渲染到ARGB32的光照信息紋理上(RGB表示diffuse顏色值贝淤、A表示高光度)
|Final Pass|"PrepassFinal"|根據(jù)光照信息紋理,物體再渲染一次政供,將光照信息播聪、紋理信息和自發(fā)光信息最終混合。LightMap也在這個(gè)Pass進(jìn)行布隔。

Forward Rendering

|渲染路徑內(nèi)部子階段|對(duì)應(yīng)的LightMode|描述
|-|-|
|Base Pass|"ForwardBase"|渲染:最亮一個(gè)的方向光光源(像素級(jí))和對(duì)應(yīng)的陰影离陶、所有頂點(diǎn)級(jí)光源、LightMap衅檀、所有LightProbe的SH光源(Sphere Harmonic招刨,球諧函數(shù),效率超高的低頻光)哀军、環(huán)境光沉眶、自發(fā)光打却。|
|Additional Passes|"ForwardAdd"|其他需要像素級(jí)渲染的的光源
注意到的是,在Forward Rendering中沦寂,光源可能是像素級(jí)光源学密、頂點(diǎn)級(jí)光源或SH光源淘衙。其判斷標(biāo)準(zhǔn)是:

  • 配制成“Not Important”的光源都是頂點(diǎn)級(jí)光源和SH光源
  • 最亮的方向光永遠(yuǎn)都是像素級(jí)光源
  • 配置成“Important”的都是像素級(jí)光源
  • 上面2種情況加起來(lái)的像素級(jí)光源數(shù)目小于“Quality Settings”里面的“Pixel Light Count”的話传藏,會(huì)把第1種情況的光源補(bǔ)為額外的像素級(jí)光源。

另外彤守,配置成“Auto”的光源有更復(fù)雜的判斷標(biāo)注毯侦,截圖如下:

2014-0720-1607-31-40.png

具體可參考Forward Rendering Path Details

Vertex Lit

|渲染路徑內(nèi)部子階段|對(duì)應(yīng)的LightMode|描述
|-|-|
|Vertex|"Vertex"|渲染無(wú)LightMap物體|
|VertexLMRGBM|"VertexLMRGBM"|渲染有RGBM編碼的LightMap物體
|VertexLM|"VertexLM"|渲染有雙LDR編碼的LightMap物體

不同LightMode的Pass的被選擇

一個(gè)工程的渲染路徑是唯一的具垫,但一個(gè)工程里的Shader是允許配有不同LightMode的Pass的侈离。
在Unity,策略是“從工程配置的渲染路徑模式開(kāi)始筝蚕,按Deferred卦碾、Forward、VertxLit的順序起宽,搜索最匹配的LightMode的一個(gè)Pass”洲胖。
比如,在配置成Deferred路徑時(shí)坯沪,優(yōu)先選有Deferred相關(guān)LightMode的Pass绿映;找不到才會(huì)選Forward相關(guān)的Pass;還找不到腐晾,才會(huì)選VertexLit相關(guān)的Pass叉弦。
再比如,在配置成Forward路徑時(shí)藻糖,優(yōu)先選Forward相關(guān)的Pass淹冰;找不到才會(huì)選VertexLit相關(guān)的Pass。

移動(dòng)設(shè)備GPU架構(gòu)簡(jiǎn)述

《The Mali GPU: An Abstract Machine》系列以Arm Mali GPU為例子給出了全面的討論巨柒,現(xiàn)簡(jiǎn)述如下:

  • Part 1 - Frame Pipelining
    • Application/Geometry/Fragment三階段組成榄棵,三者中最大才是瓶頸
    • OpenGL的同步API是個(gè)“illusion”,事實(shí)上是CommandQueue(直到遇到Fence會(huì)被強(qiáng)制同步),以減少CPU/GPU之間的互相等待
    • Pipeline Throttle潘拱,為了更低的延遲疹鳄,當(dāng)GPU累積了多幀(往往是3幀,以eglSwapBuffers()Present()來(lái)區(qū)分幀)的Command時(shí)芦岂,OS會(huì)通過(guò)eglSwapBuffers()Present()來(lái)阻塞CPU讓其進(jìn)入idle瘪弓,從而防止更多后續(xù)Command的提交
  • Part 2 - Tile-based Rendering
    • tile-based deferred rendering (WikiPowerVR/Mali/Adreno)是重要的概念禽最。其將Fragment一幀處理多個(gè)比如16x16的單元腺怯,并為Shader集成一個(gè)小但快的cache袱饭,從而大幅避免Shader和主內(nèi)存之間帶寬消耗(電量消耗)
  • Part 3 - The Midgard Shader Core
    • GPU包含數(shù)個(gè)(當(dāng)前常見(jiàn)為4-8個(gè))Unified Shading Core,可動(dòng)態(tài)分配用于Vertex Shader呛占、Fragment Shader或Compute Kernel
    • 每個(gè)Unified Shader Core包含數(shù)個(gè)(當(dāng)前常見(jiàn)為2個(gè))用于SIMD計(jì)算的運(yùn)算器Arithmetic Pipeline(A-pipe)虑乖,1個(gè)用于紋理采樣的Texutre Pipeline(T-pipe),1個(gè)用于非紋理類的內(nèi)存讀寫(xiě)的Load/Store Pipeline(LS-pipe)比如頂點(diǎn)屬性寫(xiě)讀晾虑、變量訪問(wèn)等
    • 會(huì)進(jìn)行Early-ZS測(cè)試嘗試減少Overdraw(依賴于渲染物體提交順序由前至后)
    • Arm的Forward Pixel Kill和PowerVR的Hidden Surface Removal做到像素級(jí)別的Overdraw減少(不用依賴于渲染物體提交順序由前至后)
    • 當(dāng)Shader使用discardclip疹味、在Fragment Shader里修改深度值、半透明帜篇,將不能進(jìn)行Early-ZS糙捺,只好使用傳統(tǒng)的Late-ZS
  • Part 4 - The Bifrost Shader Core
    • 2016年的新型號(hào),對(duì)架構(gòu)作出了優(yōu)化

參考資源

  • Youtube:https://www.youtube.com/watch?v=hDJQXzajiPg (包括part1-6)笙隙。視頻是最佳的入門(mén)方式?jīng)]有之一洪灯,所以墻裂建議就算不看下文的所有內(nèi)容,都要去看一下part1竟痰。
  • 書(shū)籍:《Unity 3D ShaderLab開(kāi)發(fā)實(shí)戰(zhàn)詳解》
  • Unity各種官方文檔
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末签钩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子坏快,更是在濱河造成了極大的恐慌铅檩,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件假消,死亡現(xiàn)場(chǎng)離奇詭異柠并,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)富拗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)臼予,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人啃沪,你說(shuō)我怎么就攤上這事粘拾。” “怎么了创千?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缰雇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我追驴,道長(zhǎng)械哟,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任殿雪,我火速辦了婚禮暇咆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己爸业,他們只是感情好其骄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著扯旷,像睡著了一般拯爽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钧忽,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天毯炮,我揣著相機(jī)與錄音,去河邊找鬼惰瓜。 笑死否副,一個(gè)胖子當(dāng)著我的面吹牛汉矿,可吹牛的內(nèi)容都是我干的崎坊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼洲拇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奈揍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起赋续,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤男翰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后纽乱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蛾绎,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年鸦列,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了租冠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡薯嗤,死狀恐怖顽爹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骆姐,我是刑警寧澤镜粤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站玻褪,受9級(jí)特大地震影響肉渴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜带射,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一同规、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庸诱,春花似錦捻浦、人聲如沸晤揣。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昧识。三九已至,卻和暖如春盗扒,著一層夾襖步出監(jiān)牢的瞬間跪楞,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工侣灶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甸祭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓褥影,卻偏偏與公主長(zhǎng)得像池户,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凡怎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容