在本文中使用的是基於空間的3D模型的描邊,着手於1條邊的2個鄰接面,即退化四邊形進行邊緣檢測和繪製。本文讀者默認爲有圖形學基礎和編寫Shader基礎,若沒有請先去把這些基礎學習一下,再來閱讀本文,不然可能會有閱讀障礙。git
3D模型描邊有兩種方式,一種是基於圖像,即在全部3D模型渲染完成一張圖片後,對這張圖片進行邊緣檢測,最後得出描邊效果。一種是基於空間,即針對3D模型的三角面三個頂點構成的線條作邊緣檢測(注:和基於圖像的邊緣檢測的檢測方式是不一樣的,但都叫邊緣檢測)。在本文中使用的是基於空間的3D模型的描邊。github
空間的3D模型的描邊的邊是有類型的,大體總結有4種:輪廓邊、邊界邊、折縫邊、材質邊。其中前3種是本文認爲卡通渲染所必須有的。算法
圖1 空間中的邊分類 來自文獻[1]編程
輪廓邊的檢測根據定義就是,1條邊的2個鄰接面的法線分別和視線方向的點乘結果再相乘,結果小於0就是輪廓邊。數學上表達就是:(N1·V)*(N2·V) < 0,N1和N2是2個鄰接面的法向量,V是視線方向。學習
邊界邊的檢測比較簡單,若是1條邊只有1個面鄰接,那麼這條邊就是邊界邊。優化
折縫邊的檢測須要使用1個角度參數α,1條邊的2個鄰接面的夾角和這個參數作大小比較。通常在0~180度,但本文選擇在0~90度,並規定小於參數α的就是折縫邊。spa
總結:要在3D模型上實現這3類邊的繪製,只須要知道構成1條邊的2個鄰接面便可完成3類邊的檢測。在學術上有人把這2個鄰接面當作1個點輸入到着色器中,並做爲1條邊做爲輸出,並稱這2個鄰接面爲退化四邊形。.net
圖2 退化四邊形orm
本文使用目前最主流的Uinty3D遊戲引擎實現,因爲這是脫離正常渲染流程的算法,所以若是不能自定義這部分渲染流程,那麼實現起來就無比困難,慶幸的是Unity3D支持自定義渲染流程,其中使用到的關鍵類爲CommandBuffer、ComputeBuffer。blog
因爲不一樣的圖形引擎,對圖形接口的高級用法支持程度並不一致,例如Unity3D的幾何着色器就沒辦法獲取鄰接三角形。所以必須進行預處理。
實現步驟:
步驟1:預處理階段,在這個階段找出模型全部的邊,以及邊所對應的2個鄰接面(默認全部邊都是由2個鄰接面相交構成,不規範的請自行處理)。
步驟2:把預處理階段的數據保存下來,Uinty3D提供了很好接口ScriptableObject 。(注:默認保存退化四邊形4個頂點的索引,若是保存的不是頂點索引,請自行處理)
步驟3:經過CommandBuffer、ComputeBuffer把預處理據輸入到顯卡(GPU)中,每幀都全部退化四邊形進行邊的檢測和繪製。
shader中邊緣檢測步驟:
步驟1:經過退化四邊形4個頂點數據,計算出2個鄰接面的法向量。若是頂點數不足4個則代表該邊只有1個鄰接面,所以它是邊界邊。
步驟2:計算出2個鄰接面的法向量和視線方向的點乘結果dot(N1, V) * dot(N2, V),若是結果爲負則是輪廓邊。
步驟3:pow(dot(N1, N2) / cos(α), 2)在[0, PI/2]上單調遞增, 可避免開方的特色,完成和dot(N1, N1) * dot(N2, N2)比較,若是小於則爲折縫邊。
因爲Uinty3D的幀數實在看不太懂,運算量低的時候幀數基本保持在90幀左右,運算量高一點點的時候就跑到120幀,估計是內部作了優化,所以運行結果僅供參考,並不許確,運行環境I5-4210M,GTX765M。
這個描邊算法在很早就被提出,這種基於空間的描邊極大程度的獲得模型豐富的描邊細節,惋惜在市面上貌似並不爲人所知。某崩公司貌似是使用了這種算法,結合其餘卡通渲染算法,畫面效果驚人,把同行遠遠甩在後面。所以若是想讓本身的做品更有競爭力,開發人員應該靜下心來學習一下這些教科書上沒有的論文想法。本文的後續工做是控制線條的大小,理論上應該是能夠在裁剪空間實現近粗遠細的線條算法,但因爲剛剛踏足這一塊,還有不少其餘工須要去研究,本文到此結束,謝謝。
附:源碼
[1]基於着色器技術實時卡通渲染的的研究
[2]基於GPU實時非真實感渲染的研究與實現
[3]3D日式卡通人物渲染的經驗分享
[4]Unity網格編程篇(二) 很是詳細的Mesh編程入門文章
[5]Unity Shader入門精要
[6]Geometry Shader Input: GL_TRIANGLES_ADJACENCY
[7]Adjacency information in geometry shaders
[8]Direct X 12 – Geometry Shader 幾何着色器
[9]OpenGL 圖元處理
從csdn轉移過來,順便把寫過的文章改寫一下轉過來。原文在這,比較凌亂,再也不建議閱讀。