製作簡易碰撞體線框
來自專欄高品質遊戲開發11 人贊了文章
前言:
之做過鏡頭上的線框,物體上的線框。現在做一個使用GL庫的線框,再做一個用幾何shader做的線框,之前用的是三角形來做。自帶的幾何著色器有,線點面都有。
第一個例子:
先用一個u3d內置的cube,再加入GL演算法。就是每線每線地造出來,其實 又要實時生成材質shader消耗相當的大代碼如下:本來網上是一個顯示碰撞體的例子,我改造了一下
優:可控性強,顯示不錯
缺:消耗大,而且不改線條寬窄。
不說這麼多上代碼:WireFrameDemo.cs
using UnityEngine;
public class WireFrameDemo : MonoBehaviour
{
public Color FrameDemoColor=Color.cyan;
void OnRenderObject()
{
CreateLineMaterial();
lineMaterial.SetPass(0);
GL.PushMatrix();
GL.MultMatrix(transform.localToWorldMatrix); //如果是自身的需要空間本地轉換
Vector3[] vertices = this.GetComponent<MeshFilter>().mesh.vertices;
//畫線
Vector3 p0 = vertices[1], p1 = vertices[0], p2 = vertices[6], p3 = vertices[7];
Vector3 p4 = vertices[3], p5 = vertices[2], p6 = vertices[4], p7 = vertices[5];
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p0);
GL.Vertex(p1);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p1);
GL.Vertex(p2);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p2);
GL.Vertex(p3);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p0);
GL.Vertex(p3);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p4);
GL.Vertex(p5);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p5);
GL.Vertex(p6);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p6);
GL.Vertex(p7);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p4);
GL.Vertex(p7);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p0);
GL.Vertex(p4);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p1);
GL.Vertex(p5);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p2);
GL.Vertex(p6);
GL.End();
GL.Begin(GL.LINES);
GL.Color(FrameDemoColor);
GL.Vertex(p3);
GL.Vertex(p7);
GL.End();
GL.PopMatrix();
}
static Material lineMaterial;
static void CreateLineMaterial()
{
if (!lineMaterial)
{
// Unity3d使用該默認的Shader作為線條材質
Shader shader = Shader.Find("Hidden/Internal-Colored");
lineMaterial = new Material(shader);
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
// 開啟 alpha blending
lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
// 開啟背面遮擋
lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
// Turn off depth writes
lineMaterial.SetInt("_ZWrite", 0);
}
}
}
說完腳本的方式生成,我們可以直接用geo 幾何 shader來做。先上一個三角邊的
先上三角邊的:
// Upgrade NOTE: replaced mul(UNITY_MATRIX_MVP,*) with UnityObjectToClipPos(*)
優:只要一個cube的meshfilter就行,效果不差,運行快
缺:只用4.0以上shader才行,線框大小不可控,不是我們需要四邊形模型
Shader "MyCC/ThickLine2"
{
Properties
{
_Color("Main Color", Color) = (1,1,1,1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
struct v2g
{
float4 vertex : SV_POSITION;
};
struct g2f
{
float4 pos: POSITION;
float4 c: COLOR;
};
fixed4 _Color;
v2g vert (float4 position : POSITION)
{
v2g o;
o.vertex = position;
return o;
}
[maxvertexcount(3)]
void geom(lineadj v2g p[4], inout LineStream<g2f> triStream)
{
//float4x4 vp = UnityObjectToClipPos(_World2Object);
g2f o;
float4 pos0 = UnityObjectToClipPos(p[0].vertex);
float4 pos1 = UnityObjectToClipPos(p[1].vertex);
float4 pos2 = UnityObjectToClipPos(p[2].vertex);
// float4 pos3 = UnityObjectToClipPos(p[3].vertex);
o.pos = pos0;
o.c = half4(1,0,0,1);
triStream.Append(o);
o.pos = pos1;
o.c = half4(0,1,0,1);
triStream.Append(o);
o.pos = pos2;
o.c = half4(1,1,0,1);
triStream.Append(o);
triStream.RestartStrip();
}
fixed4 frag (g2f i) : SV_Target
{
return i.c;
}
ENDCG
}
}
}
最終版:
上面的優缺點都一樣,就是這個不可支持所有模型都是4邊形顯示,因為三角化,模型發生在大的轉變
Shader "MyCC/ThickLine3"
{
Properties
{
_StartColor("Start Color", Color) = (1,0,0,1)
_EndColor("End Color", Color) = (0,1,0,1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
struct v2g
{
float4 vertex : SV_POSITION;
};
struct g2f
{
float4 pos: POSITION;
float4 c: COLOR;
};
fixed4 _StartColor,_EndColor;
v2g vert (float4 position : POSITION)
{
v2g o;
o.vertex = position;
return o;
}
[maxvertexcount(3)]
void geom(lineadj v2g p[4], inout LineStream<g2f> triStream)
{
g2f o;
float4 pos0 = UnityObjectToClipPos(p[0].vertex);
float4 pos1 = UnityObjectToClipPos(p[1].vertex);
float4 pos2 = UnityObjectToClipPos(p[2].vertex);
//下面是我想的三種去除對角線的方法,都可以在u3d上使用
//先說 對角線法 ,
//第一種,求連線長度,不可以大於(根號2=1.414213562373095),用1.4代替
//float dis01 = distance(p[0].vertex,p[1].vertex);
//float dis02 = distance(p[0].vertex,p[2].vertex);
//float dis12 = distance(p[1].vertex,p[2].vertex);
//if(dis01>1.4 && dis12>1.4 || dis02>1.4)
if(p[1].vertex.x==0.5&&p[1].vertex.y==-0.5||p[0].vertex.y==0.5&&p[0].vertex.z==-0.5&&p[1].vertex.x==0.5)//第二種續個排查法,一點排查開,只排查0,其他不排查,難點在"||"
{
o.pos = pos0;
o.c = _StartColor;
triStream.Append(o);
}
//第三種,因為線框向量,如果dot 點乘為0,那麼必定垂直
//有個比較反人類的是,上面的對角線都也是垂直的線,垂直於一個平面上面的線必須也垂直
//float3 dis01 =p[0].vertex-p[1].vertex;
//float3 dis12 = p[1].vertex-p[2].vertex;
//if(dot(dis12,dis01)==0)
//{
// o.pos = pos0;
// o.c = _StartColor;
// triStream.Append(o);
//}
//其他演算法,也可以算.x==.y或者.z,因為.x與.y同時不為0時才能顯示。。。。
o.pos = pos1;
o.c = _EndColor;
triStream.Append(o);
o.pos = pos2;
o.c = _StartColor;
triStream.Append(o);
triStream.RestartStrip();
}
fixed4 frag (g2f i) : SV_Target
{
return i.c;
}
ENDCG
}
}
}
推薦閱讀: