Unity在Game視口中繪制模型網(wǎng)格(GL、Shader)

前言:
????????本篇主要講解如何在Game窗口顯示模型網(wǎng)格——類(lèi)似Scene視口Wireframe的效果。在運(yùn)行與非運(yùn)行狀態(tài)下通過(guò)GL接口進(jìn)行繪制。另一種使用Shader幾何著色器繪制的方式。
????????文章內(nèi)容主旨在于分享一些Unity 3D的小技巧,具體的實(shí)用性還請(qǐng)各位看官根據(jù)實(shí)際情形自行判斷咯。
????????好了話不多說(shuō),發(fā)車(chē)咯!
正文:
????????來(lái)看一下最終的效果。



??????
??因?yàn)槟莻€(gè)黑色的網(wǎng)格線在桌子模型上實(shí)在不起眼,我給修改成了紅色的,修改顏色的時(shí)候有一個(gè)需要注意的地方,材質(zhì)球選擇的Shader需要調(diào)整,默認(rèn)的Shader是不行的,只能繪制出黑色的線。
????????網(wǎng)格線繪制的原理很簡(jiǎn)單,我們都知道直線是由兩個(gè)點(diǎn)連接形成的,三角面是由三條首尾相連的線段組成的,而N多個(gè)三角面按照一定順序排列,就會(huì)形式模型的網(wǎng)格。所以,我們只要拿到模型所有的頂點(diǎn)與這些頂點(diǎn)相連的順序。理論上就已經(jīng)可以繪制策劃來(lái)了。
????????說(shuō)到這,我們就不得不稍微提一下模型的Mesh中有哪些東西。(已經(jīng)了解的看官可以跳過(guò)灰色文段)
????????參考文章“https://blog.csdn.net/renwen1579/article/details/125056491”
??????從官方手冊(cè)中可以知道,在Mesh中存儲(chǔ)著三維模型的數(shù)據(jù):
? ? ? ? vertices(頂點(diǎn)數(shù)據(jù)數(shù)組Vector3[])、? triangles(三角形頂點(diǎn)索引數(shù)組,int[])、?normals(法線向量數(shù)組,Vector3[])、?uv(紋理坐標(biāo)數(shù)組,Vector2[])。我們需要的是vertices和triangles,有了這兩個(gè)數(shù)據(jù),我們就可以將網(wǎng)格線繪制出來(lái),至于normals和uv則是確定三角面的朝向確定正面和反面用的,uv是紋理采樣時(shí)用的這個(gè)地方暫時(shí)用不到。
????????知道m(xù)esh中包含的內(nèi)容,我們拿到了vertices頂點(diǎn)數(shù)據(jù)和triangles三角面數(shù)據(jù),就可以進(jìn)行繪制了。
????????過(guò)程簡(jiǎn)述:使用GL根據(jù)坐標(biāo)點(diǎn)兩兩一對(duì)進(jìn)行連線,就可以繪制出想要的結(jié)果。
????????GL繪制需要一個(gè)材質(zhì)球,這個(gè)可以提前準(zhǔn)備好也可以代碼生成一個(gè),但是這個(gè)材質(zhì)球采用的Shader建議更換成Unlit下的,因?yàn)槟J(rèn)的“Standard”無(wú)法改變顏色,始終是黑色,暫時(shí)沒(méi)搞清楚原因,有知道的看官可以留言告訴一下我。
????????下面直接看代碼,我會(huì)對(duì)代碼做出詳細(xì)的注釋。
????????對(duì)于GL部分代碼參考自:GL參考
????????如果截圖看不清下方有文字。

