Unity3D Mesh小课堂(三)圆形与圆环
来源:互联网 发布:win8 windows update 编辑:程序博客网 时间:2024/04/30 18:56
之前我们介绍了如何绘制一个三角形的Mesh。今天讲解一下如何绘制一个圆形的Mesh。
Unity3D中Mesh的基本单位是三角形,而圆形就是由许许多多的三角形组成的。那么我们就知道了绘制圆形的Mesh需要两个变量
public float Radius = 5;//半径public int Segments = 50;//分割数
先加上基本的代码
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]public class TestCircle : MonoBehaviour {public float Radius = 5;//半径public int Segments = 50;//分割数private MeshFilter meshFilter;void Awake(){meshFilter = GetComponent<MeshFilter>();meshFilter.mesh = CreateMesh(Radius, Segments);}Mesh CreateMesh(float radius, int segments){Mesh mesh = new Mesh ();return mesh;}}
那么CreateMesh这个方法要怎么实现呢?
首先是vertices(顶点)
int vlen = 1 + segments;Vector3[] vertices = new Vector3[vlen];vertices[0] = Vector3.zero;float angleDegree = 360;float angle = Mathf.Deg2Rad * angleDegree;float currAngle = angle / 2;float deltaAngle = angle / segments;for (int i = 1; i < vlen; i++){float cosA = Mathf.Cos(currAngle);float sinA = Mathf.Sin(currAngle);vertices[i] = new Vector3 (cosA * radius, 0, sinA * radius);currAngle -= deltaAngle;}
vertices[0]保存圆心坐标,后面的segments个Vector3保存圆(边)上的点。
接着是triangles(三角形),里面实际保存的是vertices的下标
int tlen = segments * 3;int[] triangles = new int[tlen];for (int i = 0, vi = 1; i < tlen - 3; i += 3, vi++){triangles[i] = 0;triangles[i + 1] = vi;triangles[i + 2] = vi + 1;}triangles [tlen - 3] = 0;triangles [tlen - 2] = vlen - 1;triangles [tlen - 1] = 1;
为了完成闭环,将最后一个三角形单独拎出来。
需要注意的是:三角形必须是顺时针取顶点。
最后是uv(纹理坐标),不太理解是什么的,可以先看一下之前的博文:为三角形添加纹理
Vector2[] uvs = new Vector2[vlen];for (int i = 0; i < vlen; i++){uvs [i] = new Vector2 (vertices[i].x / radius / 2 + 0.5f, vertices[i].z / radius / 2 + 0.5f);}
最后把属性赋给mesh就可以了
mesh.vertices = vertices;mesh.triangles = triangles;mesh.uv = uvs;
我们测试一下,导入一张图片
我特意找了一张圆外不透明的图片。
新建一个material,shader使用Unlit/Texture,选择这张图片作为纹理。
新建一个GameObject,添加TestCircel作为组件,讲MeshRenderer的Material设置为新建的material。
点击运行,就可以看到圆了。
接下来稍微提升一下难度:假如我需要一个圆环Mesh怎么办?
圆形是由segments个等腰三角形组成的,圆环就是有segments个等腰梯形组成的。
那么等腰梯形由什么组成?两个三角形(多个也是可以的)。
首先添加一个变量,内圆半径
public float InnerRadius = 3;//内圆半径
CreateMesh方法也相应的增加了一个参数。
vertices(顶点)
int vlen = segments * 2 + 2;Vector3[] vertices = new Vector3[vlen];float angleDegree = 360;float angle = Mathf.Deg2Rad * angleDegree;float currAngle = angle / 2;float deltaAngle = angle / segments;for (int i = 0; i < vlen; i+=2){float cosA = Mathf.Cos(currAngle);float sinA = Mathf.Sin(currAngle);vertices[i] = new Vector3 (cosA * innerRadius, 0, sinA * innerRadius);vertices[i + 1] = new Vector3 (cosA * radius, 0, sinA * radius);currAngle -= deltaAngle;}
每一个步长(2)保存梯形(非平行边)的两个顶点的坐标。我们为180度和-180度分别创建了两个值相等的点,是为了方便triangles的计算。
triangles(三角形)
int tlen = segments * 6;int[] triangles = new int[tlen];for (int i = 0, vi = 0; i < tlen; i += 6, vi+=2){triangles[i] = vi;triangles[i + 1] = vi + 1;triangles[i + 2] = vi + 3;triangles[i + 3] = vi + 3;triangles[i + 4] = vi + 2;triangles[i + 5] = vi;}
把每个分割梯形再分割成两个三角形。仍然需要注意的是:三角形必须是顺时针取顶点。
uv(纹理坐标)代码不变
然后我们就得到了一个被打了马赛克的圆(误)
假如我们需要一个扇形/扇面怎么办?其实我已经预留好了修改的余地,把顶点代码片段里的
float angleDegree = 360;去掉,作为参数输入即可。InnerRadius=0就是扇形,InnerRadius>0就是扇面。
加上Gizmos,调整一下代码
using UnityEngine;using System.Collections;[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]public class TestCircle : MonoBehaviour {public float Radius = 5;//半径public int Segments = 50;//分割数public float InnerRadius = 3;//内圆半径public float AngleDegree = 90;private CircleMeshCreator _creator = new CircleMeshCreator();private MeshFilter _meshFilter;void Awake(){_meshFilter = GetComponent<MeshFilter>();}void Update(){_meshFilter.mesh = _creator.CreateMesh(Radius, Segments, InnerRadius, AngleDegree);}void OnDrawGizmos(){Gizmos.color = Color.gray;DrawMesh();}void OnDrawGizmosSelected(){Gizmos.color = Color.green;DrawMesh();}private void DrawMesh(){Mesh mesh = _creator.CreateMesh(Radius, Segments, InnerRadius, AngleDegree);int[] tris = mesh.triangles;for (int i = 0; i < tris.Length; i+=3){Gizmos.DrawLine(TransformToWorld(mesh.vertices[tris[i]]), TransformToWorld(mesh.vertices[tris[i + 1]]));Gizmos.DrawLine(TransformToWorld(mesh.vertices[tris[i]]), TransformToWorld(mesh.vertices[tris[i + 2]]));Gizmos.DrawLine(TransformToWorld(mesh.vertices[tris[i+1]]), TransformToWorld(mesh.vertices[tris[i + 2]]));}}private Vector3 TransformToWorld(Vector3 src){return transform.TransformPoint(src);}private class CircleMeshCreator{private static readonly int PRECISION = 1000;private float _radius;private int _segments;private float _innerRadius;private float _angleDegree;private Mesh _cacheMesh ;public Mesh CreateMesh(float radius, int segments, float innerRadius, float angleDegree){if (checkDiff(radius, segments, innerRadius, angleDegree)){Mesh newMesh = Create(radius, segments, innerRadius, angleDegree);if (newMesh != null){_cacheMesh = newMesh;this._radius = radius;this._segments = segments;this._innerRadius = innerRadius;this._angleDegree = angleDegree;}}return _cacheMesh;}private Mesh Create(float radius, int segments, float innerRadius, float angleDegree){if (segments <= 0){segments = 1;#if UNITY_EDITORDebug.Log("segments must be larger than zero.");#endif}Mesh mesh = new Mesh();int vlen = segments * 2 + 2;Vector3[] vertices = new Vector3[vlen];float angle = Mathf.Deg2Rad * angleDegree;float currAngle = angle / 2;float deltaAngle = angle / segments;for (int i = 0; i < vlen; i+=2){float cosA = Mathf.Cos(currAngle);float sinA = Mathf.Sin(currAngle);vertices[i] = new Vector3 (cosA * innerRadius, 0, sinA * innerRadius);vertices[i + 1] = new Vector3 (cosA * radius, 0, sinA * radius);currAngle -= deltaAngle;}int tlen = segments * 6;int[] triangles = new int[tlen];for (int i = 0, vi = 0; i < tlen; i += 6, vi+=2){triangles[i] = vi;triangles[i + 1] = vi + 1;triangles[i + 2] = vi + 3;triangles[i + 3] = vi + 3;triangles[i + 4] = vi + 2;triangles[i + 5] = vi;}Vector2[] uvs = new Vector2[vlen];for (int i = 0; i < vlen; i++){uvs [i] = new Vector2 (vertices[i].x / radius / 2 + 0.5f, vertices[i].z / radius / 2 + 0.5f);}mesh.vertices = vertices;mesh.triangles = triangles;mesh.uv = uvs;return mesh;}private bool checkDiff(float radius, int segments, float innerRadius, float angleDegree){return segments != this._segments || (int)((angleDegree - this._angleDegree) * PRECISION) != 0 ||(int)((radius - this._radius) * PRECISION) != 0 || (int)((innerRadius - this._innerRadius) * PRECISION) != 0;}}}
恩,改成这样,应该命名为TestSector更为合适一点。
最后附上源码下载链接
- Unity3D Mesh小课堂(三)圆形与圆环
- Unity3D Mesh小课堂(一)三角形
- Unity3D Mesh小课堂(二)为三角形添加纹理
- Unity3D Mesh小课堂(五)CombineMeshes合并网格
- Unity3D Mesh小课堂(五)CombineMeshes合并网格
- Unity3D Mesh小课堂(四)MeshRenderer的material和sharedMaterial
- [Unity3d]自制圆形小地图
- 【Unity3D】Mesh
- Unity3D的Mesh与MeshFilter的使用
- Unity3D的Mesh与MeshFilter的使用
- 小石头课堂(三)IO流总结
- 课堂整理(三)
- Unity 中的 Mesh 及绘制圆环
- unity3d 更换装备mesh
- 【边玩边学Unity3d】Mesh属性
- 【边玩边学Unity3d】Mesh属性
- Unity3D-Mesh创建
- 【边玩边学Unity3d】Mesh属性
- QuartZ2D __ 简单用法 1
- [Swift]使用Quartz 2D进行绘图
- Struts 往jsp页面传值的三种方式 以及获取Jsp页面值得一种方式
- Fixed: Failed to create VirtualBox COM object REGDB_E_CLASSNOTREG (0x80040154)
- 常用linux命令
- Unity3D Mesh小课堂(三)圆形与圆环
- iOS-绘图(Quartz2D)的简单使用(原创)
- 枚举类型---enum
- 亿书倾力打造区块链茶座,助力技术分享
- 虚拟文件系统学习笔记(1)
- Java设计模式之解释器模式
- ReactNative-结合定时器使用ActivityIndicator
- Linux端口的关闭和启用
- 编写一个makefile的简单实例