Unity 2D Light (4) - 平行光

image

平行光

2D這裏主要是指有高度的光,即限定了影子的長度。git

Shadow Mesh

限定影子長度生成的Mesh有不少種,能夠根據實際狀況,選擇不一樣的方案。算法

  • 平移Sprite頂點(自動生成的Sprite Vertex 或者 Custome Physics Shape 頂點),而後使用凸包造成新的影子外形。
  • 使用相同的Sprite Renderer渲染影子,在Shader裏進行旋轉拉伸等操做。
  • 其餘:直接複製平移Sprite而後顏色置黑(如浮空物體);加一個單獨的影子貼圖(如一個橢圓的影子貼圖放在物體下面);等等。

平移Sprite頂點

在c#腳本里遍歷Sprite生成Mesh數據。生成一個Mesh繪製影子,只有一個Draw Call。c#

  1. 根據選擇獲取Sprite頂點(默認或Custome Physics Shape)
  2. 按照光方向平移頂點

image
3. 使用凸包算法獲取凸點,保存爲Mesh的數據3d

image
4. 軟陰影能夠在Mesh外面包一個Mesh,使用一張漸變紋理繪製軟陰影。用UV或Tangent等通道保存漸變貼圖的UV,內圍點爲0,外圍點爲1。code

imageimage

  1. 合併全部數據生成一個Shadow Mesh

使用Custome Physics Shape和默認頂點的結果:orm

image

Shader裏進行旋轉拉伸貼圖

遍歷Srpite使用Sprite原來的Sprite Renderer加上陰影的Shader繪製陰影。每一個Sprite都須要一個Draw Call。這樣作主要是由於須要使用Sprite貼圖的Alpha通道繪製影子外形。blog

若是合併使用一個Draw Call繪製,陰影的外形很差控制(使用默認Sprite頂點基本上效果會不好)。可使用cutom outline或者cutom physics shape生成相對細緻的外形,屬於空間換時間的作法。ci

  1. C#腳本中指定Sprite Renderer(主要是須要主貼圖),旋轉所用的根頂點,旋轉弧度,拉伸長度
  2. 在頂點着色器裏進行頂點旋轉拉伸,按任意方向拉伸算法參考Scaling along the cardinal axes
// c#
// 旋轉所用的根頂點,取中間接近底部的頂點
// 兼容旋轉縮放
var matrix = Matrix4x4.TRS(caster.transform.position, caster.transform.rotation, caster.transform.localScale);
var rootPos = (Vector2)(matrix * new Vector4(0f, -0.45f, 0, 1)); // Sprite過大也可使用spriteRenderer.bounds.size.y,實際上手動指定比較好
rootPos = caster.transform.InverseTransformPoint(rootPos); 

// shader
// 二維按任意方向拉伸
float2 scaleByDir(float2 p, float radian, float k){
	float2 dir = float2(cos(radian), sin(radian));
	float2 r1 = float2(1 + (k - 1) * dir.x * dir.x, (k - 1) * dir.x * dir.y);
    float2 r2 = float2((k - 1) * dir.x * dir.y, 1 + (k - 1) * dir.y * dir.y);
    return float2(r1.x * p.x + r1.y * p.y, r2.x * p.x + r2.y * p.y);
}

image

旋轉根部的瑕疵get

image

解決方法能夠是使用一張底部是圓形的Mask貼圖,這樣旋轉的時候就看不出來瑕疵。我以爲是一種比較好的方法,Mask貼圖還可用來作軟陰影(未驗證)。源碼

另外一種是底部uv.y比較小的時候不進行旋轉拉伸。

大於180°時,下面會消失。是由於底部不旋轉,其餘地方旋轉大於180°時致使三角面片反了。大於180°將底部x座標置反便可。

image

可是旋轉角度接近與0°和180°時底部太小處理起來比較麻煩,除了手動加一個圓形陰影想不出來什麼好的處理方法。相對來講仍是使用一個陰影Mask貼圖比較方便。

image

image

陰影覆蓋貼圖處理

陰影會將Sprite自己覆蓋掉。

image

由於覆蓋地方徹底貼合Sprite,因此除了每一個Sprite在渲染陰影后再渲一遍將覆蓋地方去除,目前想不其餘好的方法。

// 將alpha置反,混合時取最小便可
BlendOp Min
Blend One OneMinusDstColor
frag {
    fixed alpha = tex2D(_MainTex, i.uv).a;
	fixed4 col = 1 - alpha;
	col.a = 1;
	return col;
}

image

陰影衰減

簡單點計算頂點與以前旋轉用的點的距離而後使用1.0 - saturate(distance * _ShadowFalloff)求出衰減便可。

image

源碼

link

參考

拉伸矩陣:https://www.mauriciopoppe.com/notes/computer-graphics/transformation-matrices/scale/

相關文章
相關標籤/搜索