//目標(biāo)模型
? ? public GameObject TargetModel;
? ? //材質(zhì)球
? ? public Material mat;
? ? //緩存頂點(diǎn)
? ? Vector3[] vertices;
? ? //緩存三角面的頂點(diǎn)索引
? ? int[] triangles;
? ? private void Start()
? ? {
? ? ? ? /*
? ? ? ? ?* 實(shí)際情況下很多模型都是有許多subMesh組成的
? ? ? ? ?* 這個(gè)地方只考慮有一個(gè)mesh的情況,多個(gè)mesh情況類(lèi)似可以復(fù)用實(shí)現(xiàn)
? ? ? ? ?*/
? ? ? ? //獲取所有頂點(diǎn)
? ? ? ? vertices= TargetModel.GetComponent<MeshFilter>().mesh.vertices;
? ? ? ? //獲取三角面索引
? ? ? ? triangles=TargetModel.GetComponent<MeshFilter>().mesh.GetTriangles(0);
? ? }
? ? //這個(gè)函數(shù)必須掛在相機(jī)上才起作用
? ? //如果不確定是否掛在相機(jī)上,請(qǐng)采用OnRenderObject.
? ? //具體功能請(qǐng)可以自行查閱,不做過(guò)多解釋
? ? private void OnPostRender()
? ? {
? ? ? ? //壓棧 保存攝像機(jī)變換矩陣
? ? ? ? GL.PushMatrix();
? ? ? ? //設(shè)置材質(zhì)通道
? ? ? ? mat.SetPass(0);
? ? ? ? //設(shè)置繪制樣式,GL.LINES就是線,兩兩一對(duì)
? ? ? ? GL.Begin(GL.LINES);
? ? ? ? //這只線的顏色
? ? ? ? GL.Color(Color.red);
? ? ? ? //提供一個(gè)矩陣,作用是將模型頂點(diǎn)的自身坐標(biāo)變成世界坐標(biāo)
? ? ? ? //矩陣相乘,過(guò)程會(huì)有些復(fù)雜建議自行查閱資料
? ? ? ? GL.MultMatrix(TargetModel.transform.localToWorldMatrix);
? ? ? ? //每次循環(huán)塞入一個(gè)三角面,線段需要兩兩一組
? ? ? ? for (int i = 0; i < triangles.Length - 2; i += 3)
? ? ? ? {
? ? ? ? ? ? GL.Vertex(vertices[triangles[i]]); GL.Vertex(vertices[triangles[i + 1]]);
? ? ? ? ? ? GL.Vertex(vertices[triangles[i + 1]]); GL.Vertex(vertices[triangles[i + 2]]);
? ? ? ? ? ? GL.Vertex(vertices[triangles[i + 2]]); GL.Vertex(vertices[triangles[i]]);
? ? ? ? }
? ? ? ? GL.End();
? ? ? ? //出棧 恢復(fù)攝像機(jī)投影矩陣
? ? ? ? GL.PopMatrix();
? ? }
????????

????????到此我們通過(guò)GL繪制網(wǎng)格線就已經(jīng)完成。
????? ? 這里說(shuō)一下這種方式的限制,通過(guò)這種方式繪制的線段其實(shí)是通過(guò)相機(jī)渲染到屏幕上的,并不會(huì)真實(shí)地在模型上顯示,但是深度的遮擋關(guān)系是正確的。

????????? ?使用“OnRenderObject”運(yùn)行時(shí),如果報(bào)錯(cuò)“Matrix stack full depth reached”可以嘗試加入?GL.LoadPixelMatrix();

????????添加[ExecuteAlways]可以在非運(yùn)行狀態(tài)下顯示效果
另外一種方式,那就是使用利用Shade的幾何著色器。參考“https://www.freesion.com/article/6019262209/”,稍微修改并追加了一個(gè)Pass渲染紋理。
這種方式不建議對(duì)Shader不太熟悉的看官使用,因?yàn)楫?dāng)前這個(gè)Shader只是為了展示效果,存在一些問(wèn)題,例如不支持光照等等。當(dāng)然展示效果是肯定沒(méi)有問(wèn)題的


