Lightmap平台色差问题解决方案

Lightmap平台色差问题解决方案

本期我们聚集了这些话题:Lightmap平台色差问题解决方案、使用Post Processing V1出现曝光错误、Unreal地形制作、Mono堆内存占用定位...


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

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


渲染

Q:其实问题已经解决,为了大家不用再踩这个坑,所以写下来方便大家搜索。

问题描述
PC上烘培,转到安卓平台即可复现,但非所有项目都会遇到这个问题。我使用的Unity版本是2017.4.6f1,经过测试2017.4.14f1依然存在这个问题。色差具体表现有亮度降低和色相变化。

经过了一个通宵,排除了贴图格式、项目设置、烘培设置、灯光参数等,最终确定改了问题原因:

问题原因
Android不支持完整的exr(HDR)格式,在Swith To Android的时候,Unity会把Lightmap自动转换成一种低精度格式(并且不给任何选项和提示)。重点是这个格式转换并不是完整映射,而是类似拷贝过去的形式,转换过程会有数据溢出的问题。

变暗很好理解,而色相问题可以这么理解:假设光照贴图是一个(2000,255,0)的近乎大红的橙色,经过一个有数据溢出的格式转换后,会变成(255,255,0)的纯橙色,这就是我们看到色差的原因。

解决方案探索
在解决问题的过程中搜索到了这两个链接:
Unity填坑笔记(二):移动设备上烘焙变暗问题
解决方案:测试出Lightmap数据unity_Lightmap_HDR,然后使用这个参数直接调用源码中的HDR解码算法,并将贴图转成ETC(2)_RGB4。

这个方案我持否认态度,首先Alpha通道是用于保存亮度倍数的,抛弃了必定是有损的;其次,unity_Lightmap_HDR是根据不同烘培结果定的一个值,不是每个场景都固定的。就是说,即使要这个方案也不是这么做的,Alpha通道不能丢弃,unity_Lightmap_HDR还是得读原来的值,否则换个场景就又有色差了。

Lightmap在PC上与iOS和Android上表现不同的问题
http://www.ceeger.com/forum/read.php?tid=24457&fid=2&page=1
解决方案:拿原图重新编码,游戏中使用重新编码的贴图进行解析。这个方案比较靠谱,但是我这边测试做不出来。

问题的进一步分析
我这边已经基本上完全排除其他因素了,用新建的项目和同样的贴图、灯光对象,全部使用默认设置来烘培,这东西确实是引擎Bug,但有些项目却不会遇到,这让我感到困惑,这一定是制作流程问题。

通过对比我发现,我们场景的贴图画得非常暗,而烘培使用了高亮度的烘培,而这个做法是不合理的,贴图那么暗(基本压到0.4以下),是一种严重的精度浪费,而光照强度也很不符合正常环境的亮度。

所以如果没特殊原因,应该所有出问题的项目都是因为低亮度贴图强行烘亮的操作引起的。

我个人认为最科学的解决方案
以正常亮度为标准来绘制贴图,以符合现实世界强度的灯光参数来烘培场景,就不会出现这个问题了。不需要重新编码,也不需要实时解码,就可以避开这个引擎问题。但是如果有高亮度烘培的硬性需求的话,就必须自行编码重新保存了。

贾伟昊:解释两句。
题主说的那个Unity填坑笔记后来整理了一份更加详细的文档:https://zhuanlan.zhihu.com/p/28728151

解释几点:
1)这个文章要fix的问题是Unity 5.x版本中对于线性空间烘焙处理的一个Bug,这个Bug在2017版本中已经修复了,所以和题主遇到的问题并没有直接关系。所以文章的解决方法并不能解决题主的问题。

2)赞同Alpha通道抛弃掉必定有损,但是这是我们自己项目在观察美术烘焙好的lm贴图的alpha通道之后得出的结论——我们项目中lm贴图占用的内存尺寸和Alpha通道带来的效果提升相比,我们更倾向于损失后者,所以这是个取舍问题,建议各个项目根据自己的具体情况来定,所以并不赞同题主认为的不能丢弃的观点。

3)使用unity_Lightmap_HDR的确不正确,在我们项目中效果“正确”也只是看上去一样而已。我们也已经更改为2017 fix的正确答案:

inline half3 DecodeLightmapDoubleLDR( fixed4 color )
{
float multiplier = IsGammaSpace() ? 2.0f : GammaToLinearSpace(2.0f).x;
return multiplier * color.rgb;
}
inline half3 GammaToLinearSpace (half3 sRGB)
{
// Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return sRGB * (sRGB * (sRGB * 0.305306011h + 0.682171111h) + 0.012522878h);

// Precise version, useful for debugging.
//return half3(GammaToLinearSpaceExact(sRGB.r), GammaToLinearSpaceExact(sRGB.g), GammaToLinearSpaceExact(sRGB.b));
}

另外,美术把贴图做得亮度偏低,然后通过打光来提高整体亮度,貌似很多美术喜欢这种做法...我们美术在制定了线性空间下标准贴图亮度为187左右的情况下,还有部分场景故意使用较暗的贴图亮度。也可能是我们没给场景开HDR的原因。

感谢贾伟昊@UWA问答社区分享了该问答

欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5be90015a8b4ee66df69c250


渲染

