问答 GPU Skinning 旋转指定骨骼

侑虎科技 · 2021年04月12日 · 32 次阅读

1)GPU Skinning 旋转指定骨骼
​2)关于 UGUI 画布重建以及动静分离
3)如何设定游戏内存的峰值来保证不闪退
4)使用 UsePass 遇到 Keyword 丢失的问题
5)Unity Shader Built-in 里面宏的定义


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

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

Rendering

Q:使用 GPU Skinning 的物体,类似头需要面对角色,如何旋转指定骨骼呢?现在的想法是传入一个 Matrix 和骨骼编号,Shader 里再处理,但是算出来都是 0 点位置。反编译了同样实现的项目,传入的是个骨骼位置 float3 和骨骼旋转 float4 的变量(看命名是这个意思)。

A:实现思路就是在骨骼矩阵计算时将 LookAt 的旋转应用到 Neck Bone 及其 Child Bone 上(若骨骼矩阵计算也是在 Shader 中实现的,而不是离线预计算好的,则不考虑 Child Bone)。

不知道题主的 GPU Skinning 是如何实现的,以chengkehan/GPU Skinning的开源库为例做了个简单的测试。

将小球的旋转矩阵传入 Shader:

采样预计算骨骼矩阵时将旋转应用到 Neck 骨骼以下(boneindex > 21)所有骨骼节点计算(PS:子骨骼节点的计算是有问题的,因为矩阵没有交换律,请自行处理。)

效果如下图:

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


UGUI

Q:关于 UGUI 画布重建以及动静分离,经验证 UI 元素的大小位置变化并不会引起画布网格重建的问题?

看到很多 UWA 回答说 UI 元素的大小位置变化会引起重构,但是经过 UGUI 研究发现并不会。UGUI 引起重绘元素集合是 CanvasUpdateRegistry 类中的,如下图:

通过反射将网格重绘列表打印出来之后发现位置、大小和旋转都不会进入重建列表,而 Anchors、Pivot 还有组件的 Color 和宽高改变才会引起网格重构,请问怎么回事?

A1:可以看看 OnRectTransformDimensionsChange 函数、VertexHelper 类的代码、位置和 Rect 大小等,这些的改变都会 SetDirty 的。

VertexHelper 里的几个 List,其实是对应的材质属性和顶点位置属性;按理说这些改变都是会 Setdirty 的,但是从 RectTransform 的一些属性来看,Unity 可能只在 Pivot、SizeDelta、AnchorMin 和 AnchorMax 这四个属性更改时才会 Setdirty。

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

A2:可以按照李星的说法做个验证,写一个 MyImage 继承自 Image,然后 Override 里面的 OnRectTransformDimensionsChange,如下图:


在场景中使用 MyImage,就可以验证更改 Anchor 的位置或者 Rect 的宽高会触发 OnRectTransformDimensionsChange(这个消息是从 Native 层发回来的),触发这个是会触发 SetVerticesDirty 的。修改 Position 和 Scale 这些是不会触发 SetVerticesDirty 的,修改颜色是会触发 SetMaterialDirty。这两种 SetDirty 都会将对应的元素加入到 GraphicRebuildQueue 里面,最终的耗时都会统计到 Canvas.SendWillRenderCanvases 里面。这部分的 “重构” 并不是指网格重构,这个 “重构” 可以理解为 UI 元素本身的更新。

另外一种网格重构,是指 UI 变动导致了 Canvas 重建,就是会在 Native 层进行网格拼合,也就是会触发调用 Canvas.BuildBatch。可以参考:
https://answer.uwa4d.com/question/59af579c6a071c595e8994b2#59af5d466a071c595e8994b4

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

A3:网格重建(Canvas.BuildBatch)是在 Native 层做的,用 Rider 看不到里面内容。修改 Position 会触发重建,是 Unity 官方文档里明确写出来的。

这是我之前对各种文章做的一些总结《UGUI 优化 - 知识点》,可以参考:
https://www.jianshu.com/p/5c44eb589751

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

Memory

Q:不同内存的安卓与苹果机型上(1G、2G、3G、4G…),游戏内存的峰值一般最高多少能保证不闪退?

A1:这里有人会持续更新 iOS 上的内存峰值,帖子下面还有人发了一个测试峰值的工具:
https://stackoverflow.com/questions/5887248/ios-app-maximum-memory-budget

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

A2:做一个搬运工,感谢Jasper的测试数据提供,我将其放到这里,方便大家直接查看:

