一.標準光照模型
OpenGL與Direct3D提供了幾乎相同的固定功能光照模型。什么是固定功能光照模型?在過去只有固定繪制流水線的時候,該流水線被限制只能使用一個光照模型,也即是固定功能光照模型。該模型基于phong光照模型。在下面的這個例子里,我們使用一個“基本”模型對固定功能光照模型提供了簡化版本。這個基本模型的數學描述為高級公式為:
surfaceColor = emissive + ambient + diffuse + specular
從式子可以看出:物體表面的顏色是自發光(放射 emissive)、環境反射(ambient)、漫反射(diffuse)和鏡面反射(specular)等光照作用的總和。每種光照作用取決于表面材質性質(例如亮度和材質顏色)和光源的性質(例如光的位置和顏色)。
下面對這個基本模型的各個部分進行講解,最后我們使用CG語言寫出該基本模型。
1.)自發光(emissive)
自發光光照作用獨立于所有的光源。物體的自發光并不能照亮場景中的其他物體。換句話說,物體自發光不能照亮其他物體或者投下陰影。因此,一個放射性物體本身并不是一個光源。
另一個解釋放射項的方法:它是一種在計算完其他所有光照項后添加的顏色。
自發光的數學公式:emissive = Ke
其中Ke代表材質的放射光顏色
2.)環境光(ambient)
環境光來自于四面八方,故環境放射光照項并不依賴于光源的位置。
1.環境放射項依賴:1.材質的反射能力 2.照射到材質上的環境光的顏色。
2.用于環境放射項的數學公式:
ambient = Ka * globalAmbient
其中ka是材質的環境反射系數,globalAmbient是入射環境光的顏色。
ka= tex2D(_MainTex, i.uv.xy).rgb
globalAmbient=UNITY_LIGHTMODEL_AMBIENT.xyz
3.)漫反射項(diffuse)
蘭伯特(Lambert )光照模型是目前最簡單通用的模擬漫反射的光照模型,定義如下:模型表面的明亮度直接取決于光線向量(light vector)和表面法線(normal)兩個向量夾角的余弦值。光線向量是指這個點到光從哪個方向射入,表面法線則定義了這個表面的朝向。
diffuse = kd * lightColor * max ( NL(點積) , 0 )
max ( NL(點積) , 0 )
規范化的向量N和L的點積是兩個向量之間夾角的一個度量,夾角越小,P點受到更多的入射光照。而背向光源的表面將產生負數點積值,因此,公式*max ( NL(點積) , 0 )使得背向光源的表面的漫反射光為0,確保這些表面不會顯示漫反射光照。
_WorldSpaceLightPos0這是一個表示世界做表中的光源的位置矢量或者方向矢量;
如果_WorldSpaceLightPos0.w為0,則表示該光源為平行光,_WorldSpaceLightPos0.xyz為此平行光的方向向量;
反之_WorldSpaceLightPos0.w不為0,則表示光源為點光源,_WorldSpaceLightPos0.xyz為此點光源.
半蘭伯特光照模型
半蘭伯特光照模型,解了決光照無法到達的區域, 背光面更亮一些
diffuse = kd * lightColor * (N * L *0.5 +0.5)
cos的值是-1到1之間,乘上0.5再加0.5后,值域是(0,1)
4.)鏡面反射項(specular)
Phong光照模型:
Ks:材質的鏡面反射顏色
facing的取值為0或1:當N* L大于0時為1,當N *L小于0時為0
R = reflect(-worldLightDir, worldNormal) ;
BlinnPhong光照模型
BlinnPhong光照模型混合和了Lambert的漫反射和標準的高光,渲染有時比Phong高光更柔和、更平滑,此外它的處理速度相當快,因此成為許多CG軟件中默認的光照渲染方法。
specular = KslightColor pow(( dot(N,H), shininess )
其中H = (L + V) / | L+V |,實際就是normalize(L + V) , 計算H比計算反射向量R更快速。
4.)UnityCG.cginc常用內置函數
float3 WorldSpaceViewDir(float4 v) 輸入一個模型空間中的頂點位置,返回世界空間中從該點到攝像機的觀察方向。
float3 UnityWorldSpaceViewDir(float4 v) 輸入一個世界空間中的頂點位置,返回世界空間中從該點到攝像機的觀察方向。
float3 ObjSpaceViewDir(float4 v) 輸入一個模型空間中的頂點位置,返回模型空間中從該點到攝像機的觀察方向。
float3 WorldSpaceLightDir(float4 v) 僅可用于前向渲染中。輸入一個模型空間中的頂點位置,返回世界空間中從該點到光源的光照方向。
float3 UnityWorldSpaceLightDir(float4 v) 僅可用于前向渲染中。輸入一個世界空間中的頂點位置,返回世界空間中從該點到光源的光照方向。
float3 ObjSpaceLightDir(float4 v) 僅可用于前向渲染中。輸入一個模型空間中的頂點位置,返回模型空間中從該點到光源的光照方向。沒有被歸一化。
float3 UnityObjectToWorldNormal(float3 norm) 把法線方向從模型空間轉換到世界空間中。
float3 UnityObjectToWorldDir(flaot3 dir) 把方向矢量從模型空間變換到世界空間中。
float3 UnityWorldToObjectDir(float dir) 把方向矢量從世界空間變換到模型空間中。
二.法線貼圖
逐像素計算光照時,我們每一個像素都會根據該點的法向量來計算最終該點的光照結果,那么,我們如果能夠改變這個法線的方向,不是就可以改變這個點的光照結果了呢!那么,把紋理采樣的思想用在這里,我們直接用一張圖來存儲法線(或者法線偏移值,見下文),逐像素計算時,在采樣diffuse貼圖的時候,再采樣一張法線的貼圖,就可以修改法線了,進而修改最終的效果。
什么是切線空間?
切線空間就是,基于模型上的一個頂點建立的坐標空間,它的X軸是這個頂點在模型中的切線分量,Z軸是改點在模型上的法線,Y軸就是這個頂點的副切線,因為坐標空間XYZ三個面都是互相垂直的嘛,所以這個副切線我們是可以求出來的(以為同樣是與一個平面垂直的單位向量是有兩方向的,在Unity中模型會提供一個副切線的方向的數據)。
來上一個圖,讓我們更好的理解這個所謂的切線空間。
我們的表面法線是單位向量, 位于范圍 -1.0 到 1.0 之間. 而RGB的值域是(0,1) , 我們可以通過把法線范圍轉換為 0.0 到 1.0之間來把法線向量(x, y, z)存儲到一個 RGB 紋理貼圖中.
Color.rgb = Normal.xyz / 2.0 + 0.5;
現在我們可以分析為什么tangent space法線貼圖是偏藍色了.因為這個面渲染時計算機認為這個面的"彎曲"程度很小,即面上各個點插值得來的法線相互間偏差很小.基本跟整個面的垂直方向不會差太多.因此在tangent space里,這些法線都跟z軸偏差較小.而z軸是被保存在貼圖里的b字節處(藍色通道)里.所以貼圖顯示出來的顏色就偏藍了.
http://blog.csdn.net/candycat1992/article/details/41605257
三.(渲染路徑)Rendering Paths
1.Deferred Lighting,延遲光照路徑。3者中最高質量地還原光照陰影。光照性能只與最終像素數目有關,光源數量再多都不會影響性能。
2.Forward Rendering,前向渲染路徑。能發揮出Shader全部特性的渲染路徑,當然也就支持像素級光照。最常用、功能最自由,性能與光源數目*受光照物體數目有關,具體性能視乎其具體使用到的Shader的復雜度。
3.Vertex Lit,頂點光照路徑。頂點級光照。性能最高、兼容性最強、支持特性最少、品質最差。
1.)前向渲染
根據影響物體的光源的不同,正向渲染路徑用單個或多個通道來渲染物體。在正向渲染中,光源本身也會根據他們的設置和強度受到不同的對待。
在正向渲染中,影響物體的最亮的幾個光源使用逐像素光照模式。接下來,最多有4個點光源會以逐頂點渲染的方式被計算。其他光源將以球面調和(Spherical Harmonics)的方式進行計算,球面調和技術計算很快但只能得到近似值。根據以下的規則判斷一個光源是否為逐像素光源:
?渲染模式被設置為不重要(Not Important)的光源以逐頂點或球面調和的方式進行計算
?最亮的方向光源為像素光源
?渲染模式被設置重要(Important)的光源為像素光源
?如根據以上規則得到的像素光源數量小于質量設置中的像素光源數量(Pixel Light Count),為了減少亮度,會有更多的光源以逐像素的方式進行渲染
Base Pass 基本通道
Base pass renders object with one per-pixel directional light and all SH/vertex lights. This pass also adds any lightmaps, ambient and emissive lighting from the shader. Directional light rendered in this pass can have Shadows. Note that Lightmapped objects do not get illumination from SH lights.
Note that when “OnlyDirectional” pass flag is used in the shader, then the forward base pass only renders main directional light, ambient/lightprobe and lightmaps (SH and vertex lights are not included into pass data).
需要在設置Pass的 Tags { "LightMode"="ForwardBase" }
定義宏#pragma multi_compile_fwdbase
Additional Passes附加通道
Additional passes are rendered for each additional per-pixel light that affect this object. Lights in these passes by default do not have shadows (so in result, Forward Rendering supports one directional light with shadows), unless multi_compile_fwdadd_fullshadows variant shortcut is used.
需要在設置Pass的 Tags { "LightMode"="ForwardAdd" }
定義宏#pragma multi_compile_fwdadd
https://docs.unity3d.com/Manual/RenderTech-ForwardRendering.html
四.投射陰影和接收陰影
1.)投射陰影
需要有平行光,且平行光開啟陰影
物體的renderer組件需要開啟Cast Shadow
物體的shader需要一個tags為”ShadowCaster”的pass來讓物體加入陰影貼圖的渲染內.可用FallBack "Specular"來讓Unity自動查詢可用的shader,注意透明物體需要特別指定.另外,如果有使用頂點動畫,則需要自己定義可用的shader
2.)接收陰影
Unity中,對于衰減,主要是通過一張光照衰減紋理(_LightTexture0)的采樣得到,其采樣操作主要是在光照空間中進行。對于陰影,一般不會采用實時陰影的方式(性能耗費太高),但是現在隨著技術的發展,越來越多的游戲采用的實時光照,當然這是對整體的光照的優化有一個極其高的情況下。一般情況下都是將場景烘培,對模型自身添加相應的光照設置。
在unity中,主要是通過一個shadowCaster的shader來實現對陰影的計算,對于物體是否投射陰影可以在其自身的組件上設置。而unity將陰影和衰減的計算合并在一起,主要通過三個基本的內部操作來實現:在a2v的結構體中設置SHADOW_COORDS(n),在vert shader中進行TRANSFER_SHADOW(o),最后在frag shader中進行 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos),此時得到的atten就是綜合了衰減和陰影的結果,可以用其來作為衰減和陰影的因子與最終的光照顏色相乘。