Unity背景图片模糊



Unity应用程序开发人员可能会遇到的任务之一是将当前图像放在后台,以便将用户的注意力切换到新的事物上,例如出现的菜单或消息。本文介绍了具有Unity基本知识的开发人员无需使用外部资源或其他Unity许可证即可解决此问题的经验。我希望这些材料对那些面临类似问题但在不同阶段找不到有效解决方案的人有用。

突出显示重要的界面元素(并删除不必要的界面元素)是改善用户体验的主要工具之一。在使用移动应用程序时,这一点尤其明显。注意力的切换可以通过不同的方式进行-平稳地将按钮移出屏幕边缘,使背景变暗,动态行为等。这些操作包括使图像模糊。一方面,这种效果降低了背景元素的对比度,并且使眼睛难以捕捉。另一方面,当图像失去焦点时,模糊会产生潜意识效果,我们对此有不同的认识。这种方法常用于游戏中,从Unity上著名的游戏示例中,我们可以选择选择《炉石传说》任务菜单决斗完成屏幕

现在想象我们想在我们喜欢的应用程序中做相同或相似的事情。我们也不是没有预算并且对Unity经验很少。

第一部分-着色器


出现的第一个想法是,一切都应该已经实现。当然使用着色器。它当然应该在Unity框中。快速搜索和安装尝试表明,Unity中有这样的着色器,但事实证明,它是Standard Assets的一部分,并且无法获得免费许可证。

但这是模糊的!数学上简单的基本效果,也应该易于实现并将其集成到项目中。即使我们对着色器一无所知。接下来,进入Internet,搜索迅速显示出存在用于效果的现成着色器的源代码,甚至是不同的源代码。

对于第一个实现,采用了此着色器要对其进行测试,只需将着色器作为源文件添加到项目中,将其与材质关联,将材质绑定到Image。

第二部分-用户界面


如果我们关心应用程序的用户,则必须对用户界面给予应有的关注。如果仅在背景上添加模糊效果,而忽略它,那么这并不完全符合创建产品的内部原理。

结果,为了采取进一步的措施,您需要触摸发生的事情,然后进行思考和调整,以引起对用户的必要感知。这一点是高度上下文相关的。根据图像的对比度,各种颜色和其他因素,可以选择各种值和其他工具。在我们的情况下,在此阶段,模糊半径为25(着色器参数),附加的“透明性”为70%(通过单独的Image在着色器外部)可获得良好的结果。但是,这只是最终的背景图像。根据电话上的感觉,过渡本身太尖锐。

图像中已安装的着色器是在每一帧上计算的,因此,为了平滑切换,足以动态更改模糊半径参数和透明度。您可以用不同的方式来组织它,但是实质上,处理是Update处理程序中的更新,具体取决于时间。在应用程序的最终版本中,过渡本身为0.2秒,但事实证明,这对于用户的感知至关重要。

第三部分-生产力


近几个月来,我听到一些不同用户(甚至不是程序员)的抱怨,游戏程序经常闲置无休地使用所有可用的计算机资源。例如,这可以表示为在程序最小化到托盘的情况下最大化使用视频卡,或者不考虑所有内容和一个处理器线程的恒定负载。这样做的原因很直观-开发人员或发布者的收入不依赖于优化(人们在购买时不会关注此类事情),而普通开发者最有可能更关注本地KPI和延迟需求。但是,实际上,我不想属于这一类。

在下一次启动时完成具有背景模糊效果的上一个阶段后,将手机握在手中一段时间​​后,就会出现温暖的感觉。如果您在菜单上花费更多的时间,那么一切都会变得更加糟糕。而且即使菜单中的用户大概并且很可能没有花费太多时间,我也根本不想丢掉电池。

