在Unity中用GL库实现一个测量显示空间距离的功能
最后实现的效果如下:
这个功能分三个部分:
1.绘制三角面片;
2.绘制线段;
3.绘制距离文字;
下面细讲
1.获得鼠标点击的三角面片并绘制
这里要求鼠标点击的物体必须有mesh Collider组件,不能是box collider等几何碰撞体组件,才能获得该碰撞体组件的三角面片(查了一下,据说因为Box Collider等碰撞体是基于算法的,没有面的概念。当然速度会比mesh collider更快)
a.通过鼠标点击的RaycastHit获得三角面片的三个顶点
MeshCollider collider = hit.collider as MeshCollider;
if (collider == null || collider.sharedMesh == null)
return;
//获取碰撞器所在物体的Mesh网格
Mesh mesh0 = collider.sharedMesh;
//获取Mesh网格的所有顶点
Vector3[] vertices = mesh0.vertices;
//获取mesh的三角形索引,这里的索引的就是模型顶点数组的下标
int[] triangles = mesh0.triangles;
//然后通过hit.triangleIndex(摄像碰撞到的三角形的第一个点的索引)
//然后+1 ,+2,获取三角形另外两个点的坐标
Vector3 p0 = vertices[triangles[hit.triangleIndex * 3]];
Vector3 p1 = vertices[triangles[hit.triangleIndex * 3 + 1]];
Vector3 p2 = vertices[triangles[hit.triangleIndex * 3 + 2]];
Transform transform = collider.transform;
//上面的三个顶点是Mesh的本地坐标,需要用模型的Transform进行转换到世界坐标
p0 = transform.TransformPoint(p0);
p1 = transform.TransformPoint(p1);
p2 = transform.TransformPoint(p2);
triangleVertices = new Vector3[3];
triangleVertices[0] = p0;
triangleVertices[1] = p1;
triangleVertices[2] = p2;
b.绘制三角形
使用Unity中的GL几何库绘制三角形:
//设置画线的颜色
Color rectColor = Color.green;
Shader shader = Shader.Find("GUI/Text Shader");
//生成画线的材质
Material rectMat = null;
rectMat = new Material(shader);
rectMat.hideFlags = HideFlags.HideAndDontSave;
rectMat.shader.hideFlags = HideFlags.HideAndDontSave;
rectMat.SetPass(0);//设置材质
GL.PushMatrix();//保存摄像机变换矩阵
//绘制三角形,设置颜色和透明度,方框内部透明
GL.Begin(GL.TRIANGLES);
GL.Color(new Color(rectColor.r, rectColor.g, rectColor.b, 0.1f));
GL.Vertex3(triangleVertices[0].x, triangleVertices[0].y, triangleVertices[0].z);
GL.Vertex3(triangleVertices[1].x, triangleVertices[1].y, triangleVertices[1].z);
GL.Vertex3(triangleVertices[2].x, triangleVertices[2].y, triangleVertices[2].z);
GL.End();
//绘制三条边框,设置方框的边框颜色 边框不透明
GL.Begin(GL.LINES);
GL.Color(rectColor);
GL.Vertex3(triangleVertices[0].x, triangleVertices[0].y, triangleVertices[0].z);
GL.Vertex3(triangleVertices[1].x, triangleVertices[1].y, triangleVertices[1].z);
GL.Vertex3(triangleVertices[1].x, triangleVertices[1].y, triangleVertices[1].z);
GL.Vertex3(triangleVertices[2].x, triangleVertices[2].y, triangleVertices[2].z);
GL.Vertex3(triangleVertices[2].x, triangleVertices[2].y, triangleVertices[2].z);
GL.Vertex3(triangleVertices[0].x, triangleVertices[0].y, triangleVertices[0].z);
GL.End();
GL.PopMatrix();//恢复摄像机投影矩阵
2. 绘制射线
获得鼠标射线碰撞的点,标记该点为orin,从该点绘制一条射线,如果碰撞到物体,则标记碰撞点为end;
a.获得射线,如果鼠标点击的物体tag为“machine”则开启绘制三角面片与记录射线
//如果鼠标点击左键
if (Input.GetMouseButton(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.tag == "machine")
{
drawTrangle = true;//如果鼠标左键按下 设置开始画线标志
getVertices(hit);
Ray distanceRay = new Ray(hit.point, Vector3.forward);
//射线碰到了物体,hit为鼠标碰到的物体,hit1是从该物体发出的射线碰撞到的物体
RaycastHit hit1;
if (Physics.Raycast(distanceRay, out hit1))
{
orin = hit.point;
end = hit1.point;
}
}
}
}
b.绘制射线,使用GL绘制
float distance = 0;
distance = (end - orin).magnitude;
distanceStr = "";
if (distance != 0)
{
distanceStr = distance.ToString("f2") + "m";
GL.Begin(GL.LINES);
GL.Color(rectColor);
GL.Vertex3(orin.x, orin.y, orin.z);
GL.Vertex3(end.x, end.y, end.z);
GL.TexCoord3(end.x, end.y, end.z);
GL.End();
}
3. 绘制距离文字
因为上一步绘制射线时已经记录了距离,这里主要在射线中点处绘制射线长度文字。文字在OnGUI函数中绘制
if (distanceStr !="")
{
Vector3 worldPosition = new Vector3(((end + orin) / 2).x,
((end + orin) / 2).y, ((end + orin) / 2).z);
Vector3 position = camera.WorldToScreenPoint(worldPosition);
position = new Vector2(position.x, Screen.height - position.y);
//设置显示颜色
GUI.color = rectColor;
//计算文字的宽高
Vector2 size = GUI.skin.label.CalcSize(new GUIContent(distanceStr));
GUI.Label(new Rect( position.x - (size.x / 2),
position.y - size.y, size.x, size.y), distanceStr);
}
需要注意的的是,要测量的两个物体必须带有碰撞体,要绘制三角面片的物体的碰撞体需有mesh collider组件。而且绘制几何体的函数一般在物体的OnRenderObject 或者相机的OnPostRender里调用,这里用的是OnPostRender,该脚本是挂在相机上的。
项目下载链接:https://download.****.net/download/michaelia_hu/10976196
如有疑问或建议欢迎留言!