绘制地形和小地图

一.地形和小地图的用途

地形,作为游戏中必要的物理环境之一,在游戏中的占比非常高,像原神、GTA5、荒野大镖客等等3A大作,都有地形,小地图也是如此,像PUBG,和平精英,王者荣耀等等,这些游戏也用到了小地图,总之,地形和小地图几乎是一个成熟的游戏中必不可少的一部分,那么,我们可以考虑一下,这些游戏中的地形和小地图都是如何实现的。

先考虑小地图,一种方法是利用RT图实时渲染,需要在场景中创建一个RT相机,这样确实简单,但是对性能的消耗非常大,会造成游戏卡顿,甚至直接死机,还有一种方法就是,把场景中的地图截一张截图,然后把截图赋值到Image上面,等比例计算人物位置,但是这样对截出来的图的精度要求很高,稍有偏差可能就失之毫厘差以千里了,已我们现有的条件实现很困难,所以,我们做一个简易版的小地图,直接把地形的材质赋值给小地图,这样就能实现小地图效果。

地形的实现也比较简单,地形地形,顾名思义得有形状,不能太平,需要有起伏,所以,我们还是需要顶点辅助类的帮忙来绘制地形,绘制出来的地形没有颜色,所以我们就用两种颜色来区分地形高低,话不多说,直接上代码:

using UnityEngine;
using UnityEngine.UI;

public class MeshMap : MonoBehaviour
{
    //颜色1
    public Color color1=Color.blue;
    //颜色2
    public Color color2=Color.green;
    //地形长度
    public int width = 200;
    //地形宽度
    public int height = 200;
    //地形的Texture2D图
    public Texture2D texture2D;
    //小地图
    public Image map;
    //玩家在小地图中的位置
    public Image playerPos;
    //场景中的玩家
    public GameObject player;
    void Start()
    {
        //规定地形的Texture2D图大小
        texture2D = new Texture2D(width, height);
        //创建一个顶点辅助类
        VertexHelper vh = new VertexHelper();
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                //柏林噪声,用来实现根据地形高低显示不同颜色值的变化
                float y = Mathf.PerlinNoise(i * 0.1f, j * 0.1f);
                texture2D.SetPixel(i, j, Color.Lerp(color1, color2, y));
                //计算uv坐标
                float uvx = (float)i / (float)width;
                float uvy = (float)j / (float)height;
                //添加顶点
                vh.AddVert(new Vector3(i - width / 2, y * 3, j - height / 2), Color.white, new Vector2(uvx, uvy));
                //设置顶点顺序
                if (i<width-1&&j<height-1)
                {
                    vh.AddTriangle(i * height + j, i * height + j + 1, (i + 1) * height + j + 1);
                    vh.AddTriangle(i * height + j, (i + 1) * height + j + 1, (i + 1) * height + j);
                }
            }
        }
        texture2D.Apply();
        //创建一个Mesh类
        Mesh mesh = new Mesh();
        //把mesh赋值给顶点辅助类
        vh.FillMesh(mesh);
        //获取网格过滤器
        GetComponent<MeshFilter>().mesh = mesh;
        //获取网格渲染器
        GetComponent<MeshCollider>().sharedMesh = mesh;
        //获取网格碰撞器
        GetComponent<MeshRenderer>().material.mainTexture = texture2D;

        //创建一个Materal类,用来给地形赋材质
        Material material = new Material(Shader.Find("UI/Default"));
        material.mainTexture = texture2D;
        map.material = material;
        //根据标签找到场景中的玩家
        player = GameObject.FindGameObjectWithTag("Player").gameObject;

    }
    void Update()
    {
        //通过控制小地图的中心点来实现小地图移动
        map.rectTransform.pivot = new Vector2((width / 2 + player.transform.position.x) / width,
            (height / 2 + player.transform.position.z) / height);
        //控制小地图上玩家的点的转向
        playerPos.transform.localRotation = Quaternion.Euler(0,0, -player.transform.localEulerAngles.y);
    }
}

运行效果如下:

 这样,我们就实现了地形和小地图效果。

二.合批

我们创建好地形之后,大多数的游戏都是在地形上面创建许多的花草树木,而这些东西都是模型,只要是模型,就是用任意个三角形拼出来的,一个普通的模型可能有上万个三角形,这就是渲染压力,这还只是一个模型,要是场景中有成千上万个模型呢?渲染压力是不是会更大,但是,我们在一开始做游戏的时候,只会考虑实现效果,没时间去优化细节,所以,我们就来了解一下优化的知识。

在unity中,有种优化叫做合批,而我们为什么要进行合批呢?是因为合批(批量渲染)是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。但这是建立在GPU相对空闲,而CPU把更多的时间都耗费在渲染命令的提交上时,才有意义,而合批又分为动态合批和静态合批,动态合批与静态合批其本质是对将多次绘制请求,在允许的条件下进行合并处理,减少 CPU 对 GPU 绘制请求的次数,达到提高性能的目的,那怎么开启合批呢,如下如所示:

下面我们来介绍一下动态合批和静态合批: 

(1)动态合批:如果动态物体共用着相同的材质,那么Unity会自动对这些物体进行处理(动态批处理是自动完成的),但是要注意四个条件,一是动态批处理仅支持小于900顶点的网格物体,二是如果着色器使用顶点位置,法线和UV值三种属性,只能批处理300顶点以下的物体;如果着色器需要使用顶点位置,法线,UV0,UV1和切向量,只能批处理180顶点以下的物体,三是不能使用缩放尺度(scale),缩放不同的物体不能进行批处理;统一缩放尺度的物体不会与非统一缩放尺度的物体进行批处理,eg:(1,1,1)和(1,2,1)不会批处理,(1,2,1)和(1,3,1)可以进行批处理,四是必须使用相同材质的实例化物体,否则将会导致批处理失败;

(2)静态合批:将static的静态物体(永远不会移动、旋转和缩放) ,如果相同材质球,面数在xx之内。unity会自动合并成一个batch送往GPU处理

这两种合批的缺点就是:动态合批容易被打断,静态合批会造成较大的内存压力,会占用更大的内存,牺牲内存来减小渲染压力,总结来说,动态合批和静态合批的区别,一个是内存给GPU降低压力,一个是拿CPU给GPU降低压力,静态合批一次合批过后不能更改,动态合批在合批之后进行更改。

我们来看下面这张图:

 在unity中,Batches叫做批处理,批处理数值越大,渲染的压力越大,游戏的流畅度越低,太大就可能导致游戏卡死,这个值也可以理解为DrawCall值,因为,一次Batch至少包含一次DrawCall,这里又提到了一个新概念:DrawCall。

DrawCall本身的含义其实很简单,就是CPU调用图像应用编程接口,如OpenGL中的glDrawElecments命令或者是DirectX中的DrawIndexedPrimitive命令,来命令GPU进行渲染的操作,这样的一个过程叫做一次DrawCall,那么我们优化的目的就是为了减小DrawCall的次数,所以就用到了上面的批处理(也叫合批)。

最后附加一个合批失败原因总结:

GitHub - Unity-Technologies/BatchBreakingCause: This project demonstrates different cases when Unity has to break a batch while rendering.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值