在Unity3D中绘制一个平面图形可不容易,可别惯性思维觉得,嗯,这引擎3D的事情都能解决了,2D的不叫事情。其实除去布置UI,在Unity3D中绘制一个2D平面图形是很蛋疼的一件事情。不然的话,也不会有大量的平面图形绘制插件出现。如果不用插件的话,需要利用的GL去绘制平面图形,而在屏幕中动态放置文本则需要用的GUI。
GL是Unity3D直接调用底层的OpenGL图形库来进行图形绘制,GUI估计大家是有印象的,在Helloworld中,用脚本动态创建文本用到这个对象。下面用一个例子说明GL的使用与更进一步说明GUI如何布置文本。
如下图,十足一个苏格兰的国旗:
则需要对主摄像机挂载如下的脚本:
using UnityEngine;
using System.Collections;
public class GLLine : MonoBehaviour
{
private static Material lineMaterial;
static void CreateLineMaterial()
{
lineMaterial = new Material("Shader \"Lines/Colored Blended\" {" +
"SubShader { Pass { " +
" Blend SrcAlpha OneMinusSrcAlpha " +
" ZWrite Off Cull Off Fog { Mode Off } " +
" BindChannels {" +
" Bind \"vertex\", vertex Bind \"color\", color }" +
"} } }");//不同的Unity3D的版本,这段可能不同,具体只能从Unity3D的API抄下来,这是Unity4.x的设置
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;
lineMaterial.SetPass(0);
//只能这样设置材质,不能设置为其它材质,同时还是设置通道
}
void OnPostRender()//所有GL的绘制只能写在这个方法里面,并将此脚本赋予给主摄像机
{
CreateLineMaterial();
GL.LoadOrtho();//设置绘制2D图像
//画从左下到右上的线
GL.Begin(GL.LINES);
GL.Color(Color.white);
GL.Vertex(new Vector2(0, 0));
GL.Vertex(new Vector2(1, 1));
GL.End();
//画从左上到右下的线
GL.Begin(GL.LINES);
GL.Color(Color.white);
GL.Vertex(new Vector2(0, 1));
GL.Vertex(new Vector2(1, 0));
GL.End();
}
void OnGUI()//所有关于GUI的绘制只能写在这个方法里面,不过可以将此脚本赋予给其它物体,不一定是主摄像机
{
string string_content = "线";
GUIStyle fontStyle = new GUIStyle();
fontStyle.normal.background = null;//设置背景填充
fontStyle.normal.textColor = Color.white;//设置字体颜色
fontStyle.fontSize = 24;//字体大小
Vector2 string_size = fontStyle.CalcSize(new GUIContent(string_content));//根据设置的样式求得字体具体的尺寸
GUI.Label(new Rect(0, 0, string_size.x, string_size.y), string_content, fontStyle);
}
}
大家可以看到,虽然只是设置两条线和设置一个左上角的文字,但需要的编程的代码绝不少功夫。
首先只能赋予GL画笔一个Lines/Colored Blended的固定材质,这个材质还不是Unity3D的普通材质,所以不能用Shader.Find还代替这种冗长的用Shader字符串新建材质的方法。如果不这样设置材质,后面的GL.Color是没有效果的。其次,GL.Begin(GL.LINES);意为画一个不填充的多边形,而不是画一条线,对应OpenGL的glBegin(GL_LINE_LOOP);如果后面GL.Vertex打的点数多于3个,含本数的话,那么则会形成一个没有填充色的三角形,具体可以见下面的例子。GL.Vertex打的点中的二维坐标new Vector2(x,y)中的x与y直接就表示(屏幕的横向宽百分之几,屏幕的纵向高百分之几)。
GUI设置文本,如果需要给这个文本设置字体什么的需要用到GUIStyle,具体的设置如上所示。设置完毕,一般再用CalcSize求得这个字体的具体尺寸。所以不用设置这个字体的对齐方式,并且,可以不用考虑GUI.Label所需要设置的矩形的大小了,直接就是CalcSize的返回的Vector2的x与y。倒是矩形的坐标需要注意一下,GUI的(0,0)是屏幕的左上角,GL的(0,0)则在左下角。
如下图:
则需要对主摄像机挂载如下的脚本:
using UnityEngine;
using System.Collections;
public class GLCircle_Rectangle : MonoBehaviour
{
private static Material lineMaterial;
static void CreateLineMaterial()
{
lineMaterial = new Material("Shader \"Lines/Colored Blended\" {" +
"SubShader { Pass { " +
" Blend SrcAlpha OneMinusSrcAlpha " +
" ZWrite Off Cull Off Fog { Mode Off } " +
" BindChannels {" +
" Bind \"vertex\", vertex Bind \"color\", color }" +
"} } }");
lineMaterial.hideFlags = HideFlags.HideAndDontSave;
lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave;
lineMaterial.SetPass(0);
}
void OnPostRender()
{
CreateLineMaterial();
GL.LoadOrtho();//设置绘制2D图像
//画填充多边形
GL.Begin(GL.QUADS);
GL.Color(Color.white);
GL.Vertex(new Vector2(0.1f, 0.1f));
GL.Vertex(new Vector2(0.1f, 0.2f));
GL.Vertex(new Vector2(0.2f, 0.2f));
GL.Vertex(new Vector2(0.2f, 0.1f));
GL.End();
//画圆
GL.Begin(GL.LINES);//如果是填充圆则用GL.Begin(GL.QUADS);
GL.Color(Color.yellow); Vector2 Circle_center_point = new Vector2(0.75f, 0.75f);
float Circle_r_x = 0.1f;
float Circle_r_y = Circle_r_x * Screen.width / Screen.height;
int n = 1000;//实质是绘制一个正1000边形
for (int i = 0; i < n; ++i)//割圆术画圆
{
GL.Vertex(new Vector2(Circle_center_point.x + Circle_r_x * Mathf.Cos(2 * Mathf.PI / n * i), Circle_center_point.y + Circle_r_y * Mathf.Sin(2 * Mathf.PI / n * i)));
}
GL.End();
}
void OnGUI()
{
string string_content = "矩形和圆形";
GUIStyle fontStyle = new GUIStyle();
fontStyle.normal.background = null;//设置背景填充
fontStyle.normal.textColor = Color.white;//设置字体颜色
fontStyle.fontSize = 24;//字体大小
Vector2 string_size = fontStyle.CalcSize(new GUIContent(string_content));
GUI.Label(new Rect(Screen.width - string_size.x, Screen.height - string_size.y, string_size.x, string_size.y), string_content, fontStyle);
}
}
这里说明了Unity3D中如何用GL画圆和矩形。脚本中对GL与GUI和上面的脚本类似的,同时表达了,如何用GL画一个填充的矩形,也表明了GL.Begin(GL.QUADS);与GL.Begin(GL.LINES);的区别,GL的Begin也只有这2种初始化,GL.Begin(GL.QUADS);则对应于OpenGL的glBegin(GL_POLYGON);。
画矩形没什么所说的,就是打四个点。GL会自己连起来的。画圆这里实则是画一个正1000边形,如果你觉得不够圆还可以画成100000边形。此算法是OpenGL的公认画圆算法,甚至是计算机图形学公认的画圆算法,比起经典算法《【win32】计算机图形学——中点法画线和八分法画圆》(点击打开链接)效率高得不知道哪里去了。
至此,整个程序的说明就结束了。
这个画圆算法以后自己换换参数就行,因为其思想实在深奥,难以弄懂,不过可以说说这里的典故。
实质上这和求圆周率算法,求正多边形的中心至边角距离与正多边形周长之比,从正方形(正4边形),逼近到正6边形,到逼近到无穷的正无穷边形的正多边形的中心至边角距离与正多边形周长之比,可以概括如下公式来求圆周率:
由于从正方形不停在砍角,求新的正多边形,所以也叫割圆术。
这个公式最早的思想是由南北朝的刘徽提出的,后来祖冲之直接转化为应用,求出正96边形的中心与正多边形周长之比,3.1415926的比率用了很久很久,实际上这个比率对于平常应用也足够了。后来,西欧文艺复兴和大航海时期,甚至到了近代,极限和微积分的计算方法发明之后,也是用直接用这中国的古典思想在求圆周率。而今天,在计算机上打点画圆,发现也是画正N变形,通过对N的迭代来画圆是最快的。实质上就是在割圆。可见,刘徽与祖冲之的历史地位!