当前位置: 首页 > news >正文

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。如下图所示:

图1

在URP渲染管线下,我们如下设定活动相机的参数:

图2

导出图片

默认管线下

  在默认渲染管线下,我们只要选择Png格式便可以直接导出带透明通道的图片。但是这里我们要注意一些问题。Shader中无需带透明通道的Shader因将Alpha通道设为1,导致无法显示透明通道。比如一个不透明物体。你在Shader的着色函数中将Alpha通道设为0。那么在我们对贴图进行采样的时候,因为这里贴图的Alpha值为0,所以这里本该显示出物体的部分变成了透明通道。特别是在做一些后处理相关的操作时,请将Alpha值默认设定为0。

URP管线下

  在默认渲染管线下,我们只要选择Png格式便可以直接导出带透明通道的图片。其实URP管线下本应该也是这样的操作。但是URP自带的后处理Shader,会在一开始将Alpha值设为1,导致无法显示透明通道。所以要不然我们将URP的后处理效果关闭,要不然就是我们修改URP的Shader,将默认值设定为0。

  如果你不想要URP的后处理效果,那么我们直接设定相机的参数将后处理关闭即可。如下图所示:

图3

但是有时候我们确实是需要URP的后处理效果(请注意下面的方法仍然会让一些后处理的效果丢失)。那么我们就需要修改URP的Shader。首先我们要先找到URP项目中的配置文件,具体的方法可以查看这篇文章Unity查找URP设置的方法。然后我们下图中红框选中的地方。

图4

跳转到下图这个界面后,图中红色框选中的地方就是后面我们要修改的URP的后处理设定。

图5

首先我们先创建出这个设置。我们在Unity的上菜单栏中找到Assets,或是在Project视窗下的空白处右键鼠标。接下来我们按照Create->Rendering->URP Post-process-data创建新的设置。我将其命名为SelfPostSetting。我们将这个设置替换掉现在URP的后处理设定。我们点击这个刚刚创建的设置,在Inspect视窗下,这里并不显示任何东西。我们点击下图所示框柱的按钮,并按照箭头的指示改为Debug

图6

之后我们的Inpector就可以得到下面的这样的界面,我们需要替换的就是图片中框选出来的Uber Post PS

图7

我们要找到这个UberPostShader并改造一下。如果你是使用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的文件路径,LibraryAssets同级目录下。如果你用其他的方法导入的,那你只能自己寻找了。特别注意: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.shaderUberPost.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效果开得很大。如果没有后处理的效果则如下图所示:
图10

这是因为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。如下图所示:
图11

但是并不是所有的.mov编码格式都支持带透明通道。因为版本问题,最好大家自己可以上网找一下对应的格式是否带透明通道。这里我只给出当前Recorder支持的.mov编码格式中,有那些带透明通道的编码格式。带透明通道的编码格式有Apple ProRes 4444 XQApple ProRes 4444

  即使你设定正确了视频格式和编码格式。但是你会发现你仍然不能导出一个带透明通道的视频。这是因为决定一个视频是否带透明通道的因素,除了视频格式和编码格式外,还有一个因素是视频色彩的存储方式。而Recoder输出的视频的色彩的存储方式(Recoder输出的.mov的色彩的存储方式为yuv444p12le)压根就不支持带透明通道。而在URP下,.webm格式的视频导出就是没有通道,Recoder在输出.webm的时候就是认为其不能有透明通道,且质量是最低的。因此同样的设置下,.webm格式的视频都会糊一些。

默认管线下

  在默认管线下,.webm格式的视频导出是支持输出透明通道的。你只需要在导出设置中勾起Include Alpha后,便可以达成我们的目的。如下图所示:

图12

但是正如我之前所说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导入到项目中。导入的步骤如下图所示:

图13

我们先点击+号,然后选择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场景

闲言碎语

  希望这篇文章能对你有所帮助。

相关文章:

  • Java 版本 24 性能更新:更快、更智能
  • 高效构建与配置高可用负载均衡集群:从理论到实践的全面实施
  • WordPress 角标插件:20 种渐变色彩搭配,打造专属菜单标识
  • LeetCode每日精进:142.环形链表II
  • 应用分层、三层架构和MVC架构
  • 容器运行常见数据库
  • 使用 IntersectionObserver 实现懒加载和无限滚动
  • 静态页面在安卓端可以正常显示,但是在ios打开这个页面就需要刷新才能显示全图片
  • Dify+Ollama+DeepSeek部署本地大模型+知识库搭建
  • CSS flex布局 列表单个元素点击 本行下插入详情独占一行
  • BMS项目-面试及答疑整理
  • 【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发三
  • Linux:线程的互斥与同步
  • Vmware ubuntu22.04 虚拟机 连接windows主机虚拟串口
  • 5G时代的运维变革与美信监控易的深度剖析
  • 【漫话机器学习系列】093.代价函数和损失函数(Cost and Loss Functions)
  • 网络安全扫IP工具
  • QT基础二、信号和槽
  • Python常见面试题的详解8
  • Javascript网页设计案例:通过PDF.js实现一款PDF阅读器,包括预览、页面旋转、页面切换、放大缩小、黑夜模式等功能
  • 426.8万人次!长三角铁路创单日客发量历史新高
  • 4月译著联合书单|心爱之物:热爱如何联结并塑造我们
  • 美国通过《删除法案》:打击未经同意发布他人私密图像,包括“深度伪造”
  • 石家庄:城市更新,生活向美
  • 上海74岁老人宜春旅游时救起落水儿童,“小孩在挣扎容不得多想”
  • 人到中年为何腰围变粗?科学家发现腹部脂肪增加的细胞元凶