(三)GL 空间变换

1.前言

采用GL类以及Graphics类进行绘制图形时,都需要用到坐标变换。这跟采用图形学接口进行绘制时相同。以一个球为例,如果在坐标原点处绘制,球心坐标为0。如果在其他位置绘制,球心坐标不为0,此时球面顶点坐标需要重新计算。如果采用矩阵变换(坐标变换),将坐标原点移动到球心所在的位置,则球心仍在原点位置,球面坐标保持不变。所以我们在绘制一个图形时,先进行坐标变换,然后再绘制,绘制结束后再恢复到原来坐标系(即世界坐标系),然后再坐标变换绘制其他图形,然后再恢复到世界坐标系。

2.GL类坐标变换

此节主要针对GL类几个加载坐标系的方法来进行说明,如LoadPixelMatrix、MultMatrix等。

2.1 世界坐标系

默认为世界坐标系,如下代码为在世界坐标系下绘制正多边形。此时移动camera,图像会移动。因为图像的坐标与camera坐标无关系。

    public int circleRadius = 3;
    public int circleCount = 6; public int zValue = 0; private void DrawCircleSurface() { float angleDelta = 2 * Mathf.PI / circleCount; GL.Begin(GL.TRIANGLES); GL.Color(Color.yellow); for (int i = 0; i < circleCount; i++) { float angle = angleDelta * i; float angleNext = angle + angleDelta; GL.Vertex3(0, 0, zValue); GL.Vertex3(Mathf.Cos(angle) * circleRadius, Mathf.Sin(angle) * circleRadius, zValue); GL.Vertex3(Mathf.Cos(angleNext) * circleRadius, Mathf.Sin(angleNext) * circleRadius, zValue); } GL.End(); }`在这里插入代码片` 

2.2 局部坐标系

如果我们让图像变成某一个游戏物体的子物体(如camera),可以采用GL.MultMatrix方法。首先通过transform.localToWorldMatrix获取局部坐标到世界坐标的变换矩阵,然后通过GL.MultMatrix加载进来就可以。此时我们进行绘制时,计算的坐标都为局部坐标,最终数据会通过坐标转换为世界坐标系下结果,代码如下。如果此时再移动camera,图像则跟随camera移动。

   private void DrawCircleSurfaceLocal()
    {
        GL.PushMatrix(); GL.MultMatrix(transform.localToWorldMatrix); DrawCircleSurface(); GL.PopMatrix(); } 

2.3 屏幕坐标系

可以通过GL.LoadPixelMatrix将图像直接绘制到屏幕上,此方法有两个重载,对应绘制到当前屏幕上和绘制到指定大小像素上。如果指定大小像素与屏幕相同,则即为绘制到当前屏幕大小。但是此时图像坐标是以像素为大小的,且z值坐标为0.。而且屏幕坐标没有负值,所以以本例正多边形为例,由于原点在0位置,所以屏幕只能显示四分之一图像。

    private void DrawCircleSurfaceScreen()
    {
        //circleRadius = 1000; 以像素为单位 GL.PushMatrix(); //GL.LoadPixelMatrix(); GL.LoadPixelMatrix(0,screenSize.x,0,screenSize.y); DrawCircleSurface(); GL.PopMatrix(); } 

当screenSize的x与y值和屏幕分辨率相同时,与GL.LoadPixelMatrix()的效果一致。

2.4 正交坐标系

采用GL.LoadOrtho()方法可以将图像直接绘制到屏幕上,此时是正交视图,大小为1,所以坐标系数值均要小于等于1.。同样坐标值没有负值。

    private void DrawCircleSurfaceOrtho()
    {
        //circleRadius = 1; 最大值为1 GL.PushMatrix(); GL.LoadOrtho(); DrawCircleSurface(); GL.PopMatrix(); } 

2.5 视口分离

通过GL.Viewport方法可以实现绘制在局部视口内,如下所示:

    private void DrawCircleSurfaceViewport()
    {
        GL.PushMatrix(); GL.LoadPixelMatrix(); GL.Viewport(new Rect(0, 0, Screen.width / 2, Screen.height / 2)); DrawCircleSurface(); GL.PopMatrix(); } 

注意:如果坐标系采用的正交坐标系,即采用GL.LoadOrtho()方法,则视口的最大值为1,此时 GL.Viewport(new Rect(0, 0, 1.0f / 2, 1.0f / 2));

3.完整代码

using System.Collections;
using System.Collections.Generic; using UnityEngine; public class Graphics03GLMatrix : MonoBehaviour { public enum SpaceType { WORLD, LOCAL, SCREEN_NORMAL, SCREEN, VIEWPORT } public SpaceType type = SpaceType.WORLD; public int circleRadius = 3; public int circleCount = 6; public int zValue = 0; public Vector2 screenSize = Vector2.zero; private void OnRenderObject() { SetMaterialPass(); switch (type) { case SpaceType.WORLD: DrawCircleSurface(); break; case SpaceType.LOCAL: DrawCircleSurfaceLocal(); break; case SpaceType.SCREEN_NORMAL: DrawCircleSurfaceOrtho(); break; case SpaceType.SCREEN: DrawCircleSurfaceScreen(); break; case SpaceType.VIEWPORT: DrawCircleSurfaceViewport(); break; } } private Material glMat; private void SetMaterialPass() { if (glMat == null) { glMat = new Material(Shader.Find("Hidden/Internal-Colored")); } glMat.SetPass(0); } private void DrawCircleSurface() { float angleDelta = 2 * Mathf.PI / circleCount; GL.Begin(GL.TRIANGLES); GL.Color(Color.yellow); for (int i = 0; i < circleCount; i++) { float angle = angleDelta * i; float angleNext = angle + angleDelta; GL.Vertex3(0, 0, zValue); GL.Vertex3(Mathf.Cos(angle) * circleRadius, Mathf.Sin(angle) * circleRadius, zValue); GL.Vertex3(Mathf.Cos(angleNext) * circleRadius, Mathf.Sin(angleNext) * circleRadius, zValue); } GL.End(); } private void DrawCircleSurfaceLocal() { GL.PushMatrix(); GL.MultMatrix(transform.localToWorldMatrix); DrawCircleSurface(); GL.PopMatrix(); } private void DrawCircleSurfaceOrtho() { //circleRadius = 1; 最大值为1 GL.PushMatrix(); GL.LoadOrtho(); DrawCircleSurface(); GL.PopMatrix(); } private void DrawCircleSurfaceScreen() { //circleRadius = 1000; 以像素为单位 GL.PushMatrix(); //GL.LoadPixelMatrix(); GL.LoadPixelMatrix(0,screenSize.x,0,screenSize.y); DrawCircleSurface(); GL.PopMatrix(); } private void DrawCircleSurfaceViewport() { GL.PushMatrix(); GL.LoadPixelMatrix(); GL.Viewport(new Rect(0, 0, Screen.width / 2, Screen.height / 2)); DrawCircleSurface(); GL.PopMatrix(); } } 

猜你喜欢

转载自www.cnblogs.com/llstart-new0201/p/12315711.html
今日推荐