Unity Batches与glDrawElements的关系

1)Unity Batches与glDrawElements的关系
​2)渲染大面积草地时,如何降低消耗
3)HUD随着摄像机偏移
4)Unity中如何在竖屏模式的UI之上显示强制横屏的UI
5)iOS能耗问题


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

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

Rendering

Q:Unity里用FrameDebug显示DrawCall是422个,Batches是435个,在SnapDragonProfiler里抓取出来glDrawElements数目是1449个,怎么理解这种差异?

A1:可参考以下几点:

  1. 题主的数据有一点问题,DrawCall数目应该大于等于Batches。根据特定的规则,多个DrawCall可以视为一个Batch,一个Batch中包含多个DrawCall,节省了一部分CPU提交数据的操作。
  2. 调用glDrawElements函数进行绘制就是DrawCall的一种形式。

感谢Vest@UWA问答社区提供了回答

A2:这部分可以参考UWA Day 2020里张强老师的分享 《Unity移动游戏项目优化案例分析(上)》中的第二小节。

感谢范君UWA问答社区提供了回答


Rendering

Q1:渲染大面积草地时,如何降低消耗?

A1:有以下几点:

  1. 使用DrawMeshInstance。
  2. 上面这个API是不会进行视距剔除,视锥体剔除和遮挡剔除的。

下面有两种方案:
a. 将草地按区域分组,用每组的中心点计算视距,依据距离切换网格LOD或剔除。
还能用向量点乘简单剔除在相机后方的草地。(注意临界问题)
b. 借助CullingGroup。
CullingGroup.onStateChanged事件绑定,通过事件触发调整传入DrawMeshInstanced的Matrix顺序和渲染数量。(比较难的是,DrawMeshInstanced只能指定渲染前几个Matrix)

通过cullingGroup.SetBoundingSpheres实现视锥体剔除和遮挡剔除;
通过cullingGroup.SetBoundingDistances实现视距剔除和LOD;
这个方案也最好进行区域分组,不然CullingGroup的事件监听占用会比较高。中端机上4000个监听会占约2ms。

以后如果有对比两种方案的性能,我再进行补充。
附:
CullingGroup API的使用说明

Unity 3D研究院之Lightmap支持GPU Instancing

如何高效使用GPU Instancing技术来进行草丛渲染

升级Unity 2018过程中,遇到的DrawMeshInstanced不生效的问题

感谢题主李先生@UWA问答社区提供了回答

A2:可以使用Indirect模式的Instancing,配合ComputerShader实现视锥剔除和遮挡剔除。

感谢邹春毅@UWA问答社区提供了回答

Q2:手机上能用吗?

A:推荐一个使用URP制作的草海效果,亲测可在Mobile端使用。
Unity URP Mobile Draw Mesh Instanced Indirect Example

性能测试:

  • can handle 10 million instances on Samsung Galaxy A70 (GPU = adreno612, not a strong GPU), 50~60fps, performance mainly affected by visible grass count on screen(draw distance = 125)
  • can handle 10 million instances on Lenovo S5 (GPU = adreno506, a weak GPU), 30fps, performance mainly affected by visible grass count on screen(draw distance = 75)

感谢Vest@UWA问答社区提供了回答


Rendering

Q:HUD为什么会随着摄像机偏移?

代码如下:

    Vector3 midVe1   = obj.transform.position;
    Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1 + new Vector3(0, 2, 0));
    viewPose.x *= Screen.width;
    viewPose.y *= Screen.height;
    hud.anchoredPosition = viewPose; 

图片如下:

Unity Batches与glDrawElements的关系_第1张图片

Unity Batches与glDrawElements的关系_第2张图片

Unity Batches与glDrawElements的关系_第3张图片

A:题主说的这个现象是合理的。

Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1);
Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1 + new Vector3(0, 2, 0)); 

这两个算出来的viewPos.x是不一样的。所以可以改成先算x,再偏移y。大概是这样:

Vector3 midVe1 = obj.transform.position;
Vector2 viewPose = worldCamera.WorldToViewportPoint(midVe1);        
viewPose.x *= Screen.width;
viewPose.y *= Screen.height;
viewPose.y += 130;
hud.anchoredPosition = viewPose; 

