基础知识
1、静态对象
Graphics的静态对象提供了缓冲区数据与渲染Setting。
- Graphics.activeColorBuffer:当前绘制区域颜色缓冲。
- Graphics.activeColorGamut:当前绘制区域色域。
- Graphics.activeDepthBuffer:当前绘制区域深度缓冲。
- Graphics.minOpenGLESVersion:最低OpenGL ES版本,可在PlayerSettings中设置。
- Graphics.preserveFramebufferAlpha:若勾选后可在Android平台中保留原生UI的缓冲alpha值,用以在原生UI上绘制对象。
2、纹理
- Graphics.DrawTexture:在屏幕空间绘制纹理。
- Graphics.Blit(source, dest,mat):使用着色器拷贝原纹理到RenderTexture
- Graphics.CopyTexture(sre, dst):复制纹理(可对区域拷贝,无法改变分辨率)
在绘制屏幕纹理时,需要注意API导致的坐标系变化。在默认坐标系中,顶点坐标原点在屏幕左上角,uv坐标原点在绘制对象的左下角。Graphics.DrawTexture只能在OnGUI以及之后的生命周期方法中调用,否则无法显示。
Graphics.Blit通常用于后处理,需要注意的是使用后会修改RenderTexture.active,在此接口后直接使用Graphics绘制接口,不会生效。
Graphics.CopyTexture可以从一个纹理的像素数据拷贝到另一个纹理,也可以从mip或子区域中拷贝。拷贝对象时,不会进行放缩操作。因此原纹理与目标纹理的大小与格式必须匹配。当目标纹理与原纹理大小不一致时,只会读取目标纹理大小区域。若希望改变读取的纹理分辨率,只能手动创建与目标相同尺寸的RT,拷贝后再读取。Graphics.Blit(texture, rt, mats[0]); RenderTexture.active = null; Graphics.DrawTexture(screenRects[0], rt, uiRects[0], 0, 0, 0, 0, null);
QualitySettings.masterTextureLimit会影响Graphics.CopyTexture获取纹理的质量,具体见API说明。
3、mesh
- Graphics.DrawMesh:绘制Mesh
- Graphics.DrawMeshInstanced:使用GPU instancing多次绘制相同的Mesh
- Graphics.DrawMeshNow:立即绘制Mesh(对象无法逐像素光照着色,也无法接收与投射实时阴影)
一般的,Graphics下的绘制命令不会立即绘制,更多的是“提交”渲染命令,后续在渲染阶段进行统一绘制。
所以,一般的使用,在
Update()
方法中即可。
Graphics.DrawMesh在一帧中绘制网格,而无需创建GameObject。此接口不会立即绘制Mesh,只是将数据提交到正常的渲染流程中,如果希望立即绘制对象,使用Graphics.DrawMeshNow。
Graphics.DrawMeshInstanced使用GPU instancing特性绘制大量相同Mesh,同样为一帧渲染一次。需要注意的是,此接口将所有需要实例化的网格作为一组进行culling与sorting。在Mesh合并完成后,不会对其进行额外的视锥体剔除与遮挡剔除,也不会为透明度或深度对实例进行排序。单次调用实例数量上限为1023。
虽然看似Graphics.DrawMeshInstanced性能更高,但其没有Unity自带的视锥体剔除、LOD以及遮挡剔除等优化流程,因此要达到效率最优还需要额外的处理。
4、其他
- Graphics.SetRenderTarget(rt/rb):设置渲染对象
- Graphics.SetRandomWriteTarget(index, cb/rt):为Shader Model 4.5以上的PS设置随机渲染目标(UAV)
- Graphics.ConvertTexture(sre, dst):转换纹理格式(RenderTextureFormat)
- Graphics.CreateGraphicsFence(GraphicsFenceType,SynchronisationStage):创建一个GraphicsFence,在最后Blit、Clear、Draw、Dispatch或Texture Copy命令之后传递到GPU。通常用于GPU计算同步。
- Graphics.WaitOnAsyncGraphicsFence:GPU等待GraphicsFence同步。
实际应用-实现Scence场景中的Grid 网格对齐效果
public class DrawGroundGrid : MonoBehaviour
{
private Transform _cameraTR;
public Material[] _lineMaterial;
private Mesh lineMeshX;
private Mesh lineMeshY;
private bool _enabled_1 = false;
private bool _enabled_10 = false;
// Start is called before the first frame update
void Start()
{
lineMeshX = new Mesh();
lineMeshX.vertices = new Vector3[] { new Vector3(0, 0, 0), new Vector3(1000, 0, 0) };
lineMeshX.SetIndices(new int[] { 0, 1 }, MeshTopology.Lines, 0);
lineMeshX.UploadMeshData(true);
lineMeshY = new Mesh();
lineMeshY.vertices = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 1000) };
lineMeshY.SetIndices(new int[] { 0, 1 }, MeshTopology.Lines, 0);
lineMeshY.UploadMeshData(true);
_cameraTR = GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
if (Mathf.Abs(_cameraTR.position.y) < 10)
{
if (_enabled_1) _enabled_1 = false;
Color linshi1 = _lineMaterial[0].color;
linshi1.a = (float)(1 - Mathf.Abs(_cameraTR.position.y) / 10);//这个值随着相机Y轴的距离而变化
_lineMaterial[0].color=linshi1;
}
else
{
_enabled_1 = true;
if (Mathf.Abs(_cameraTR.position.y) < 100)
{
if (_enabled_1) _enabled_10 = false;
Color linshi2 = _lineMaterial[1].color;
linshi2.a = (float)(1 - Mathf.Abs(_cameraTR.position.y) / 100);
_lineMaterial[1].color=linshi2;
}
else
{
_enabled_10 = true;
}
}
for (int i = -500; i < 501; i++)
{
if (i % 10 != 0)
{
if (!_enabled_1)
{
Graphics.DrawMesh(lineMeshX, new Vector3(-500, 0, i), Quaternion.identity, _lineMaterial[0], 0);
Graphics.DrawMesh(lineMeshY, new Vector3(i, 0, -500), Quaternion.identity, _lineMaterial[0], 0);
}
}
else if (i % 100 != 0)
{
if (!_enabled_10)
{
Graphics.DrawMesh(lineMeshX, new Vector3(-500, 0, i), Quaternion.identity, _lineMaterial[1], 0);
Graphics.DrawMesh(lineMeshY, new Vector3(i, 0, -500), Quaternion.identity, _lineMaterial[1], 0);
}
}
else
{
Graphics.DrawMesh(lineMeshX, new Vector3(-500, 0, i), Quaternion.identity, _lineMaterial[2], 0);
Graphics.DrawMesh(lineMeshY, new Vector3(i, 0, -500), Quaternion.identity, _lineMaterial[2], 0);
}
}
}
}