7

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四)

 3 years ago
source link: https://www.xuanyusong.com/archives/4699
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四)

GPU发热的元凶之一“带宽” 所以通常我们都会打开mipmap,如下图所示,在unity中可以拖动右上角的条来查看贴图每个等级的mipmap贴图。那么它的等级一共分成0级-9级, 0级表示最清楚,9级表示最模糊。 贴图分辨率是依次减半,如512 256 128 64 32 16 8 4 2 0

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四) - 雨松MOMO程序研究院 - 1

所以说如果带了mipmap的贴图在内存上就会多占33%左右,然而显存和纹理带宽就不一定了,如果摄像机离得比较远,就会采用更小的贴图渲染,贴图所占显存小了,那么对应纹理带宽也一并减小,从而性能就优化了。

也有团队为了减少内存,从而关闭了mipmap。这就会导致另一个问题,美术的贴图非常精细,在UI中近距离观察效果很好,可是一旦摄像机拉远,就会出现“噪点”很难看。 所以mipmap一定要打开。

总体来说mipmap是用内存换纹理带宽的一种优化方式。可是内存毕竟涨了33%,所以unity2018.2中提供了streaming流加载贴图mipmap,意思就是用到哪一级mipmap只加载这一级的,其他级的mipmap不加载。这将大量减少内存开销,如下图所示,unity提供了一个demo例子,内存大概减少25%-30%。

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四) - 雨松MOMO程序研究院 - 2

到这里大家应该都能很好理解流加载,其实本篇文章我想说的是利用流加载的特性来进一步优化纹理带宽。例如,

1.unity原本默认计算出需要加载第0级的mipmap贴图,我们能不能手动设置成别的级别mimap

2.比如打开非全屏界面,能不能强制将游戏里面所有3D物体的mipmap都降到最低

答案是可行的,有了这两个特性,我们就可以自己灵活控制mipmap按需加载了。

首先我们需要在unity中开启texture streaming,如下图所示,在Quality Setting面板中勾选Texture Streaming,请注意Max Level Reduction这个数值比较重要,它的意思就是mipmap最多可以减小几级,最大是7.默认是2,如果是2的话,那么代码里怎么设置最多只能减少2级.

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四) - 雨松MOMO程序研究院 - 3

Max Level Reduction代码中也可以动态修改。

        //mipmap最多可以减少的等级, 默认是2 ,也可以在QualitySettings面板中设置
        QualitySettings.streamingMipmapsMaxLevelReduction = 7;

还需要在Editor Settings中勾选 Enable Texture Streaming,我的测试中即使勾选在编辑器下也未必能有正确的结果,所以大家一定要打出包在真机中查看。

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四) - 雨松MOMO程序研究院 - 4

如下代码所示,首先实例化一个Prefab,并且调用requestedmipmaplevel来设置默认加载mipmap的等级。

    void Start()
        mr = Instantiate<GameObject>(prefab).GetComponent<MeshRenderer>();
        t1 = mr.material.mainTexture as Texture2D;
        t1.requestedMipmapLevel = 5; //加载第5等级mipmap
        //t1.ClearRequestedMipmapLevel();可以清空加载mipmap的等级
    void OnGUI()
        GUILayout.Label("<size=50>desiredMipmapLevel" + t1.desiredMipmapLevel + "</size>");
        GUILayout.Label("<size=50>calculatedMipmapLevel" + t1.calculatedMipmapLevel + "</size>");
        GUILayout.Label("<size=50>loadedMipmapLevel" + t1.loadedMipmapLevel + "</size>");
        GUILayout.Label("<size=50>loadingMipmapLevel" + t1.loadingMipmapLevel + "</size>");
        GUILayout.Label("<size=50>minimumMipmapLevel" + t1.minimumMipmapLevel + "</size>");
        GUILayout.Label("<size=50>mipMapBias" + t1.mipMapBias + "</size>");

如下图所示,我们可以看到贴图已经被加载到第5级的mipmap了。此时你可能会有个疑问,如果摄像机拉远拉近是否会影响mipmap,答案是肯定的,只是摄像机拉到最近mipmap也只能5,但是摄像机如果拉远超过了5,那么就会自动适应6 、7级。

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四) - 雨松MOMO程序研究院 - 5

这里有几个数值的含义我需要解释一下,

calculatedMipmapLevel:表示当前摄像机与物体的距离计算出需要加载的mipmap等级。

desiredMipmapLevel:表示当前实际加载的mipmap等级,因为我们可能强制设置了加载等级,那么实际加载的可能就和calculatedMipmapLevel不一致了。

mipMapBias:表示加载加载mipmap等级的偏移量,比如原本加载的是0级的mipmap,由于mipMapBias设置了2,所以实际加载的就是2级,这个数值也可以设置成负数,比如原本需要加载2级mipmap,由于设置了-1,所以实际加载1级的mipmap,这样无疑就更浪费性能。

下面我们在代码里动态修改mipmap的等级。

        if (GUILayout.Button("<size=80>调整mipmap等级 +</size>"))
            t1.mipMapBias += 1f; //更模糊
        if (GUILayout.Button("<size=80>调整mipmap等级 -</size>"))
            t1.mipMapBias -= 1f; //更清楚

unity还提供了streaming controller组件, 需要绑定在摄像机上,其实就是对摄像机所看到的所有物体的mipmap做偏移和上面的用法类似。

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四) - 雨松MOMO程序研究院 - 6

我们既然能修改单个物体的贴图的mipmap能否统一修改所有呢?比如现在打开了一个半屏界面,可以将背景中的3D物体的贴图mipmap全部降低。因为UI并不会开mipmap所以即使降低也不会受影响。

        if (GUILayout.Button("<size=80>整体减低采样 -</size>"))
            //0表示正常尺寸
            //1表示降低1/2
            //2表示降低1/4
            //3表示降低1/8
            //4表示降低1/16
            QualitySettings.masterTextureLimit = 4;

在切回0级mipmap

Unity3D研究院之给每个贴图指定不同mipmap减低纹理带宽(一百一十四) - 雨松MOMO程序研究院 - 7
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK