UnityDecal 貼花方法總結

2021-08-11 19:36:38 字數 3242 閱讀 2071

unity裡貼花的實現方法比較多,商店裡的外掛程式也是各種各樣,最近正好又在弄這個,趁機會給自己總結下:

1、構造貼片網格

思想很簡單,就是在當前表面構建乙個與表面完全貼合的新mesh,並重新計算紋理貼圖,從而實現貼片效果。

假設 p 為當前表面上的一點, 當前表面在該點的法線為 n, 點 p 為貼片的中心, 貼片的單位切向量為 t

根據給定的點 p , n , t,t 與 n 叉乘可得出的副法線 b, 可以定義表面在點 p 的切平面, 從切平面內可以裁剪出乙個矩形, 再加上與法向量 n 平行的4個邊平面, 可以構造貼片所在區域。 令 w 和 h 為貼片的寬和高, 則4個邊平面的可表示為:

同時,也要進行前後平面的裁剪, 避免那些位於點 p 前後很遠但位於邊平面內的面被裁剪, 前後平面表示為

最後可構建出乙個裁剪立方體

網格構造的執行過程為: 先確定貼片會影響到世界空間中的哪些表面, 可在計算出裁剪立方體後通過 unity 中bounds 類自帶的 intersects 函式得到, 對於得到的平面, 逐個遍歷其中的三角形。 遍歷時, 通過三個點得出三角形的兩條邊的對應向量, 再通過叉乘得出其法線 m, 轉換到同一座標系後可通過 vector3.angle 計算 n 與 m 的夾角, 若夾角大於 90 度, 則表明該三角形為背向貼片, 將其捨棄。 若夾角小於 90 度, 則依次與裁剪立方體的六個面進行比較判斷 , 通過 plane.getside 三角形的三角點是在立方體內。 若整個 mesh 的頂點都在裁剪立方體內, 則將其儲存為貼片的 mesh 頂點, 若沒有任何一點在裁剪立方體內, 則將其捨棄, 主要處理的是既有在立方體內的點,又有不在立方體內部的點的情況。

對於貼片可能影響的物體表面, 其中的三角形被當作凸多邊形處理, 依次用6個邊界平面中的乙個螢幕去裁剪。用螢幕裁剪乙個有n個頂點的多邊形,最多產生乙個有 n + 1 個定點的凸多邊形, 這樣乙個三角形被6個平面裁剪, 最多產生有9個頂點的多邊形:

如圖所示, 截4次,得到7個頂點, 因為 mesh 中的三角形大部分都會共邊,因此每個三角形只裁剪一次。

6個平面裁剪完成後, 最終的凸多邊形被當作三角形扇形 (********_fans) 新增到貼片的三角網格。

可通過射線等方式按照其頂點順序計算出其與立方體表面的乙個交點, 並捨棄立方體外的點, 用交點與內部的點重新構建 mesh, 最後根據新頂點的位置計算其投影後的 uv 座標即可。

2、g-buffer投影

最早接觸到的是 unity 官方 commandbuffer 例子裡的,c#指令碼這邊很簡略,只是在 lightingpass 前 new 了乙個commandbuffer, 核心實現則在 shader 中,vertex 和 fragment shader **如下:

struct v2f

;v2f vert (float3 v : position)

cbuffer_start(unitypercamera2)

// float4x4 _cameratoworld;

cbuffer_end

sampler2d _maintex;

sampler2d_float _cameradepthtexture;

sampler2d _normalscopy;

fixed4 frag(v2f i) : sv_target

其中 ray 是相機相機指向每個頂點的向量 (乘以 float3(-1,-1,1) 是因為經過mv變換後 z 通常是負數)

i.ray = i.ray * (_projectionparams.z / i.ray.z);

則是將向量的終點由頂點(光柵化後應該是畫素,但放這裡好像又不太好表達)位置投影到了相機遠截面,如圖所示:

然後通過深度圖得到畫素投影在其他物體上的座標,並重新轉換至模型座標

clip (float3(0.5,0.5,0.5) - abs(opos.xyz));
因為 demo 中使用的是單位大小的 cube, 因此投影後在 cube 外的畫素直接 clip 掉

clip (dot(wnormal, i.orientation) - 0.3);
則是將投影點的法線與設施好的 decal 朝向做比較,裁剪掉角度太大的避免貼圖拉伸造成的artificial, 如圖所示:

也不能說好還是不好23333

3、forward render 投影

思路基本上跟g-buffer的一樣,既然延遲渲染可以這樣做,forward為什麼不行,同樣有 normaldepthtexture,如法炮製,我們得到這樣的結果:

很明顯,深度圖精度不夠,導致渲染結果慘不忍睹,其原因是在 forward path 下相機渲染 depthnormals texture 時, depth 和 normal 各分配了兩個 8 bits 的通道, 而單獨只渲染 depth 的時候會根據配置給 16 或者 32 bits。因此,我們可以通過 replacement shader 和 command buffer 自己提前渲染好高精度的深度圖,結果就好很多了:

projector就不說啦,適合用於數量多的血跡或者彈孔,之前看到寒霜引擎有篇演講是用 geometry shader 去做彈痕的 decal,但一時半會兒找不到鏈結了,之後再補吧

ThinkPHP的redirect方法總結

首先我們大家都知道,redirect 是起到重定向的作用的乙個函式。我們需要注意在它使用的過程中的一些易錯點。下面是它的兩種使用方法 為了方便我直接從官網中複製過來了 重定向到new模組的category操作 this redirect new category array cate id 2 5,...

javascript Array陣列方法總結

1 陣列方法 1.arr.concat arr1 把arr1拼接早arr後邊 2.arr.indexof 1 判斷某個值是否在陣列中,如果有返回索引號,日過如果沒有返回 1 3.arr.join 用指定的拼接陣列為字串 2 必須記住的四個陣列方法 1.push 最後一位追加。2.pop 刪除最後一位...

Android Studio 檢視總方法數

背景 由於android方法數超過65535就需要分包,需要multidex,有些機器對multidex支援不好導致啟動特別慢需要多花費3 10s時間,看下工程方法數具體是多少 classpath com.getkeepsafe.dexcount dexcount gradle plugin 0.6...