1.前言
由于现在大家都喜欢用PBR 很显然要考虑到 线性空间。手机上大部分现在的都做法都是在Gamma下 shader中 Color 平方一下 最后在后处理中 再转为Gamma空间 而UI显然还是Gamma空间 我本人也是比较赞同这个做法 毕竟 增加的消耗不多。 具体可以看下大神的这篇文章具体可以看下大神的这篇文章(Unity的Gamma校正以及线性工作流)。。。。。再看完这篇文章后 陈嘉栋提到了关于解决混合问题的处理方法
1.所有美术制作人员在线性空间中制作
2.ui shader alpha pow(2.2) 操作(PS:每个UI 都要做pow 2.2? 算了吧 感觉很费)
3. “仍然使用gamma space的workflow,但是涉及光照的shader自己来做pow 2.2和pow 0.45的” PS:通过抓帧看 很多游戏 是直接平方 和开根 毕竟 很近似 为了性能考虑吧
然后真的没办法了?公司有几个项目组 考虑就在Unity线性环境下制作。特效 场景 角色 可以在线性环境下工作 但是UI在混合上 出现了问题。我觉得UI有没光照计算 也没要线性下工作。。而且切换到线性 workflow 制作UI 会给美术带来一些麻烦吧(我这么觉得的。。很多人可能从来没把PS调到过线性下吧。当然也可以这么干PS: Unity 线性空间下UI Alpha混合错误,Photoshop设置) 。 那有没有可以不改变UI制作的流程 在GAMMA下 制作呢 我想到一种方法 以下仅供参考。
2.原因分析
首先我在PS下做了一张 (0.5,0,0,0.5) 的半透红色贴图在GAMMA工作下的展示
上图是在GAMMA下
上图是在linear下勾选SRGB
上图是linear下 没有勾选srgb
可以看出 图2 比图1亮一些 原因在于在 SrcAlpha OneMinusSrcAlpha 下(背景纯黑)
图1:color = 0.5 r * 0.5 alpha + 0 * (1-0.5) = 0.25
我们看下 linearworlflow下
在RD下看到 BackBuff 是srgb的 所以 图2 color = (0.5 * pow(2.2) *0.5 + 0 * pow(2.2) *(1-0.5) )pow(1/2.2) = 0.3648.. 所以亮于 图1
图3 color = (0.5*0.5 + 0 * pow(2.2) *(1-0.5) ) * pow(1/2.2) = 0.5325...
所以图3最亮 > 图2> 图1 (PS: 可以拿颜色截取工具看下哦)
3.如何解决?
既然知道了原因。。。既然是因为FrameBuff 是SRGB的 那我自己建一张 线性的 RT不就行了吗。于是立马动手板砖。
1.首先简历一张 线性RT 绑定在UICamera上
public void OnPreRender()
{
RenderTextureFormat format;
format = RenderTextureFormat.ARGB32;
m_RT = RenderTexture.GetTemporary(m_Camera.pixelWidth, m_Camera.pixelHeight, 24, format,RenderTextureReadWrite.Linear);
m_RT.DiscardContents(true, true);
m_Camera.targetTexture = m_RT;
}
public void OnPostRender()
{
if (m_RT != null)
{
m_Camera.targetTexture = null;
RenderTexture destination = null;
Graphics.Blit(m_RT, destination, mt,0);
RenderTexture.ReleaseTemporary(m_RT);
}
}
2.取消图片SRGB选项
3.Render..
linear workflow 下的 效果
4.最终检测 和 GAMMA下渲染效果一样
4.原理
1.讲所有的UI 绘制到 linear 下的RT,因为图片本身也是linear的 所以 在RT中的颜色 = 0.5 * 0.5 + 0*(1-0.5) = 0.25
然后绘制 RT到 frameBuff上 color = (0.25 * Pow(2.2) + 0 *(1-0.5) *pow(2.2)) *pow(1/2.2) = 0.25,
当中的 “0.25 * Pow(2.2)” 需要你在shader中加上
fixed4 col = tex2D(_MainTex,i.uv);
col.rgb = GammaToLinearSpace(col.rgb);
return col;
最终效果 两遍一样 ~开心 当然 这个工作流程 需要 多一个RT 和多一次后效的draw call
5.实际项目中。。。当你用这个方法一样。。就会遇到坑爹情况
这个原因是因为我们的rt中Alpha通道倍多次写入造成的 。那我们的解决办法就是整个场景渲染也作为一个RT进行混合sceneRT=>UI RT====》Framebuff=>最终结果
Graphics.Blit(null, rt) //拷贝一下SceneRT
Graphics.Blit(rt, rt,Material); //因为拷贝之后场景是线性的 偏暗 所以再转回GAMMA下颜色
在写回Framebuff时候 记得 混合模式修改 1 0 然后大功告成!让我们看下效果
上面区域的是GAMMA模式 下面是线性 混合正常 搞定
5.结束
当然 会多出3次draw call全屏渲染 但是如果考虑场景本身也会做 tonemapping 和bloom 其他 也就多一次吧 当然。。这样做IOS马甲包时候 是不是可以不用换UI。。直接风格化下 就可以了。。反正是后效。。。前面的几种方法都可以 我个人倾向于 GAMMA下的工作流程
PS:9流程程序员 第一次分享 大家见谅