UnityRecorder导出带透明通道的视频和图片
UnityRecorder导出带透明通道的视频和图片
前言
本篇文章仅说明URP和默认渲染管线下的实现效果。
环境
Unity版本:2022.3.34f1c1
Recorder版本:4.0.3
操作系统:Windows 10
Universal RP 14.0.8
具体操作
预准备
无论是导出视频还是导出图片,我们都要将Recorder输入源改为Texture Sampling
。然后我们设定活动相机的参数。在默认渲染管线下,我们在相机组件中找到Clear Flags
选项,将其设置为Solid Color
,并在Background
中设定背景颜色将其透明通道值改为0。如下图所示:
在URP渲染管线下,我们如下设定活动相机的参数:
导出图片
默认管线下
在默认渲染管线下,我们只要选择Png
格式便可以直接导出带透明通道的图片。但是这里我们要注意一些问题。Shader中无需带透明通道的Shader因将Alpha通道设为1,导致无法显示透明通道。比如一个不透明物体。你在Shader的着色函数中将Alpha通道设为0。那么在我们对贴图进行采样的时候,因为这里贴图的Alpha值为0,所以这里本该显示出物体的部分变成了透明通道。特别是在做一些后处理相关的操作时,请将Alpha值默认设定为0。
URP管线下
在默认渲染管线下,我们只要选择Png
格式便可以直接导出带透明通道的图片。其实URP管线下本应该也是这样的操作。但是URP自带的后处理Shader,会在一开始将Alpha值设为1,导致无法显示透明通道。所以要不然我们将URP的后处理效果关闭,要不然就是我们修改URP的Shader,将默认值设定为0。
如果你不想要URP的后处理效果,那么我们直接设定相机的参数将后处理关闭即可。如下图所示:
但是有时候我们确实是需要URP的后处理效果(请注意下面的方法仍然会让一些后处理的效果丢失)。那么我们就需要修改URP的Shader。首先我们要先找到URP项目中的配置文件,具体的方法可以查看这篇文章Unity查找URP设置的方法。然后我们下图中红框选中的地方。
跳转到下图这个界面后,图中红色框选中的地方就是后面我们要修改的URP的后处理设定。
首先我们先创建出这个设置。我们在Unity的上菜单栏中找到Assets
,或是在Project
视窗下的空白处右键鼠标。接下来我们按照Create
->Rendering
->URP Post-process-data
创建新的设置。我将其命名为SelfPostSetting
。我们将这个设置替换掉现在URP的后处理设定。我们点击这个刚刚创建的设置,在Inspect
视窗下,这里并不显示任何东西。我们点击下图所示框柱的按钮,并按照箭头的指示改为Debug
。
之后我们的Inpector
就可以得到下面的这样的界面,我们需要替换的就是图片中框选出来的Uber Post PS
。
我们要找到这个UberPost
Shader并改造一下。如果你是使用Unity的PacketManager
,或是在创建项目时选择了URP项目,那么你可以你项目地址+Library\PackageCache\com.unity.render-pipelines.universal@14.0.11\Shaders\PostProcessing
下找到UberPost.shader
。比如我的项目地址为D:\URPTest
,则UberPost.shader
的地址为D:\URPTest\Library\PackageCache\com.unity.render-pipelines.universal@14.0.11\Shaders\PostProcessing\UberPost.shader
。或者你打开项目中Assets
的文件路径,Library
在Assets
同级目录下。如果你用其他的方法导入的,那你只能自己寻找了。特别注意:com.unity.render-pipelines.universal@14.0.11中的@14.0.11是URP的版本号。你的URP版本和我的不同则不一样。这里位置我也不能保证一样,但是你可以试试找找看。接下来,我们就将找到的文件复制到我们项目中(不一定要改名,但是我为了方便我自己查找,所以我改名为了SelfUberPost
)。然后我们在Shader中找到return half4(color, 1.0);
,并将其改成return half4(color, SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, SCREEN_COORD_REMOVE_SCALEBIAS(input.texcoord)).a);
。
因为版本的不同,你可能找不到return half4(color, 1.0);
或是SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, SCREEN_COORD_REMOVE_SCALEBIAS(input.texcoord)).a
存在报错的情况。但是万变不离其宗,这里我们主要的思想是因为UberPost.shader
在片元着色器(有的称为像素着色器)将颜色的透明度通道设定为1,导致最后我们进行Texture Sampling
时得不到应该有透明通道的地方。所以解决办法就是找到UberPost.shader
的具体实现,并将其输出透明度通道的规则进行修改。这里的SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, SCREEN_COORD_REMOVE_SCALEBIAS(input.texcoord)).a
中_BlitTexture
,你可以理解为不开后处理时,我们所截取的图片。显然这里是有带透明通道的,那么我们只要对其进行采样并将其的透明通道值作为UberPost.shader
透明通道的输出,最终我们就可以得到带透明通道的图片。
最后,我们将Uber Post PS
里的Shader替换为我们刚刚修改的Shader即可。
如果你开启了抗锯齿,那么你会发现之前即使你修改过了UberPost.shader
,也没办法得到带透明的图片。其中的缘由和UberPost.shader
一致。而这次我们要修改是FinalPost.shader
。我们直接按照查找UberPost.shader
得到FinalPost.shader
,并将设置中的FinalPost.shader
设定为我们的。设置中FinalPost.shader
在UberPost.shader
下面。我们在FinalPost.shader
中找到half4 finalColor = half4(color, 0);
,然后将其改为half4 finalColor = half4(color, SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, SCREEN_COORD_REMOVE_SCALEBIAS(input.texcoord)).a);
。
仍有问题
之前我们说为了保留后处理的效果,所以我们要修改UberPost.shader
和URP自身的设置。但是这样做仍然有一个问题,有些后处理形成的效果会在我们截图后会被截掉。比如下面这两张图:
这两张图片都是我们截图下来的结果。不带透明通道的图片就是我们游戏内的样子。很明显我们发现游戏内的样子和我们带透明通道的图片很不一致。原因是在游戏内,我将后处理中的Bloom
效果开得很大。如果没有后处理的效果则如下图所示:
这是因为Bloom
效果处理后,原本在_BlitTexture
中透明通道值为0的地区变成了有颜色的地区。而我们用_BlitTexture
的透明通道来做是否有透明的区分显得有些不够了。当然我们可以使用其他的办法来做到这一点,比如我们将背景设定为黑色,然后其他地方只要有颜色就给它加上透明通道,或者你按照他们rgb通道的值来决定它们透明通道的值。我试过几个方法,都不太行。下面是我使用的方法:
// 用亮度来判断是否有透明通道
float extraAphla = pow(color.r,2.2) + pow(color.g * 1.5,2.2) + pow(color.b * 0.6,2.2);
// 3.76509844 = 1 + 1.5^2.2 + 0.6^2.2
extraAphla /= 3.76509844;
// 0.45454545 = 2.2^2
extraAphla = saturate(pow(extraAphla,0.45454545));
return half4(color, max(SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, SCREEN_COORD_REMOVE_SCALEBIAS(input.texcoord)).a,extraAphla));
这里我预先将背景设定为黑色,然后将其他地方的颜色的亮度值加上透明通道。但是我们也要注意,如果背景颜色不是黑色,那么我们就不能用这种方法了。当然这在抗锯齿处理中同样也有问题。如果抗锯齿的处理会产生额外的非透明像素,这些像素如果不满足我们的条件也会被我们截图时丢掉。这时候抗锯齿的效果就变得不那么的明显了。这样的问题即使在后面导出视频的时候也不能解决。因为视频仍然是截取图片,然后制成视频。
导出视频
有关视频的一些知识
Recoder
的视频导出功能支持以下几个视频格式:.mp4
,.mov
和.webm
。其中.mov
和.webm
的视频格式支持带透明通道。在Recoder
的视频导出下,还提供了一个.gif
的选择。虽然.gif
确实可以带透明通道,但是对其而言透明值只有0或1。所以在使用.gif
导出时候,你会发现图片中的某些地方出现斑点状的透明地带。这其实就是因为.gif
的格式本身对透明通道值的认知只有0或1,所以才导致的。如果你要选择.mov
则要将编码器(Encoder)设定为ProRes Encoder
。如下图所示:
但是并不是所有的.mov
编码格式都支持带透明通道。因为版本问题,最好大家自己可以上网找一下对应的格式是否带透明通道。这里我只给出当前Recorder
支持的.mov
编码格式中,有那些带透明通道的编码格式。带透明通道的编码格式有Apple ProRes 4444 XQ
和Apple ProRes 4444
。
即使你设定正确了视频格式和编码格式。但是你会发现你仍然不能导出一个带透明通道的视频。这是因为决定一个视频是否带透明通道的因素,除了视频格式和编码格式外,还有一个因素是视频色彩的存储方式。而Recoder
输出的视频的色彩的存储方式(Recoder
输出的.mov
的色彩的存储方式为yuv444p12le
)压根就不支持带透明通道。而在URP下,.webm
格式的视频导出就是没有通道,Recoder
在输出.webm
的时候就是认为其不能有透明通道,且质量是最低的。因此同样的设置下,.webm
格式的视频都会糊一些。
默认管线下
在默认管线下,.webm
格式的视频导出是支持输出透明通道的。你只需要在导出设置中勾起Include Alpha
后,便可以达成我们的目的。如下图所示:
但是正如我之前所说Recoder
输出的.mov
格式视频,色彩的存储方式是不支持输出透明通道的。所以在默认渲染管线下,我们只能使用.webm
格式来导出透明通道的视频。
URP管线下的准备
为了可以导出透明视频,我们要先让URP管线可以导出透明图片(如何导出透明图片请看上文)。然后我们就可以准备导出透明视频了。
通用的解决办法
因为是在代码层次上不支持导出带透明通道的视频,所以我们只能修改Recoder
的源码来实现这个效果。首先我们先拿到Recoder
的源码,我们先使用包管理器(Package Manager)在自己的项目下安装Recoder
。如同我们之前找UberPost.shader
方法一样。Recoder
的源码路径为你项目地址+Library\PackageCache\com.unity.recorder@4.0.3
,比如我的项目地址为D:\URPTest
,则地址为D:\URPTest\Library\PackageCache\com.unity.recorder@4.0.3
。我们将一整个com.unity.recorder@4.0.3
复制并粘贴到电脑的任意一个地方。我就直接放在了桌面下(地址为:C:\Users\Administrator\Desktop
)。然后我们再使用包管理器将项目中的Recoder
进行移除,并将我们刚刚复制的com.unity.recorder@4.0.3
导入到项目中。导入的步骤如下图所示:
我们先点击+
号,然后选择Add package from disk...
,然后选择刚刚复制的com.unity.recorder@4.0.3
的路径,在com.unity.recorder@4.0.3
文件夹下有一个package.json
的文件。我们选中它并点击Open
按钮,便可以成功导入。
接下来,我们在com.unity.recorder@4.0.3
中找到MovieRecorderEditor.cs
,其相对路径为:com.unity.recorder@4.0.3\Editor\Sources\Recorders\MovieRecorder\MovieRecorderEditor.cs
。然后我们搜索if (!UnityHelpers.UsingURP() && mrs.ImageInputSettings.SupportsTransparent && mrs.EncoderSettings.CanCaptureAlpha)
并将其改为if (mrs.ImageInputSettings.SupportsTransparent && mrs.EncoderSettings.CanCaptureAlpha)
。如果你使用URP管线,你会发现原本在默认管线中的Include Alpha
在URP下是没有的。而我们将这个修改后无论是URP还是默认渲染管线都可以拥有Include Alpha
的功能。
既然我们都已经修改原代码了,那么我们自然可以将.mov
格式视频修改为支持输出透明通道的样子。我们按照com.unity.recorder@4.0.3\Samples~\FFmpegCommandLineEncoder
路径找到FFmpegEncoderSettings.cs
。并在其中搜索OutputFormat.ProRes4444XQ => "-c:v prores_ks -profile:v 5 -vendor apl0 -pix_fmt yuva444p10le"
将其替换为OutputFormat.ProRes4444XQ => "-c:v prores_ks -profile:v 5 -vendor apl0 -pix_fmt yuv420p"
。之后.mov
格式视频支持输出透明通道了。这里有一个问题,在Unity下不能直接查看这个导出的.mov
视频,但是我将.mov
视频转为.webm
格式后导入Unity,我是可以看到有透明通道的。
一些遗憾
虽然这样我们可以导出透明视频了,但是这里仍然存在一个问题。当我导出.webm
格式的视频时,.webm
视频在有些地方会糊,即使我修改设置也无济于事。我找不到原因,所以就只能这样了。
参考文章
- 【URP趟坑】Unity在URP中使用RenderTexture在UI中渲染带透明相机背景的PostProcessing场景
闲言碎语
希望这篇文章能对你有所帮助。