這篇文章是繼這篇文章的序章,更進一步學習及了解投影。
寫作思路:
1、簡述投影原理(包括剖析Unity的陰影投射原理)4張陰影貼圖、陰影貼圖采集
2、介紹什麼是bias、normalBias,以及為什麼會産生 投影瑕疵
3、引入兩個Unity的兩個對應bias和normalBias的shader内置函數
4、bias、normalBias在Shader中的簡單應用
1、簡述Unity陰影投射原理
正如上篇所說:
點A在相機的深度值設為 ZA ,點A在陰影紋理的深度值為ZB ,如果ZA >ZB ,則該點處于相機的可見範圍并且處于陰影之中,即該點看得到陰影
那Unity是如何獲得ZA 和ZB的呢?
在Unity中,ZB 其實是記錄在光照深度圖(陰影貼圖)中的; ZA 就是我們相機中看到的對象的深度! 先記住上面說的這兩個東西!
1、首先我們随機建立一個場景,裡面放置一些簡單的模型對象,并且放置一束平行光,如下圖
2、接下來打開Unity的Frame Debug并啟用,可以看到渲染的順序清單中先渲染了模型相關的深度圖,點選如下清單的位置,可以看到Game視窗出現第二幅圖,這就是模型相關的深度圖(也就是上圖 3球1方塊的深度圖)
3、選中下面第一張圖,可以看到Game視窗渲染了4張深度圖,這四張深度圖就是光照深度圖(陰影貼圖)
因為我們在Project Settings中設定了如下設定,最關鍵的就是 “Shadow Casades”我們設定了四個變量(具體内部怎麼實作不曉得,但是會根據這個數量增加光照深度圖的渲染數量!不重要!不影響我們了解),然後大多數情況下是看不到四張圖的,需要調整一下第二個紅框中的比例
4、Unity渲染原理描述: 在上述幾個步驟中,我們獲得了模型深度圖和光照深度圖,其中模型深度圖存儲的是 相機範圍内模型各個點的深度數值,光照深度圖存儲的是從場景平行光角度擷取的 所能照射到的各點的深度值(其實就是相當于從平行光的方向放置一個相機,相機螢幕所能照到的點各點的深度值),然後現在就回到了我們最上面的A,B兩點的問題, 如果模型深度圖裡的點的深度值 >光照深度圖的值,則該點就處于陰影之中,則該點顔色為黑
Unity增加陰影精準度的主要方式有以下幾種:
①增加光照深度圖數量:對應上面的shadow Cascades,即從不同的位置渲染多張光照深度圖,然後每次都與模型深度圖進行比對,以此增加精準度。但是每增加一張深度圖,就要把整個場景多渲染一遍,是以性能消耗也會逐漸增加。我們把shading Mode改成 Shadow Cascades 就能看到四張光照貼圖渲染的不同區域
②增加光照深度圖的分辨率:對應上面的Shadow Resolution,我們渲染光照深度圖的時候,實際上是給螢幕一個分辨率比如(1920*1080),然後Unity按照上面每個像素點一一擷取每個點的深度,是以增加分辨率就能增加精準度,同樣的性能消耗會增加
2、關于光照設定裡面的Bias、NormalBias
1、選中平行光,找到如下截屏的陰影設定,可以看到有Bias、NormalBias這兩個選項,我們通過調整這兩個屬性可以改變投影的“偏移”,這兩個屬性分别就是對應着UnityShader内置庫中的 “UnityApplyLinearShadowBias” 和 “UnityClipSpaceShadowCasterPos”這兩個函數
2、我們将設定裡面的投影資料為以下圖檔,并且把Bias和NormalBias都調整為“0”
我們會看到場景内的球體表面産生了一些投影,如下圖
我們再次調整投影設定,把Shadow Resolution調整為 High,可以看到,球體表面的投影并沒有消失,他隻是變得更密集了
上面這種情況我們稱之為“投影粉刺”(shadow acne)”,此時,我們隻要調整Bias和Normal的值為恰當值,這些表面投影就會消失
3、關于“投影粉刺”(shadow acne)的産生原因
我們在計算光照深度圖的時候,計算的是各個像素點所處螢幕位置的可照射深度值,這就可能會出現一種情況,我們計算得到的該點的像素點内包含多個三角片元,由于是處在同個像素點的,他們的光照深度圖中的深度是一緻的,但實際上片元的深度值是不同的,舉個栗子
PS這篇簡易草在投影中也有提到陰影粉刺的事
下面借用一下這個部落格老哥的圖
我們假設存在兩個片元A和B都處在像素點P上,像素P的深度值為0.5,A、B分别為0.49和0.51,那麼此時B就處在陰影當中,而A就是處在陰影外,但實際上B點應該也是處在陰影外的,是以我們利用Bias将A、B兩點的深度值都下移,這樣兩點都是處于陰影之外
4、下面是Bias和NormalBias在Shader中的簡單應用,
#if !defined(MY_SHADOWS_INCLUDED)
#define MY_SHADOWS_INCLUDED
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
//float3 normal : NORMAL;
};
float4 MyShadowVertexProgram(appdata v):SV_POSITION
{
float4 pos = UnityObjectToClipPos(v.vertex);
// 下面是normalBias
// float4 pos = UnityClipSpaceShadowCasterPos(v.vertex,v.normal);
return UnityApplyLinearShadowBias(pos);
}
half4 MyShadowFragmentProgram():SV_TARGET
{
return 0;
}
#endif
Shader "Unlit/ShadowLearn"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
Tags { "RenderType"="Opaque" }
LOD 100
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
Pass
{
Tags
{
"LightMode" = "ShadowCaster"
}
CGPROGRAM
#include "MyShadows.cginc"
#pragma vertex MyShadowVertexProgram
#pragma fragment MyShadowFragmentProgram
ENDCG
}
}
}
下一篇會利用上述陰影投射的原理,不使用Unity自帶的陰影計算庫,實作陰影投射
人有多少個一年,每個禮拜一晃眼就過去了,一年也就52個禮拜,别讓自己太放松了