Why Bothers江场?
為什么已經(jīng)有ShaderForge這種可視化Shader編輯器澎办、為什么Asset Store已經(jīng)有那么多炫酷的Shader組件可下載嘲碱,還是有必要學(xué)些Shader的編寫(xiě)金砍?
- 因?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
如上圖,一句話總結(jié):
- GameObject里有MeshRenderer楞泼,
- MeshRenderer里有Material列表驰徊,
- 每個(gè)Material里有且只有一個(gè)Shader;
- 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"
{
// ...
}
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.RenderWithShader
或Camera.SetReplacementShader
配合使用屑宠。Unity內(nèi)置的RenderType包括:-
"Opaque"
:絕大部分不透明的物體都使用這個(gè)厢洞; -
"Transparent"
:絕大部分透明的物體、包括粒子特效都使用這個(gè)典奉; -
"Background"
:天空盒都使用這個(gè)躺翻; -
"Overlay"
:GUI、鏡頭光暈都使用這個(gè)卫玖; - 用戶也可以定義任意自己的
RenderType
這個(gè)標(biāo)簽所取的值公你。 - 應(yīng)注意,
Camera.RenderWithShader
或Camera.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
的接口(比如SetFloat
、SetTexture
編輯) - 之后在Shader程序通過(guò)
[name]
(固定管線)或直接name
(可編程Shader)訪問(wèn)這些屬性拦英。 - 在每一個(gè)Property前面也能類似C#那樣添加Attribute蜒什,以達(dá)到額外UI面板功能。詳見(jiàn)MaterialPropertyDrawer.html疤估。
Shader中的數(shù)據(jù)類型
有3種基本數(shù)值類型:float
灾常、half
和fixed
。
這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
;
-
- GL:
-
vInNDC = vInClip / vInClip.w
后拳恋,得出左手坐標(biāo)系Normalized Device Coordinates中的vInNDC
凡资,其為w=1的Homogenous Cooridniates(故等效于Cartesian Coordinates)vInNDC = float4(xn, yn, zn, 1)
。
xn
和yn
的取值范圍為[-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]
- 如果
- 如果
- GL:
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)有CGPROGRAM
和ENDCG
塊焚辅。
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)
CGPROGRAM
和ENDCG
塊 - 編譯指令
#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)
CGPROGRAM
和ENDCG
塊矮冬。(而不是出現(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
- 在Shader里定義
- 也可以自己定義光照函數(shù)琼牧。比如編譯指令為
- 你定義輸入數(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)注毯侦,截圖如下:
具體可參考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
-
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使用
discard
或clip
疹味、在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各種官方文檔