GPU Skinning旋转指定骨骼

GPU Skinning旋转指定骨骼

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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/601009a410a17c6c2b09dcf0


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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/600e8d0310a17c6c2b09dc5c


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提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5b8e2f5f339d267d357c6eda


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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d32da6a70a3811865f79387


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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6014c9f910a17c6c2b09dd20


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

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