???????
Shader "Unlit/line"
{
? ? Properties
? ? {
? ? ? ? _MainTex ("Texture", 2D) = "white" {}
? ? ? ? _LineColor("Color",COLOR) = (1,1,1,1)
? ? }
? ? SubShader
? ? {
? ? ? ? //第一部分 ?順序不能搞錯(cuò)了,否則會(huì)被覆蓋的
? ? ? ? ?Pass
? ? ? ? {
? ? ? ? ? ? CGPROGRAM
? ? ? ? ? ? #pragma vertex vert
? ? ? ? ? ? #pragma fragment frag
? ? ? ? ? ? #include "UnityCG.cginc"
? ? ? ? ? ? struct appdata
? ? ? ? ? ? {
? ? ? ? ? ? ? ? float4 vertex : POSITION;
? ? ? ? ? ? ? ? float2 uv : TEXCOORD0;
? ? ? ? ? ? };
? ? ? ? ? ? struct v2f
? ? ? ? ? ? {
? ? ? ? ? ? ? ? float2 uv : TEXCOORD0;
? ? ? ? ? ? ? ? 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);
? ? ? ? ? ? ? ? return o;
? ? ? ? ? ? }
? ? ? ? ? ? fixed4 frag (v2f i) : SV_Target
? ? ? ? ? ? {
? ? ? ? ? ? ? ? // sample the texture
? ? ? ? ? ? ? ? fixed4 col = tex2D(_MainTex, i.uv);
? ? ? ? ? ? ? ? return col;
? ? ? ? ? ? }
? ? ? ? ? ? ENDCG
? ? ? ? }
? ? ? ? //第二部分 ?順序不能搞錯(cuò)了,否則會(huì)被覆蓋的
? ? ? ? Pass
? ? ? ? {
? ? ? ? ? ? Tags { "RenderType" = "Opaque" }
? ? ? ? ? ? CGPROGRAM
? ? ? ? ? ? #pragma vertex vert
? ? ? ? ? ? #pragma fragment frag
? ? ? ? ? ? //幾何著色器
? ? ? ? ? ? #pragma geometry geo
? ? ? ? ? ? #include "UnityCG.cginc"
? ? ? ? ? ?
? ? ? ? ? ? struct v2g
? ? ? ? ? ? {
? ? ? ? ? ? ? ? float4 ?pos ? ? ? : POSITION;
? ? ? ? ? ? };
? ? ? ? ? ? struct g2f
? ? ? ? ? ? {
? ? ? ? ? ? ? ? float4 ?pos ? ? ? : POSITION;
? ? ? ? ? ? };
? ? ? ? ? ? fixed4 _LineColor;
? ? ? ? ? ? v2g vert(appdata_base v)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? v2g o;
? ? ? ? ? ? ? ? o.pos = UnityObjectToClipPos(v.vertex);
? ? ? ? ? ? ? ? return o;
? ? ? ? ? ? }
? ? ? ? ? ? [maxvertexcount(3)]
? ? ? ? ? ? void geo(triangle v2g vg[3], inout LineStream<g2f> ls)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int i = 0; i < 3; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? g2f g2f_1;
? ? ? ? ? ? ? ? ? ? g2f_1.pos = vg[i].pos;
? ? ? ? ? ? ? ? ? ? ls.Append(g2f_1);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ls.RestartStrip();
? ? ? ? ? ? }
? ? ? ? ? ? fixed4 frag(g2f input) : COLOR
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return _LineColor;
? ? ? ? ? ? }
? ? ? ? ? ? ENDCG
? ? ? ? }
? ? }
}
?這種方式繪制的網(wǎng)格線是真正顯示在模型上的。這里線的顏色用的默認(rèn)白色,建議修改其他顏色以免和純白色的模型區(qū)分不明顯。

好的本期的內(nèi)容就到這個(gè)這里。
如果本文對(duì)您有一點(diǎn)點(diǎn)幫助,那俺這一番功夫就沒(méi)白費(fèi)
如果喜歡請(qǐng)點(diǎn)贊支持哦