这里面y方向上的偏移值(130)就要自己调整了。

感谢Xuan@UWA问答社区提供了回答


UGUI

Q1:针对Unity中如何在竖屏模式的UI之上显示强制横屏的UI,我们现在有两种思路:

1. 改Screen.orientation之后再显示。
- 需要考虑外层RectTransform是否已经“完成”了转屏导致的变化,然后才能去适配内层。尚未找到可靠的事件。
- 需要处理背景,半透明背景可能透出后面的UI排版混乱。

2. 将新UI旋转依z轴转-90度显示。
- 对刘海屏的适配需要做相应的修改。
- 无法转屏(因为还是竖屏,且不支持Upside Down)。但是这个应该可以克服。

请问,两者哪种更好呢?

A1:一个简单的方式:
Canvas的RenderMode改成World Space,管它摄像机怎么裁剪,UI画布也不会发生变化。

如果自适应不了,在Canvas上根据你自己的需求做好自适应就好。

感谢郑骁@UWA问答社区提供了回答

A2:我们之前项目里刚好有个类似的需求,在横屏游戏中,个别玩法切换到竖屏。

当时采用的解决方案是修改Screen.orientation,同时修改UI全局根节点上的CanvasScaler的referenceResolution(竖屏时设置为1080,1920,横屏设置为1920,1080),matchWidthOrHeight(和项目的适配策略相关,竖屏时设置为0,横屏时为1),切换时会有一个全屏遮罩的fade效果,来避免切换时显示错误。

感谢范君@UWA问答社区提供了回答

Q2:请问为什么不考虑转90度的做法?

A:我们当时没有测过旋转的方案。现在大概想想有两个问题,不知道想的对不对。

  1. 单纯旋转角度应该只能在横屏的中间部分显示一个竖屏界面吧?感觉还需要其他处理。
  2. 对工作流是否有影响,如果要一个竖屏界面是否是要在制作时先做成一个横屏界面?内部控件是否要处理?例如最终呈现出竖屏效果的剧情文字从左到右出现,旋转之前应该是横屏的从下到上出现,同时每个单一文字也是旋转的。

感谢范君@UWA问答社区提供了回答

Q3:RectTransform的尺寸会被延迟修改这件事, 没有造成什么问题么?

A:记不太清了,印象中没有遇到什么大问题。不过我们当时这类需求切换时都是全屏UI,加了遮罩Fade后没有什么明显的穿帮。

感谢范君@UWA问答社区提供了回答


iOS

Q:我在Xcode真机调试中发现能耗始终处于Very High,而我项目的基本情况是(每帧GPU渲染3.5ms,每秒60帧,渲染分辨率为750*1624,设备iPhone XS,场景中只有UGUI没有其它3D或2D物体),麻烦大家帮忙看看这个电量消耗属于正常吗?还有没有优化的空间(帧数和分辨率估计是没有办法下降了,会影响项目效果),谢谢。

Unity Batches与glDrawElements的关系_第4张图片

Unity Batches与glDrawElements的关系_第5张图片

A1:测试过空场景Average Energy Impact也会显示Very Hight(如下图),题主的GPU能耗占比较大说明CPU相对而言还是压力比较小的:

Unity Batches与glDrawElements的关系_第6张图片

这个帖子也是类似的问题,看上去是因为Unity引擎本身相对于普通APP体量较大,所以能耗表现较高,降低目标帧率会有所改善。

Unity Batches与glDrawElements的关系_第7张图片

Energy Impact表征的是全局(非单个APP)的能耗,而且测试的时候USB连接充电是如何在统计中避免的不太清楚机制,建议使用Xcode Instruments工具的Energy Log模版测一下单个APP的能耗情况。
Energy Log文档

关于能耗的优化先关注整体的是GPU还是CPU(或其他方面),GPU的用FrameCapture定位一些比较高耗时的Shader进行优化,CPU结合Time Profiler模版定位一些高能耗的CPU Bound。

感谢羽飞@UWA问答社区提供了回答

A2:GPU的用FrameCapture定位一些比较高耗时的Shader进行优化。
关键是Capture哪一帧,这个会很盲目。

感谢山中千年@UWA问答社区提供了回答

封面图来源于网络


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

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

你可能感兴趣的