本篇是我在实际工作中整理给项目组分享的一篇PPT,也是当时项目中用到的解决方案。当时的情况是,由于项目升级提升画面质量,所以领导决定对整个项目采用线性工作流,这项工作也就交给了我。
原有Gamma工作流和线性工作流的差别
原有的gamma工作流下,无论贴图是否勾选sRGB选项,都不会对最终结果产生影响(这一点可以自行借助工具查看验证)。而更换到线性工作流下,原有的sRGB贴图会多一步移除伽马校正,而线性贴图/未勾选sRGB的贴图这不会:
所以升级之后要对美术资源进行把控和制定新的规范,以及给美术同学提供后续使用各种dcc软件产出美术资源的标准。
UI 半透明混合解决方案
出现问题的原因:UI资源勾选了sRGB,但是由于线性空间下和gamma空间下的alpha混合方式不同,所以导致了颜色的偏差:
gamma空间混合公式: color = (A.rgb * A.a) + (B.rgb * (1 - A.a))
Linear空间混合公式: color = ((A.rgb ^ 2.2 * A.a) + (B.rgb ^ 2.2 * (1 - A.a))) ^(1 / 2.2)
解决方案:
方案1.更改UI美术同学PS中的设置,直接出线性图,UI贴图不勾选sRGB,最后在urp渲染管线中pow(2.2)
方案2 :
方案3:更改渲染管线
落地方案
最终选择了方案三,不选1和2的原因是因为一是影响到美术同学的工作流,二是部分UI是外包的,更改起来成本很大。
更改管线思路以及主要代码
原版的项目是在URP10.8+进行的,并没有用URP12+,正好借着这个机会在URP12中实现一下。
创建一个URP新工程,目前是URP12.1.8
添加测试场景(在PS里制作两份测试图片素材),建立一个独立的UI_Camera和对应的RendererData
UniversalRenderer.cs中对_UIColorTexture初始化
RenderTargetHandle m_UguiTaget;
m_UguiTaget.Init("_UIColorTexture");
创建对应RT
var uiDescriptor = descriptor;
uiDescriptor.useMipMap = false;
uiDescriptor.autoGenerateMips = false;
uiDescriptor.depthBufferBits = k_DepthStencilBufferBits;
uiDescriptor.height = Screen.height;
uiDescriptor.width = Screen.width;
uiDescriptor.graphicsFormat = GraphicsFormat.R8G8B8A8_UNorm;
cmd.GetTemporaryRT(m_UguiTaget.id, uiDescriptor, FilterMode.Bilinear);
释放RT
cmd.ReleaseTemporaryRT(m_UguiTaget.id);
URP不执行后处理时,我们需要写一个额外的Pass去得到FinalblitPass最后绘制的图像,然后在主Render流程里执行
RenderTargetHandle finalTarget = isUICamera ? m_UguiTaget : m_ActiveCameraColorAttachment;
m_FinalBlitPass.Setup(cameraTargetDescriptor, finalTarget);
NoPostProcessPass m_NoPostProcessPass;
m_NoPostProcessPass = new NoPostProcessPass(RenderPassEvent.AfterRenderingPostProcessing + 1, m_BlitMaterial);
...
//
if (!anyPostProcessing && !isSceneViewCamera && camera.CompareTag("MainCamera"))
{
m_NoPostProcessPass.Setup(m_UguiTaget,m_ActiveCameraColorAttachment);
EnqueuePass(m_NoPostProcessPass);
}
此外,我们还需要更改UGUI Pass,因为代码太多后续具体代码更改可以看已经写好的开源项目(因为时间关系我只写了不执行后处理时的情况,后续有时间添加上):
https://github.com/Nuomi-Chobits/Unity-URP-LinearSpaceUI
这里只讲讲思路:
如何处理后处理:
URP执行相机后处理时,会根据使用哪种后处理分别走postProcessPass或finalPostProcessPass(比如FXAA这种抗锯齿就要在最后做,走finalPostProcessPass,Bloom这种我们就在postProcessPass做)。
我们要做的就是将上文中的UIColorTexture,根据实际情况,在主相机这两个Pass中(UI相机执行前的最后一个相机)设置为其RenderTarget。
Scene:
因为编辑器下只有一个相机,所以我们需要写额外的Pass,添加编辑器对应的处理情况。
纹理资源规范
- 场景和角色的 Abedo (BaseColor) 贴图、UI、各类特效贴图勾选sRGB
- 一些通道图、Alpha贴图不勾选sRGB
- 法线贴图、光照贴图和环境贴图只需要关注选择的贴图类型
后续项目一些便利性更新
1.为了节省美术同学的精力,制作一些批量化脚本来处理旧的资源,对于新的导入Unity的资源,设定新的Import Setting,美术同学只需输出资源到对应目录下即可。
2.为了减少反复确认带来的沟通成本,添加了一些测试工具和场景,方便美术自己对比,验证是否偏色,比如SP To Unity对齐。