分析表明,根据所选半径,上面选择的着色器具有渐近复杂度O(r 2。换句话说,如果将模糊半径从1增加到25,则每个点的计算数量将变为625倍。在这种情况下,计算发生在每一帧上。

解决方案的第一步是,并不是所有着色器都同样有用,并且可以以不同方式实现模糊效果。为此,您可以进行单独的模糊处理,其本质是首先仅对水平线进行模糊处理,然后仅对垂直线进行模糊处理。结果,我们获得O(r),而ceteris paribus的复杂度下降了一个数量级。另一种方法是采用较小的mipmap,它已经进行了一些模糊工作。以该着色器

为基础然而,事实证明其模糊效果不足,并且在图形的高对比度下,图像变得“破裂”。为了获得更好的效果,更改了分布(GRABPIXEL元素)。如果在着色器中,从0.18到0.05,半径4,在我们的版本中,从0.14到0.03,半径6(有效,但总和应为1)。

因此,处理复杂度降低了1-2个数量级。但这并不需要停止。

第四部分-静态背景


但是,如果我们将着色器保留在现有图像上,那么它将一直在工作,对每帧进行相同的计算。如果后台发生了某些事情,那么我们需要这样做。但这是完全可选的,如果背景是静态的。因此,在动态模糊之后,您可以记住结果,将其放在背景上并关闭着色器。

接下来,让我们尝试代码片段。为整个屏幕准备目标纹理可能如下所示:

_width = Screen.width / 2;
_height = Screen.height / 2;
_texture = new Texture2D(_width, _height);

var fillColor = Color.white;
var fillColorArray = _texture.GetPixels();
for (var i = 0; i < fillColorArray.Length; ++i)
{
	fillColorArray[i] = fillColor;
}
_texture.SetPixels(fillColorArray);
_texture.Apply();

_from.GetComponent<Image>().sprite = Sprite.Create(_texture, new Rect(0, 0, _texture.width, _texture.height), new Vector2(0.5f, 0.5f));

即,这里准备的纹理在水平方向和垂直方向上比屏幕小2倍。由于我们知道设备的屏幕尺寸是2的倍数,因此几乎可以完成此操作,如果完成,这将减小纹理的尺寸并促进模糊。

RenderTexture temp = RenderTexture.GetTemporary(_width, _height);
Graphics.Blit(_from_image.mainTexture, temp, _material);
RenderTexture.active = temp;

_texture.ReadPixels(new Rect(0, 0, temp.width, temp.height), 0, 0, false);
_texture.Apply();

_to_image.sprite = Sprite.Create(_texture, new Rect(0, 0, _texture.width, _texture.height), new Vector2(0.5f, 0.5f));
RenderTexture.ReleaseTemporary(temp);

要使用着色器(在_material上)使用mainTexture捕获图像,将使用Graphics.Blit。接下来,我们将结果存储在准备好的纹理中,并将其放置在目标图像中。

一切都会好起来;在实践中,他面对这样的效果,根据设备的不同,背景图像会上下颠倒。经过对问题的研究和调试后,原因很清楚- 坐标系之间差异Unity无法隐藏的Direct3D和OpenGL。同时,我们的着色器会检测并正确处理UV,并且反转已在Unity环境(ReadPixels)中发生。网上有很多提示,例如“如果您颠倒了,请将其翻过来”或“更改紫外线信号”。使用UNITY_UV_STARTS_AT_TOP宏无助于对所有被测设备进行良好的概括。另外,例如,我们遇到过这样的情况:如果您准备在iPhone上使用Xcode进行仿真的程序集,而Unity不使用Metal,而仅使用OpenGLES,则您需要拦截诸如在其他软件上运行的设备的仿真之类的情况。

在尝试了多种选择之后,我选择了两种平板电脑。第一个是强制相机渲染(在图像捕获时设置forceIntoRenderTexture)。第二个是通过SystemInfo.graphicsDeviceType即时确定图形系统的类型,这使得可以定义类OpenGL或类Direct3D(第一个组是OpenGLES2,OpenGLES3,OpenGLCore和Vulkan)。此外,在类似Direct3D的实现中,我们需要进行翻页,该翻页是按程序进行的(例如,像这样)。

结论


我希望本文对克服未知的问题,改善用户生活和了解Unity的人有用。如果您看一下战斗中的效果,那么在Unity中,它看起来像这样在应用程序中,感觉有些不同,但是此动画可以给出足够的想法。

All Articles