原文地址:深入理解NGUI(Unity)显示特效的问题
粒子特效显示差异引出问题
游戏开发时常会需要在UI里显示一个特效,我们将特效在UI里显示出来后,发现效果不符合预期。该特效在普通3D场景下,效果如下:
图1
但在UI里效果偏差比较大:
图2
该特效是由多个粒子器组合而成。对于显示差异,我们对比了其中一个粒子器在渲染时的Mesh细节:
图3
如上图左边是正确效果,右边是UI中错误效果。原本怀疑粒子系统的运动坐标系是否参照World而非Local导致观察运动方向不同,但查看各粒子属性和坐标Gizmo排除此可能性。
经过一天的努力,找到上述问题的本质原因。但让我们先回顾一些知识。
粒子渲染
美术在制作粒子时通常偏好使用Additive Shader,这中渲染方式适用于大部分明亮的例如火焰型、光感型特效。
其主要计算法则如下:
当Texture使用通道时:
SrcBlend = SRC_ALPHA;
DestBlend = ONE;
BlendOp = ADD;
当Texture不使用通道(通常已经预先乘到RGB分量中):
SrcBlend = ONE;
DestBlend = ONE;
BlendOp = ADD;
不难看出,该方式的最终效果受影响。因此在光亮度(luminance)不同的背景下会呈现不同的显示效果。显然,图1、2两个特效的
相近,故排除此种可能。
NGUI的UI坐标系详解
UI的工作一般是从这样一个环境下开始:
图4
UIRoot的Hierarchy和Inspector如下图:
图5
图4中的orthographic摄像机的属性是:
不难得出该摄像机的可视区域是2 X 3 X 29(World坐标系)的立方体,大小不会随着Camera GameObject的Hierarchy层级关系改变而变化。此摄像机的宽是高的1.5倍(宽高比3:2)。
图4中的红色框即NGUI里Panel的Gizmo。可认为是Camera的投影平面。显然该Gizmo的大小在World下为2 X 3。那如何表示该投影坐标系下的960 X 640范围呢?NGUI巧妙利用空间坐标系转换来实现这一点。
在图5中,UIRoot是一切UI Hierarchy的根。如果我们通过B中设置投影平面的Height,可以发现相应的UIRoot的Transform的Scale在对应改变,同时红色Gizmo的大小不发生任何改变。阅读UIRoot的代码可以看到,UIRoot每帧都会主动调整Transform的Scale匹配上投影平面的大小。
那为什么需要特别对应此种关联呢?根据分析,有如下猜想:
图6
UIRoot既然是根节点,那么该根节点的Scale改变,也就映射出UI Camera投影坐标系和世界坐标系的比例关系。即:
其中
那么在所有UI下的Widget进行Local Position Y(X)方向移动一个单位时,也就会对应到UI Camera投影坐标系中的一个单位,而在World下只移动了(
)单位
根据Viewport调整UI Camera投影坐标系
最后再根据Viewport坐标系调整UI Camera投影坐标系范围。
设Back Buffer的分辨率为 H * W
NGUI提供了三种方式
Scaling Style | Height取值 | Width取值 |
Fixed Size | Height= |
|
Pixel Perfect | Height = H , Clamp To |
|
Fixed Size On Mobiles | 在Unity Build里切换成移动平台后同Fixed Size否则同Pixel Perfect | 在Unity Build里切换成移动平台后同Fixed Size否则同Pixel Perfect |
参考代码如下:
这样调整的意义在于保证Widget在Y方向进行后续光栅化时不会走样失真。对于手机开发,通常使用
就可以较好匹配这两种主流分辨率。
问题解决
读到这里,大家一定不难发现,开头两个特效渲染不正确的问题是坐标系不统一导致的。
上图红框里Box X是粒子初生的范围,为1,这个坐标系是在Local下的。因此也就是UI Camera的投影坐标系的单位1,变换到World将是非常小的一个距离。该特效的制作是在World场景下,显然转换到UI投影坐标系会产生较大偏差,这就是上述问题的本质原因。
总结
1. 美术在制作特效时,须要在正确应用的坐标系下创作(区分是UI Projection还是World)。
2. 美术在UI上使用粒子特效时,一定要注意应用背景的颜色。
此文通过解决这个问题来梳理一下UI的基础知识,帮助程序更好理解NGUI。如有不正确的地方,欢迎指出以便共同进步。