本地资源检测功能使用疑问

本地资源检测功能使用疑问

1)本地资源检测功能使用疑问
2)大型Mesh的渲染开销
3)API的使用
4)关于在Android与iOS平台上的Z-fighting问题
5)如何获取当前帧逝去的时间


​这是第194篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Asset

Q:我们使用了本地资源检测这个功能后,想问下有几个维度为什么会出现在检查规则内:
1)使用Tiled模式的Image组件
2)精度过高的动画片段(Force Text只对编辑器有效果、二进制的情况下,Float是4个字节,精度应该没有区别)
3)未使用PCM格式的音频
4)Wrap模式为Repeat的纹理

A:以下是几个规则的解释,后续也会放到网页上进行显示。
1)使用Tiled模式的Image组件:Tiled模式的Image组件可能产生过多的面片,建议进一步检查。
2)精度过高的动画片段:建议把精度缩减到3-4位,从而降低动画片段的内存占用。
3)未使用PCM格式的音频:未使用PCM格式的音频可能存在音质问题,建议进一步检查。
4)Wrap模式为Repeat的纹理:Wrapmode使用了Repeat模式,容易导致贴图边缘出现杂色,建议进行检查。

部分规则确实只是起到提醒作用的,即使不符合规则也有可能是合理的;目前规则也在完善和调整中,感谢使用。

该功能已于3月6日正式上线,欢迎大家前来使用UWA本地资源检测

以下是降低动画精度的相关文章《Unity动画文件优化探究》,可以参考。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e53a02886a395489198397e


Mesh

Q:请教一个问题:比如,有一个大型的Mesh,8000个三角面,尺寸大概有三四屏。把它绘制到屏幕,Statistic显示的是8000个三角面,而实际绘制到屏幕的只有2000个三角面。

那么其余6000个三角面会不会被视口剔除掉,还是根本就没有视口剔除,直接绘制到屏幕外?所以虽然只有2000个有效面的渲染,但开销和8000个面是一样的。是这样吗?

光栅化/Fragment会不会跳过屏幕外的点?还是另有说法。

如果有视口剔除,那么视口剔除的本身的开销又有多少?需不需要把这种大型的Mesh手动切成小块?

A:如果是这样大型的物体,是非常建议切成小块来进行渲染的。

视域剔除在默认渲染管线中是一定会做的,但它剔除的基本并不是以Triangle为单位,而是以GameObject为单位的,只要这个GameObject(Mesh)的包围盒与视域体有交集,就会被认为是要渲染的,所以会将其Triangle全部放入Draw Call中,并传入GPU进行渲染。因此,你会看到Statistics中显示的三角面片是很高的。

在传入到GPU后,该Draw Call中所有的顶点都会进入Vertex Shader阶段,所以如果该Mesh是8000个Triangle,那么这8000个都要进行计算,从目前我们所优化过的游戏项目(特别是重度MMO)中可以发现,很多时候都是几万的网格顶点在这里进行计算,在中低端设备上都会造成不小的计算量。

而你问题中说的光栅化实际上是在Vertex Shader之后,也就是要到Fragment Shader阶段,这个时候是在经过一系列操作(比如深度检测等)后将Triangle向屏幕进行投射,然后通过光栅化进行颜色计算,再经过各种Buffer之后形成最终的FrameBuffer,也就是我们最终看到的内容。

渲染Pipeline大体上是上述过程,所以通过上面的说明,题主就可以明白,光栅化和视域剔除其实没有关系,前者是GPU部分,后者是CPU部分,功能是完全不一样的。光栅化主要根据每个Triangle在屏幕上的投影来计算颜色,跟Mesh中Triangle的前期剔除是没有关系的。Statistic中的统计没有问题,因为确实有一定数量的Triangle被传入到了GPU中。如果你的项目遇到了这种情况,那么我建议将其切成小块来进行渲染,因为从概率上来说,它可以降低渲染计算压力。希望上面的说明能对你的疑惑有所帮助。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e5f665a4149350dbc5aea7c


