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();
- }
- }
来源: http://www.bubuko.com/infodetail-3422906.html