device: (crash amount/total amount/percentage of total)
iPad1: 127MB/256MB/49%
iPad2: 275MB/512MB/53%
iPad3: 645MB/1024MB/62%
iPad4: 585MB/1024MB/57% (iOS 8.1)
iPad Mini 1st Generation: 297MB/512MB/58%
iPad Mini retina: 696MB/1024MB/68% (iOS 7.1)
iPad Air: 697MB/1024MB/68%
iPad Air 2: 1383MB/2048MB/68% (iOS 10.2.1)
iPad Pro 9.7": 1395MB/1971MB/71% (iOS 10.0.2 (14A456))
iPad Pro 10.5”: 3057/4000/76% (iOS 11 beta4)
iPad Pro 12.9” (2015): 3058/3999/76% (iOS 11.2.1)
iPad Pro 12.9” (2017): 3057/3974/77% (iOS 11 beta4)
iPad Pro 11.0” (2018): 2858/3769/76% (iOS 12.1)
iPad Pro 12.9” (2018, 1TB): 4598/5650/81% (iOS 12.1)
iPad 10.2: 1844/2998/62% (iOS 13.2.3)
iPod touch 4th gen: 130MB/256MB/51% (iOS 6.1.1)
iPod touch 5th gen: 286MB/512MB/56% (iOS 7.0)
iPhone4: 325MB/512MB/63%
iPhone4s: 286MB/512MB/56%
iPhone5: 645MB/1024MB/62%
iPhone5s: 646MB/1024MB/63%
iPhone6: 645MB/1024MB/62% (iOS 8.x)
iPhone6+: 645MB/1024MB/62% (iOS 8.x)
iPhone6s: 1396MB/2048MB/68% (iOS 9.2)
iPhone6s+: 1392MB/2048MB/68% (iOS 10.2.1)
iPhoneSE: 1395MB/2048MB/69% (iOS 9.3)
iPhone7: 1395/2048MB/68% (iOS 10.2)
iPhone7+: 2040MB/3072MB/66% (iOS 10.2.1)
iPhone8: 1364/1990MB/70% (iOS 12.1)
iPhone X: 1392/2785/50% (iOS 11.2.1)
iPhone XS: 2040/3754/54% (iOS 12.1)
iPhone XS Max: 2039/3735/55% (iOS 12.1)
iPhone XR: 1792/2813/63% (iOS 12.1)
iPhone 11: 2068/3844/54% (iOS 13.1.3)
iPhone 11 Pro Max: 2067/3740/55% (iOS 13.2.3)

同时,Slyv也对上述测试数据做了一个更为宏观的总结,具体如下:
device RAM: percent range to crash
256MB: 49% - 51%
512MB: 53% - 63%
1024MB: 57% - 68%
2048MB: 68% - 69%
3072MB: 63% - 66%
4096MB: 77%
6144MB: 81%

Special cases:
iPhone X (3072MB): 50%
iPhone XS/XS Max (4096MB): 55%
iPhone XR (3072MB): 63%
iPhone 11/11 Pro Max (4096MB): 54% - 55%

该问答由 UWA 提供

Shader

Q:为了共用 Shader 代码,把若干个 Pass 写在一个单独的 Shader A 中,在 Shader B 中用 UsePass 关键字组合成不同的 SubShader。

在 PC 上没什么问题,但是打包 AssetBundle 以后发现里面的 Keyword 丢失。我们是通过 ShaderVariantCollection 搜集的变体,并且确定 ShaderVariantCollection 含有需要的 Keyword,如果不用 UsePass 直接写代码,Keyword 没有丢失。

请问是否不能通过这种方法来共用含有 Keyword 的 Shader 代码?

A1:猜测楼主使用 AssetBundle 的方法过程中,没有先加载共用的 Shader A,只加载了 Shader B 和 SVC,所以导致 Shader 解析的时候缺失了共用 Shader A 的 Pass 信息,而 PC 上没问题是因为所有的 Shader 资源都在。

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

A2:我在这个网址(https://forum.unity.com/threads/keywords-and-usepass.324180/)上看到了类似你的问题,他们回答说是 UsePass 后面跟的 Pass 名字必须是大写。

感谢安日天@UWA问答社区提供了回答

A3:需要解析出来引用的 Shader,并根据变体列表分析出引用 Shader 的变体列表,添加到 SVC 里面。

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

A4:测试后(打包成 exe 测试)发现,把 SVC 文件和 Shader 打包在一个 AssetBundle 里面,使用 UsePass 也不会出现 Keyword 丢失的问题的。如果 SVC 和 Shader 分开打包,就算不使用 UsePass 也会出现 Keyword 丢失的问题。

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

Shader

Q:Unity Shader 里面的 UNITY_FAST_COHERENT_DYNAMIC_BRANCHING 宏,表示什么意思?什么时候这个宏是 true?

A:在 HLSLSupport.cginc 中有定义:

#if (SHADER_TARGET < 30) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLES) || defined(SHADER_API_N3DS)
    //no fast coherent dynamic branching on these hardware
#else
    #define UNITY_FAST_COHERENT_DYNAMIC_BRANCHING 1
#endif 

比如下面这段代码,这些条件判断也决定是否使用 UNITY_BRANCH 来跳过 if 为 false 里面的代码:

half UnityDeferredSampleRealtimeShadow(half fade, float3 vec, float2 uv)
{
    half shadowAttenuation = 1.0f;

    #if defined (DIRECTIONAL) || defined (DIRECTIONAL_COOKIE)
        #if defined(SHADOWS_SCREEN)
            shadowAttenuation = tex2D(_ShadowMapTexture, uv).r;
        #endif
    #endif

    ++
**
#if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)
    //avoid expensive shadows fetches in the distance where coherency will be good
    UNITY_BRANCH
    if (fade < (1.0f - 1e-2f))
    {
    #endif
**
++

        #if defined(SPOT)
            #if defined(SHADOWS_DEPTH)
                float4 shadowCoord = mul(unity_WorldToShadow[0], float4(vec, 1));
                shadowAttenuation = UnitySampleShadowmap(shadowCoord);
            #endif
        #endif

        #if defined (POINT) || defined (POINT_COOKIE)
            #if defined(SHADOWS_CUBE)
                shadowAttenuation = UnitySampleShadowmap(vec);
            #endif
        #endif

    #if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)
    }
    #endif

    return shadowAttenuation;
} 

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


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

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

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