问答 Unity Batches 与 glDrawElements 的关系

侑虎科技 · 2020年10月14日 · 1733 次阅读

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;

图片如下:

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 物体),麻烦大家帮忙看看这个电量消耗属于正常吗?还有没有优化的空间(帧数和分辨率估计是没有办法下降了,会影响项目效果),谢谢。

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

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

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(原群已满员)

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册