找回密码
 立即注册
查看: 305|回复: 0

[笔记] 关于RenderTarget与SceneCapture组件的基础使用

[复制链接]
发表于 2023-2-13 12:00 | 显示全部楼层 |阅读模式
前言

——渲染纹理(渲染目标),也就是RT,是玩一些进阶的效果(比如画布,水雪沙草地轨迹,基于深度相机的Decal和ShadowMap等)很常用的一个东西。当然也有一些简单的应用,比如后视镜或者小地图。
——RT在U3D里的全称是RenderTexture,UE里则是RenderTarget。
——本文会做一个基于RT的圆周运动拖尾效果,并将效果的俯视图作为小地图显示在屏幕左上角。


https://www.zhihu.com/video/1604167471401058304
UE蓝图

1、创建

——本文的案例使用的是动态创建的RT。如果你想要静态的,可以在Content面板右键->Add/ImportContent->Materials&Textures->RenderTarget进行创建。


——动态创建的节点是CreateRenderTarget2D。


——参数含义分别是:图像宽高、图像像素格式、默认颜色、是否计算MipMap,常用的配置是前三项。
2、使用材质绘制RT

——如果我们想做一些可实时交互的绘制功能,同时又想将绘制的历史暂时保留下来,常见的套路就是将RT作为一个保留痕迹的"画布",然后用shader(材质)去控制每一笔怎么“画”,比如我以前做过的一个抄原神的沙漠轨迹效果。
——UE相关的节点也很简单直接,即DrawMaterialToRenderTarget节点。


——在这个过程中经常会有个叫“乒乓缓冲”的东西,即创建一个临时交换用的RT丢到shader里得到当前帧的计算结果,然后再把这个结果拷贝回原RT。
——U3D的Blit方法里有直接从RT1拷贝到RT2的重载,但UE的节点里我没找到,所以这里采用比较笨的方法,创建一个只有纹理采样的材质,然后利用DrawMaterialToRenderTarget节点做交换。


——比如本案例中的RT绘制部分便是下面的结构,其中最后的CopyMID就是基于上面的材质创建出的交换专用MID。


3、SceneCapture组件



——单看可配置项,其实UE的SceneCapture组件很像U3D中的相机,下面的TextureTarget就是塞RT的地方。和U3D相机的OutputTexture相同,它会将相机照到的场景渲染到这里的RT上。




——这里有个坑点,就是SceneCapture的CaptureSource选项在本案例中要换成LDR,否则用默认的HDR会出现透明通道全黑的问题,进而导致小地图显示不出来(完全透明)。


4、案例

——首先是蓝图的总流程,封装的具体方法可自行下载后面的资源查看。


——然后是绘制用的材质。


——这类材质最常见的参数就是输入贴图、笔刷位置和笔刷大小,这里对应MainTex、PosTS和Radius01,并做了一个按距离衰减,同时用布尔的方式混合新旧灰度,最后再做一个随时间衰减。
——根据之前做沙漠轨迹的经验,涉及到随时间衰减的效果,RT的像素格式不能用8位精度的RGB,事实证明UE里也是如此(因为一帧的衰减量往往小于2^-8,导致直接截断为0,就不衰减了),所以统一用16位精度。


UE C++

1、关键代码

——因为这次的蓝图和C++ API都很简单粗暴,所以就直接列在一起了。
#include "Kismet/KismetRenderingLibrary.h"

//创建
UKismetRenderingLibrary::CreateRenderTarget2D(GetWorld(), 宽, 高, 像素格式)

//使用材质绘制
UKismetRenderingLibrary::DrawMaterialToRenderTarget(GetWorld(), RT, 材质or材质实例);

//SceneCapture组件配置示例
CaptureC->ProjectionType = ECameraProjectionMode::Orthographic;
CaptureC->OrthoWidth = 10000;
CaptureC->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
——关于两个方法中的第一个参数,也就是UObject* WorldContextObject,可以参考下面的解释。


——简单来说,应该就是个间接获取当前World指针的方式。如果是本案例中继承自Actor类写的代码还可以调GetWorld直接获得World指针,但如果是别的可能就需要间接的方式传个当前World中的对象,然后逐级往上找到World,所以这个参数才叫Context(上下文)。(所以说上面的代码里传this应该也没问题。至于它要World指针干什么就去翻源码吧- -有点复杂,个人能力有限目前还啃不动)
——另外需要特别说明的是C++里实现RT拷贝方法。本文案例的关键代码如下。
//绘制RT
DrawMID->SetVectorParameterValue(TEXT("PosTS"), FLinearColor(PosTS));
UKismetRenderingLibrary::DrawMaterialToRenderTarget(GetWorld(), TempRT, DrawMID);
UKismetRenderingLibrary::DrawMaterialToRenderTarget(GetWorld(), DstRT, CopyMID);
——其实就是纯纯照搬蓝图。但为了实现RT拷贝,这里搞了个单纹理材质,这样做……有点蠢。还有种纯代码的方式好像能实现相同的效果,但我还没玩明白,就是FRHICommandList类下的CopyToResolveTarget和CopyTexture方法,这里可以先放一份好像能借鉴的资料慢慢啃。
——另外,可能还要抽空了解一下UE的RHI线程是什么鬼。(看样子好像和U3D的Command Buffer有点像- -···虽然U3D里这玩意我也不熟就是了)
2、资源链接

——案例做成了一个plugin,下面的压缩包直接解压到项目工程根目录,然后重新编译即可。
链接:
提取码: m2yk
U3D

1、创建

——U3D的RT构造函数有很多种重载。


——以下面几种示例。
RenderTexture RT1 = new RenderTexture(256, 256, 0, RenderTextureFormat.ARGB32);
RenderTexture RT2 = new RenderTexture(RT1.descriptor);
RenderTexture RT3 = new RenderTexture(256, 256, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
——有几点需要注意:
1、U3D的像素格式像上面的ARGB32这种写法的指的是通道总位数=32,各通道平分,相当于UE里的RTF_RGBA8。
2、RT2只是new出了一份长宽、format等相关配置和RT1相同的RT,其中不包括RT1的图像内容。
3、RT3的RenderTextureReadWrite控制的是这个RT的色彩空间是线性还是gamma。如果是属性遮罩则需要手动Linear。
2、使用材质绘制RT

——U3D里一般用RenderTexture.GetTemporary来创建临时RT,并用下面的方式实现乒乓缓冲。当然你也可以像上面的UE案例一样,创建一个持久的TempRT做交换,而且UE里也有相关的ReleaseRenderTarget2D方法来做这种临时RT。
RenderTexture TempRT = RenderTexture.GetTemporary(256, 256, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Graphics.Blit(SrcRT, TempRT, material);
Graphics.Blit(TempRT, SrcRT);
RenderTexture.ReleaseTemporary(TempRT);
——其中,GetTemporary的参数列表和RT的构造函数基本一致。
——另外,U3D还有个叫CustomRenderTexture的东西。


——我目前没用过。但只看配置列表的话,感觉就是把上面的代码过程单独封装成了一个可实例化对象的类,从而使用起来更方便。
——另外这里还有个双缓冲选项。看提示感觉勾了的话,可能在乒乓缓冲的时候就不用TempRT,而是可以Graphics.Blit(SrcRT, SrcRT, material)这么写了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-29 10:08 , Processed in 0.172917 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表