Rendering

Q:项目用的Addressable准备做常规的启动更新检测,但是Addressable.CheckForCatalogUpdates这个API返回的更新目录长度永远是0。

已经取消了启动静默更新,并且把发布时的资源进行了修改后打包到了服务器,对比过Catalog本地和服务器的确实不一样,应该怎么解决这个问题呢?

A:在AddressableAssetSetting内勾选Disable Catalog Update On Startup。
然后用下面的代码做了测试:

var initHandle = Addressables.InitializeAsync();
yield return initHandle;
var handler = Addressables.CheckForCatalogUpdates(false);
yield return handler;
var catalogs = handler.Result;
Debug.Log($"need update catalog:{catalogs.Count}");
foreach (var catalog in catalogs) {
   Debug.Log(catalog);
}

if (catalogs.Count > 0) {
    var updateHandle = Addressables.UpdateCatalogs(catalogs, false);
    yield return updateHandle;
    var locators = updateHandle.Result;
    foreach (var locator in locators) {
        foreach (var key in locator.Keys) {
            Debug.Log($"update {key}");
        }
    }
}
Addressables.Release(handler);

我这里是可以更新到Catalog的。一般来说,做启动更新检测只要使用Download相关的2个函数就可以了,大致如下:

var sizeHandle = Addressables.GetDownloadSizeAsync(keys);
yield return sizeHandle;
long totalDownloadSize = sizeHandle.Result;
if (totalDownloadSize > 0) {
    var downloadHandle =  Addressables.DownloadDependenciesAsync(keys, Addressables.MergeMode.Union);
    while (!downloadHandle.IsDone) {
        float percent = downloadHandle.PercentComplete;
        Debug.Log($"已经下载:{(int) (totalDownloadSize * percent)}/{totalDownloadSize}");
    }
}

一般的流程是Addressable.InitializeAsync做初始化,这个时候其实是会去下载最新的Catalog的,然后通过GetDownloadSizeAsync从本地最新的Catalog计算需要下载的大小,再调用DownloadDependenciesAsync(内部实际调用LoadAssetsAsync)去下载AssetBundle。

在游戏运行期间如果服务器上有更新了,那么这个时候去调用CheckForCatalogUpdates,则可以发现有Catalog的更新,然后调用UpdateCatalogs去更新Catalog,但是只是更新,实际并没有下载资源,再走Download的流程去完成不重启更新。需要注意更新前把正在使用的AssetBundle等资源释放掉。

感谢黄程@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e3b7fe686a39548919837e1


Addressable

Q:Unity 2018.1.6f1,相机近、远裁剪平面分别为1和400,遇到如下问题:
在Android手机上Z-fighting严重,但是iOS设备上没有问题。
即便我使用Standard Shader仍然如此。
难道是因为远裁剪平面400太大了?为什么两个平台差距这么大?是不是哪里设置错了?
我用GPA查看深度缓冲,截图如下:

A:有可能是计算精度问题导致的,这个在不同的驱动中会出现问题。
建议调整视域体,特别是近平面的位置,然后看看是否达到效果。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e5cfa974149350dbc5aea70


Script

Q:网络消息是在非主线程上处理的,计算延时用的都是系统时间,但是我需要换算回Unity时间才能使用。请问如何才能获取代码执行处理当前帧开始过去了多久的时间呢?

A:其实一帧的最开始是FixedUpdate,然后可能有多个FixedUpdate正在处理,很难知道具体哪个是第一个,而且FixedUpdate的处理时机和Update不一样,不能做计时的参考点。那么是不是可以反过来考虑:一帧的最开始约等于上一帧的结束,一次循环处理的最后是yield WaitForEndOfFrame。可以准备一个协程:

IEnumerator EndOfFrame() {
    yield return new WaitForEndOfFrame();
    //记下当前帧结束的时间,给下一帧用
}

然后在其它Update的地方和这个时间做比较。虽然未必非常准确,但是也可以试试。

感谢黄程@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e5f1e2b4149350dbc5aea7b


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)