Q:之前项目用PPS V1的Tonemapping时一直用的Neutral模式,最近感觉很多时候ACES调出的效果更好些。在PC端没问题,在手机上测试的时候发现ACES时在亮度很大的时候可能会出现奇怪的曝光效果。

试了骁龙820,845都出现这个问题,但是麒麟970反而没问题。类似下图:
请输入图片描述

Tonemaping的设置:
请输入图片描述

反复查了很多遍,现在比较怀疑出问题的地方,是在ACES生成的FP16的LUT图,这个图的生成或者读取的数据似乎就出现了问题,在亮度最高的地方。但是就是查不到具体是哪块出的问题,Git上下了最新版的v1也还是有这个问题,而且不管HDR设置成FP16还是R11G11B10都一样出现,不知道大家是否碰到类似的问题,怎么解决?

A:建议单独提取ACES而不是混在LUT里,ACES库存在浮点数精度问题,进行FilmicACES的RGB通道值不要超过100,一般half小数点后有3位有效精度,超过100很容易丢失小数精度出现问题。 如果同时使用LUT可能略有出入。总之相当于 color = clamp(color, 0.0, 100.0) 后再执行TonemapFilmic(color, exposure)

感谢蒙占志@UWA问答社区回答了该问题

题主:的确是精度的问题,说下这个问题的定位思路:
先排除bloom的影响,反复调整bloom参数,在不用tonmaping前都不会出现这个问题。

使用ColorGrading流程:生成HDR的LUT,采样LUT进行映射。采样代码很简单,测试下没发现问题。用snapdragon查看移动端LUT,发现在高亮处出现了颜色缺失,怀疑是生成的LUT就有问题了,颜色越界或者NaN?
请输入图片描述
再检查ACES tonemapping生成LUT的流程:sRGB转ACES -->ACES中分别在LogSpace和Linear Space中进行颜色调整–>ACES中进行tonemapping -->ACES转回sRGB

分别注释几部分的代码观察问题变化,最后在第3部分color = FilmicTonemap(aces)前限制ACES的范围,aces =clamp(aces,0,4)(没错移动端安全范围就这么小…),这时候的LUT已经显示正常了,问题解决。

感谢题主分享了该经验
该回答来自UWA问答社区,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5be544557b7ea841f3724a73


虚幻&渲染

Q:请问Unreal在手机上是直接用Landscape还是说把Landscape转成StaticMesh呢?如果用Landscape,Section选择11还是22,顶点数有什么好工具可以优化吗?

UWA:建议直接使用Landscape,我们之前在手机上做过测试,红米2这样的低端设备直接使用landscape也是没有问题,加上地表静态植被,配合动态场景加载,渲染效率可以到30多帧。landscape的Section是LOD的变化单元,增加Section数量会增加DrawCall,同时LOD变化计算开销也会增加。但是,更细粒度的LOD可以使得效果更好。如果从效率上考虑选择1x1,如果从效果上考虑可以选择2x2。顶点数量可以直接在做地形的时候进行设置,quad的数量主要决定了一块地形里顶点数。最后总的顶点数是componentCount x sectionPerComponent x quadPerSection + 1。默认情况下,顶点与顶点之间x,y轴上距离是1m,因此顶点数量越多地形覆盖面积越大。不过也可以通过调整Scale来控制大小。

地形在内部存储的时候是使用的Heightmap的形式,也就是一张高度图。每个像素(32bit x 1)代表一个顶点,而Staticmesh则需要使用x,y,z三个坐标(32bit x 3),同时可能需要Tangent,UV坐标等,所以在存储方面地形会比Staticmesh要节省不少,这可能是使用地形的好处之一。再就是如果是用地形的Foliage刷地表物体,它默认是用了ISM(Instanced Static Mesh)这样效率会高一些,当然如果用StaticMesh自己做也可以的,直接用地形的Foliage就稍微方便一点。LOD的设置,我了解到的就是设置Section了,希望能对您有所帮助。

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


堆内存

Q:关于Unity的Mono内存,我有两个问题:
1)堆内存占用突然上涨30多MB,请问可能是由哪些因素引起的?
第一次测试

请输入图片描述

第二次测试
请输入图片描述

2)Mono堆内存占用与逻辑代码堆内存分配有什么关系呢?UWA提示都显示通过”Mono详细堆内存分配报告“来进行优化,但是为什么逻辑代码堆内存分配降低了,但是Mono堆内存占用反而上升了呢?

UWA:无论是测试一还是测试二,从一开始就Reserverd Mono较高(蓝色线条)且Unused Mono(黄色线条)同样较高,这个是典型的游戏启动时大量堆内存分配且马上被GC掉所致。一般来说,这种情况常见于文件解压、配置文件加载所致。题主可以查看在游戏启动时是否确实存在如此操作。

Mono堆内存的占用多少,其实就是你项目中逻辑代码的堆内存分配所致,只是有些会被GC回收,有些不会而已。对于第二次Mono从一开始就占据较高的堆内存,主要还是查看是否游戏启动时有大量文件的热更新、大量配置文件的加载。

由于你们的Mono堆内存是从一开始就升起的,建议题主可以直接通过UWA GOT工具的Direct模式来进行检测,该模式可以从游戏启动时进行记录,从而可以直接抓取到详细Mono堆内存是由哪些具体代码进行分配的,进而对其进行完善。

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


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

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