【Unity3D杂谈】记查相机渲染保存的图片与场景中不一致的问题的经历

一、问题描述

        最近在做2D手游的时候遇到一个需求:给2D图片识别边缘并且加上一圈描边,再在此基础上再加一圈柔和的外发光。

        效果实现本身没有什么特别之处,普通的描边shader参考,稍微改改即可以实现能接受的效果。

        类似的效果可以参考其他大佬的实现方案:

        [Unity Shader]2D边缘发光效果(学习笔记) - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/633616509Unity Shader 极简实践4——图片外发光 - 简书 (jianshu.com)icon-default.png?t=N7T8https://www.jianshu.com/p/333917633789

       shader的问题在于:一方面、2D图片边缘的识别本身就需要取周围像素点通过透明度判断出边缘来,即需要在片元着色器for循环去多重采样;另一方面、对描边和外发光的效果有柔边要求,因此需要实现类似于高斯模糊的边缘效果,本身高斯模糊也需要for循环多重采样。因此本身性能上讲是不高的。而根据需求, 场景内可能会有数量较多的图片需要使用这个shader。

        实际上其实美术在出图的时候就加上描边也是可以的,但这样美术童鞋就需要大量处理。本着能用代码就不用人力的原则,最后得出一个折衷的方案:

        实现一个Editor工具,使用相机拍摄使用了对应材质球的图片,相机背景色设置为Color.clear,然后拍摄出的图片自动存储到目录下。

        方案上本身没有问题,但是这个工具造出来之后,却发现,拍摄下来的图片,透明度变浅,颜色也变得有点奇怪。

二、问题分析

        测试过程中发现,相机的填充色改为其他颜色,但是透明度为0,拍摄出来的图片竟然也跟着变化。

        此时开始怀疑是混合模式的问题。

        查看shader,实现的方式是普通的透明度混合,即

        Blend SrcAlpha OneMinusSrcAlpha

        回忆起基础知识可知

        FinalColor = SrcColor * SrcAlpha + DstColor * (1 - SrcAlpha)

        

        让我们起一个更加直观的变量名字,假定渲染图片之前的渲染Buffer的Color和Alpha分别为BufferColor,BufferAlpha,图片的Color和Alpha分别为ImgColor和ImgAlpha,最终屏幕颜色为ResultColor和ResultAlpha,则有:

        ResultColor = ImgColor * ImgAlpha + BufferColor * (1 - ImgAlpha)

        ResultAlpha = ImgAlpha * ImgAlpha + BufferAlpha * (1 - ImgAlpha)

        由此可见,实际上输出的图片的Color和Alpha不是(ImgColor, ImgAlpha),Alpha是一个乘方关系,而Color也被乘上了Alpha系数。保存的图片效果不是想象中的效果的原因就是因为保存的结果是(rgb = ImgColor * ImgAlpha,  a = ImgAlpha * ImgAlpha)。

        但是为什么Scene里边看到的效果又是对的呢,这是因为,Scene界面下即便我们选了相机的颜色为Color.clear,场景依旧是按天空盒等形式展示的,因此看到的效果不是和Color.clear叠加的结果。

三、解决方式

       由于此处我们只是想导出正确的图片,最取巧的方式是,修改BlendMode为

        BlendMode One One

        则对应的

        ResultColor = ImgColor * 1 + BufferColor * (1 - 1)

        ResultAlpha = ImgAlpha * 1 + BufferAlpha * (1 - 1)

        正好是对应图片的值。这样拍摄出来的效果就是想要的效果了。

你可能感兴趣的:(unity,游戏引擎,游戏,3d)