GL圖象庫
GL圖象庫是底層的圖象庫,主要功能是使用程序來繪制常見的2D與3D幾何圖形。這些圖形具有一定的特殊性,他們不屬於3D網格圖形,
只會以面的形式渲染。使用GL圖象庫,可在屏幕中繪制2D幾何圖形,並且該幾何圖形將永遠顯示在屏幕當中,不會因為攝象機的移動而
改變。2D圖形的呈現方式和前面章節介紹的GUI有點類似,值得注意的是,繪制2D圖像時,需要使用GL.LoadOrtho()方法將圖形映射在
平面中;如果繪制的是3D圖形,就無須使用此方法。
使用GL圖象庫時,需要將所有繪制相關的內容寫在OnPostRender()方法中。此方法由系統自身調用,無法手動調用。此外,有關GL圖象
庫的腳本需要綁定在Hierarchy視圖中的攝象機對象當中,否則將無法顯示繪制的圖形
繪制線
在了解如何繪制線之前,先熟悉Unity中GL圖象庫的平面坐標系。按照箭頭所指的方向,平面坐標系的原點(0,0)位於左下腳。
值得注意的是,GL圖象庫的平面坐標和普通坐標是有區別的,GL圖象庫的x軸的最大值是1,y軸的最大值也為1,而不是按照像素
來計算的,因此,在GL圖象庫的平面坐標系中,每個點的橫坐標和縱坐標都應當是0與1之間的浮點數,而真實的像素坐標需要根據這
個浮點數來計算。
比如當前游戲屏幕的像素寬高是500*500,在GL圖象庫平面上選擇一個點(0.5f,0.5f),那么這個點的真實像素的橫坐標和縱坐標應
當是:500(屏幕寬)*0.5(x坐標) = 250
500(屏幕高)*0.5(x坐標) = 250
public class Script:MonoBehaviour
{
//繪制線段材質
public Material material;
//此繪制方法由系統調用
void OnPostRender()
{
if(!material)
{
Debug.LogError(“請給材質資源賦值”);
Return;
}
//設置該材質通道,0為默認值
Material.SetPass(0);
GL.LoadOrtho();
//表示開始繪制,繪制類型為線段
GL.Begin(GL.LINES);
//繪制線段
DrawLine(0,0,200,100);
DrawLine(0,50,200,100);
DrawLine(0,100,200,200);
GL.End();
}
void DrawLine(float x1,float y1;float x2,float y2)
{
//繪制線段,需要將屏幕中某個點的像素坐標點除以屏幕完成寬或高
GL.Vetex(new Vector3(x1/Screen.width,y1/Screen.height,0));
GL.Vetex(new Vector3(x2/Screen.width,y2/Screen.height,0));
}
7.5.2實例----繪制曲線(228)
本例通過GL圖象庫記錄鼠標移動的軌跡並且將其以曲線的形式顯示在屏幕當中,如圖所示,具體實現原理是:記錄鼠標在Game視圖中移動
時每一點的坐標,然后將鼠標移動的坐標存儲在鏈表中,使用繪制方法OnPostRender()遍歷鏈表中記錄的鼠標坐標點,最后通過GL圖象庫
繪制線段的方法將這些點兩兩連成一條線段
當前鼠標x軸位置:835
當前鼠標y軸位置:894
public class Script:MonoBehaviour
{
//繪制線段材質
public Material material;
Private List<Vector> lineInfo;
void Start()
{
//初始化鼠標線段鏈表
lineInfo = new List<Vector3>();
}
void Update()
{
//將每次鼠標改變的位置存儲進鏈表
lineInfo.Add(Input.mousePosition);
}
void OnGUI()
{
GUILayout.Label(“當前鼠標x軸位置:” +Input.mousePosition.x)
GUILayout.Label(“當前鼠標y軸位置:”+Input.mousePosition.y)
}
//此繪制方法又系統調用
void OnPostRender(){
if(!material)
{
Debug.LogError(“請給材質資源賦值”);
Return;
}
//設置該材質通道,0為默認值
material.SetPass(0);
//設置繪制2D圖象
GL.LoadOrtho();
//表示開始繪制,繪制類型為線段
GL.Begin(GL.LINES);
//得到鼠標信息的總數量
int size=lineInfo.Count;
//遍歷鼠標點的鏈表
for(int i=0;i<size-1;i++)
{
Vector3 start = lineInfo[i];
Vector3 end = lineInfo[i+1];
//繪制線段
DrawLine(start.x,start.y,end.x,end.y);
//結束繪制
GL.End();
}
void DrawLine(float x1,float y1;float x2,float y2)
{
//繪制線段,需要將屏幕中某個點的像素坐標點除以屏幕完成寬或高
GL.Vetex(new Vector(x1/Screen.width,y1/Screen.height,0));
GL.Vetex(new Vector(x2/Screen.width,y2/Screen.height,0));
}
}
在上述代碼中,我們通過Update()方法獲取當前鼠標的位置,將每幀的鼠標位置存儲在lineInfo鏈表中,然后在OnPostRender()中遍歷
這個鏈表,將鏈表中記錄的鼠標坐標點連接起來繪制在屏幕當中。
繪制四邊形
在平面內,由不在同一條直線的四條線段首尾順序相接組成的圖形就是四邊形。要確定平面中的一個四邊形,就需要知道4個點,然后將
這4個點連接起來即可。在GL中繪制四邊形,需要使用GL.Begin(GL.QUADS)方法,該方法中參數表示需要繪制的圖形為四邊形。如果設置
的4個點在一條直線上,或者只設置了其中3個點,或者兩個點重疊,無法讓這4個點構成一個四邊形,程序就無法繪制該圖形,這里需要
讀者注意.
本例共繪制了三組幾何圖形------兩個正四邊形和一個無規則四邊形
public class Script:MonoBehaviour
{
public Material mat0;
public Material mat1;
public Material mat3;
void OnPostRender()
{
//繪制正四邊形
DrawRect(100,100,100,100,mat0);
DrawRect(250,100,100,100,mat1);
//繪制無規則四邊形
DrawQiads(15,5,10,115,95,110,90,10,mat3);
}
/**
繪制正四邊形
float x:x軸起始坐標
float y:y軸起始坐標
float width:正四邊形的寬
float height:正四邊形的高
*/
void DrawRect(float x,float y,float width,float height,Material mat)
{
GL.PushMatrix();
mat.SetPass(0);
GL.LoadOrtho();
//繪制類型為四邊形
GL.Begin(GL.QUADS);
GL.Vertex3(x/Screen.width,y/Screen.height,0);
GL.Vertex3(x/Screen.width,(y+height)/Screen.height,0);
GL.Vertex3(x+width)/Screen.width,(y+height)/Screen.height,0);
GL.End();
GL.PopMatrix();
}
/**
繪制無規則的四邊形
float x1:起始點1的橫坐標
float y1:起始點1的橫坐標
float x2:起始點2的橫坐標
float y2:起始點2的橫坐標
float x3:起始點3的橫坐標
float y3:起始點3的橫坐標
float x4:起始點4的橫坐標
float y4:起始點4的橫坐標
void DrawQuads(float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,Material mat)
{ GL.PushMatrix();
mat.SetPass(0);
GL.LoadOrtho();
//繪制類型為四邊形
GL.Begin(GL.QUADS);
GL.Vertex3(x1/Screen.width,y1/Screen.height,0);
GL.Vertex3(x2/Screen.width,y2/Screen.height,0);
GL.Vertex3(x3/Screen.width,y3/Screen.height,0);
GL.Vertex3(x4/Screen.width,y4/Screen.height,0);
GL.End();
GL.PopMatrix();
}}
為了說明正四邊形和無規則四邊形之間的區別,本例將它們封裝成兩個不同的方法,其中DrawRect()方法用於繪制無規則四邊形。
在上述代碼的最后,我們使用GL.End()方法繪制的四邊形顯示在屏幕中。
7.5.4繪制三角形
繪制三角形之前,需要確定平面中的3個點,並且保證這3個點能構成一個三角形,然后將3個點首尾連接起來即可。繪制三角形時,
可以使用GL.Begin(GL.TRIANGLE)方法,該方法的參數為三角形的類型.本例在屏幕中央繪制了一個正三角形,具體代碼如代碼清單
public class Script:MonoBehaviour
{
//材質
public Material mat;
void OnPostRender()
{
//繪制三角形
DrawTriangle(100,0,100,200,200,100,mat);
}
void DrawTriangle(float x1,float y1,float x2,float y2,float x3,float y3,Material mat)
{
mat.SetPass(0);
GL.LoadOrtho();
//繪制三角形
GL.Begin(GL.TRAINGLES);
GL.Vertex3(x1/Screen.width,y1/Screen.height,0);
GL.Vertex3(x2/Screen.width,y2/Screen.height,0);
GL.Vertex3(x3/Screen.width,y3/Screen.height,0);
GL.End();
}}
在上述代碼中,我們使用GL.Vertex3()方法確定三角形三個頂點的位置,並將繪制三角形的所有方法封裝在
DrawTriangle()方法中,最后使用GL.End()方法將三角形顯示在屏幕中。需要說明的是,在調用DrawTriangle()方法時,
需要將三個點的坐標與材質傳入該方法。
繪制3D幾何圖形
GL圖形庫不僅支持繪制2D幾何圖形,還支持繪制3D幾何圖形,而本例將在3D世界中繪制三個平面四邊形,如圖7-17所示,
為了讓讀者更方便看出立體效果,我們在Game視圖中添加了一個立方體組件作為視圖的參照物。通過隨時移動鼠標來修改攝像機朝向
的位置,可以觀察它們之間的區別。圓圈內就是使用GL繪制的圖形,它會隨着攝像機的位置改變而發生移動,
具體的代碼如代碼清單7-19所示
圖7-17 立方圖形
代碼7-19
public class Script:MonoBehaviour
{
public Material mat0;
public Material mat1;
public Material mat3;
void OnPostRender()
{
//繪制正四邊形
DrawRect(100,100,100,100,mat0);
DrawRect(250,100,100,100,mat1);
//繪制無規則四邊形
DrawQuads(15,5,10,115,95,110,90,10,mat3);
}
/**
繪制正四邊形
float x:x軸起始坐標
float y:y軸起始坐標
float width:正四邊形的寬
float height:正四邊形的高
*/
void DrawRect(float x,float y,float width,float height,Material mat)
{
GL.PushMatrix();
mat.SetPass(0);
//繪制類型為四邊形
GL.Begin(GL.QUADS);
GL.Vertex3(x/Screen.width,y/Screen.height,0);
GL.Vertex3(x/Screen.width,(y+height)/Screen.height,0);
GL.Vertex3(x+width)/Screen.width,(y+height)/Screen.height,0);
GL.Vertex3(x+width)/Screen.width, y/Screen.height,0);
GL.End();
GL.PopMatrix();
}
/**
繪制無規則的四邊形
float x1:起始點1的橫坐標
float y1:起始點1的橫坐標
float x2:起始點2的橫坐標
float y2:起始點2的橫坐標
float x3:起始點3的橫坐標
float y3:起始點3的橫坐標
float x4:起始點4的橫坐標
float y4:起始點4的橫坐標
void DrawQuads(float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,Material mat)
{ GL.PushMatrix();
mat.SetPass(0);
//繪制類型為四邊形
GL.Begin(GL.QUADS);
GL.Vertex3(x1/Screen.width,y1/Screen.height,0);
GL.Vertex3(x2/Screen.width,y2/Screen.height,0);
GL.Vertex3(x3/Screen.width,y3/Screen.height,0);
GL.Vertex3(x4/Screen.width,y4/Screen.height,0);
GL.End();
GL.PopMatrix();
}}
在繪制四邊形時,首先需要使用GL.Begin(GL.QUADS)方法設定渲染模型的類型為四邊形,然后使用GL.Vertex3()設置
四邊形每個點的坐標,最后使用GL.Eng() 方法將四邊形渲染在屏幕當中移動攝像機的代碼.
MoveCamera.cs
public class MoveCamera:MonoBehaviour
{
//攝像機參照的模型
public Transform target;
//攝像機距離模型的默認距離
private float distance = 2.0f;
//鼠標在x軸和y軸方向移動的角度
float x;
float y;
//限制旋轉角度的最小值與最大值
float yMinLimit=-20.0f
float yMaxLimit=80.f
//x和y軸方向的移動速度
float xSpeed = 250.0f;
float ySpeed = 120.0f;
void Start(){
//初始化和y軸角度等於參照模型的角度
Vector2 angles =transform.eulerAngles;
x=angles.y;
y=angles.x;
if(rigidbody)
{
rigidbody.freezeRotation = true;
}
void LateUpdate()
{
if(target)
{
//根據鼠標的移動修改攝像機的角度
x+=Input.GetAxis("Mouse X")*xSpeed*0.02f;
y-=Input.GetAxis("Mouse Y")*ySpeed*0.02f;
y = ClampAngle(y,yMinLimit,yMaxLimit);
Quaternion rotation =Quaternion.Euler(y,x,0);
Vector3 position = rotation*new Vector3(0.0f,0.0f,(-distance))+target.position;
//設置模型的位置與旋轉
transform.rotation =rotation;
transform.position =position;
}
}
float ClampAngle(float angle,float min,float max);
}
}
在LateUpdate()方法中通過鼠標的移動來觀察模型,該模型的對象包尋在target變量當中。
線渲染器
線渲染器主要用於在3D世界中渲染線段,與GL圖象庫渲染相比,它更加專業,可以控制線段的組細程度
以及線段的數量,並且以網格對象的形式出現在3D世界中。使用線渲染器繪制線段時,必須先確定這條線段兩個
端點的位置。需要說明的是,這兩個點不是平面中的點而是3D世界中的點
線渲染器以組件的形式出現Unity當中,所以需要將它綁定在某個游戲對象中。這里我們在Unity導航中選擇
"GameObject"--->"CreateEmpty"菜單項創建一個空的游戲對象,然后雜Hierarchy視圖中選擇該對象后,繼續在
Unity導航菜單中選擇"Component"-->"Line Render"菜單項,即可將線渲染器組件添加至游戲對象中,接着是設置
參數。
本例中繪制了3條相連的線段,它是以4個頂點確定的3條線段,並且它們首尾相接成一條線。這個線段以立體的形式
出現在3D世界中
public class Script:MonoBehaviour
{
//線段對象
private CameObject LineRenderGameObject;
//線段渲染器
private LineRenderer lineRenderer;
//設置線段的頂點數,4個點確定3條直線
private int lineLength = 4;
//記錄4個點,連接一條線段
private Vector3 v0 = new Vector3(1.0f,0.0f,0.0f);
private Vector3 v1 = new Vector3(2.0f,0.0f,0.0f);
private Vector3 v2 = new Vector3(3.0f,0.0f,0.0f);
private Vector3 v3 = new Vector3(4.0f,0.0f,0.0f);
void Start()
{
//獲得游戲對象
LineRenderGameObject = GameObject.Find("ObjLine");
//獲得線渲染器組件
lineRenderer = (LineRendererGameObject.GetComponent("LineRenderer");
//設置線的頂點數
lineRenderer.SetVertexCount(lineLength);
//設置線的寬度
lineRenderer.SetWidth(0.1f,0.1f);
}
void Update(){
//使用4個頂點渲染3條線段
lineRender.SetPosition(0,v0);
lineRender.SetPosition(1,v1);
lineRender.SetPosition(2,v2);
lineRender.SetPosition(3,v3);
}
}
在上述代碼,我們首先獲取了線渲染器組件對象,然后設置頂點的數量,最后調用SetPosition()方法將線段顯示在屏幕中。
SetPosition()方法的第一個參數表示每個點的ID,讓它保持唯一性,第一個參數表示該頂點的3D的位置.
網格渲染
“Component”?Mesh?”Mesh Filter”菜單項與”Mesh Renderer”菜單項,即可將組件添加至游戲對象本身。
本例在屏幕中渲染了兩個網格面對象。因為網格面又三角形網格頂點的位置,三角形由3個頂點組成,所以它們的規律是:
一個三角形數組長度就是3,兩個三角形數組長度就是6,依次類推該數組的長度只可能是3的倍數。最后繪制網格時使用triangles數組,
數組中的ID和Vertices(網格頂點)的頂點ID一一對應。
分5等份填充
完全填充
public class script:MonoBehaviour
{
//構成三角形1的位置
Vector3 v0 = new Vector3(5,0,0);
Vector3 v1 = new Vector3(0,5,0);
Vector3 v2 = new Vector3(0,0,5);
//構成三角形2的位置
Vector3 v3 = new Vector3(-5,0,0);
Vector3 v4 = new Vector3(0,-5,0);
Vector3 v5 = new Vector3(0,0,-5);
//構成三角形1的貼圖比例
Vector2 u0 = new Vector2(0,0);
Vector2 u1 = new Vector2(0,5);
Vector2 u2 = new Vector2(5,5);
//構成三角形2的貼圖比例
Vector2 u3 = new Vector2(0,0);
Vector2 u4 = new Vector2(0,1);
Vector2 u5 = new Vector2(1,1);
void Start()
{
//得到網格渲染器對象
MeshFilter meshFilter =(MeshFilter)GameObject.Find("face").GetComponent(typeof(MeshFilter));
//通過渲染器對象得到網格對象
Mesh mesh = meshFilter.mesh;
//設置三角形頂點的數組,6個點表示設置了兩個三角形
mesh.Vertices = new Vector3[]{v0,v1,v2,v3,v4,v5};
//設置三角形面上的貼圖比例
mesh.uv = new Vector2[] {u0,u1,u2,u3,u4,u5};
//設置三角形索引,繪制三角形
mesh.triangles = new int[]{0,1,2,3,4,5};
}}
代碼最后的mesh.triangles表示設定三角形的索引數組,該數組中的ID表示相對頂點數組中的坐標。目前這個數組中的元素是0、1、2、3、4和5,
對應頂點數組中6個頂點坐標。因為3個點確定一個三角形面,所以這里使用定點數組中0,1,2確定了一個三角形,3,4,5又確定了一個
三角形。
游戲實例—控制人物移動
為了讓讀者更清晰地了解如何控制主角移動與播放骨骼動畫,下面我們將角色控制器組件的人物動畫拆開,使用代碼自行實現他的行走動畫。運行游戲后,按鍵盤鍵上的
“W”,”S”,”A”,”D”來移動主角。
public class Script:MonoBehaviour
{
//人物行走的方向狀態
public const int HERO_UP=0;
public const int HERO_RIGHT=1;
public const int HERO_DOWN=2;
public const int HERO_LEFT=3;
//人物當前的行走方向
public int state = 0;
//人物移動速度
public int moveSpeed =10;
//初始化人物的默認位置
public void Awake(){
state =HERO_DOWN;
}
void Update(){
//獲取控制的方向數據
float KeyVertical =Input.GetAxis("Vertical");
float KeyHorizontal = Input.GetAxis("Horzontal");
if(KeyVertical ==-1)
{
setHeroState(HERO_LEFT);
}else if(KeyVertical ==1)
{
//設置人物動畫往右行走
setHeroState(HERO_RIGHT);
}
if(KeyHorizontal ==1)
{
setHeroState(HERO_DOWN);
}else if(KeyHorzontal ==-1)
{
setHeroState(HERO_UP);
}
if(KeyVertical ==0 &&KeyHorizontal ==0)
{
animation.Play();
}
}
public void setHeroState(int newState)
{
//根據當前人物方向與上一次備份方向計算出模型旋轉的角度
int rotateValue =(newState-state)*90;
Vector3 transformValue =new Vector3();
//播放行走動畫
animation.Play("walk");
//模型移動的位移的數值
switch(newState){
case HERO_UP:
transformValue = Vector3.forward*Time.deltaTime;
break;
case HERO_DOWN:
transformValue =(-Vector3.forward).Time.deltaTime;
break;
case HERO_LEFT:
transformValue = Vector3.left*Time.deltaTime;
break;
case HERO_RIGHT:
transformValue = (-Vector3.left)*Time.deltaTime;
break;
}
//模型旋轉
transform.Rotate(Vector3.up,rotateValue);
//移動人物
transform.Translate(transformValue *moveSpeed,Space.World);
state = newState;
}
}
本例使用游戲狀態機將主角的行走分為4個狀態:向前行走,向后行走,向左行走,向右行走。按下不同的方向鍵后,
使用animation.Play()方法播放行走動畫,該方法的參數為動畫名稱,最后根據當前的行走狀態計算模型的旋轉角度,
使其按照正確的方向行走。
本章首先介紹了如何處理鍵盤與鼠標輸入事件,比如按下事件,抬起事件和長按事件等,接着介紹了自定義按鍵事件、
模型與動畫,然后介紹了如何使用GL圖像庫繪制2D與3D的線段與網絡模型,以及線渲染器與網格渲染器的繪制方法,
最后以一個實例的形式向讀者介紹如何使用鍵盤控制主角移動並且播放骨骼動畫。