当前位置:   article > 正文

2d 蓝图_UE4中3D角色预览和2D头像动态生成

2d制图,3d预览

ed9e31c3dcc7f74466791df1b1a42142.png

游戏开发中的捕获需求

在游戏开发过程中,对角色的预览,和某些情况下生成头像是比较常见的需求。有些引擎,可以创建一个额外的场景和相机,然后将需要的内容放置到合适的位置,然后可以获得想要的图像。在UE4中不是这样,没法创建其他的场景,我们需要使用CaptureSceneComponent2D,字面意思看出来这是个场景捕获的组件,它就是用来捕获我们想要的场景内容到2D图像上。

CaptureSceneComponent2D如何使用

这个组件的使用相当简单,首先我们新建一个第三人称的示例工程,使用以下基本步骤:

  1. 创建RenderTarget2D,捕获组件捕获的内容需要渲染到这个纹理上。选择AddNew->Materials & Textures -> RenderTarget来创建,并命名为 RT_Preview。

eff78756ddb9418b793048a41f71dd80.png
  1. 在场景中放入SceneCapture2D,调整位置和朝向,对着角色

0ff03fc0950a3a4afcd464c83f326515.png

9c150316cd6480ccaa0537e080b097d1.png
  1. 选中场景中的SceneCapture2D对象,查看Details面板,找到TextureTarget项,然后选择刚刚创建的RT_Preview,选择之后立刻就会发现其中的小预览图就变成场景中的样子。

e5774e983070cd2a4244eb8fe4100557.png

对,就是这么简单,这个时候我们已经得到了一张捕获的纹理。那么这个纹理如何使用呢?可以跟普通的贴图一样使用,不过稍微有一点点区别,这个稍后再说。

使用RenderTarget2D

为了使用刚刚捕获的图像,我们可以建立一个界面来展示它。

  1. 创建一个界面蓝图,并放上一个图像控件,并设置图像控件Brush中的图片为之前的RT_Preview,然后在关卡蓝图里面,设置一个按键响应,把这个界面给展示出来。这里选择F键。

2147c494a325101c498a4e6b64aab09a.png

996f89c682271d09d8c6bc9cbd9cd060.png

2. 运行游戏之后,按F键,发现界面上啥也没有。原来RenderTarget纹理的透明度比较特别(具体是怎样还没有研究),我们回到场景,再次选中SceneCapture2D对象,在Details面板找到Catpture Source选项,然后选择如下。再次运行我们就可以看到界面中的图像。

5a98a313e014fcfcd8069361fbe84891.png

d60bc8642adb9d8e416fbbfb180c2eb3.png

只捕获想要获取的对象

从上面的截图可以看出来,角色背后的蓝天及场景中的其他内容都被捕获了。在实际应用中,这些部分一般来说是不需要的。那么如何设置只捕获特定内容呢?

  1. 依然是SceneCapture2D对象,它的属性中有个参数叫Primitive Render Mode, 这个就是用来控制绘制内容模式的,它有个叫做Use ShowOnly List的模式,这个模式就可以指定一些对象,然后只捕获这部分内容。

使用这个模式的一个小问题是,如果在编辑器中直接选择这个选项,那么TextureTarget中的预览将会消失。所以我们在编辑器中不修改它的模式,而在蓝图中动态设置。

2cd6a472a6394f0d6aa2ba4943b6cef9.png

2. 打开场景蓝图,在F按键之后的代码加上SceneCapture2D的一些设置

43e7813feb37b9f51c7126c87afd5df4.png

结果如下:可以看到场景部分都消失了,不过还有蓝的白的背景在。这部分内容应该是属于透明内容。下一节我们再把它给去除掉。在这里我们使用了Show Only ActorComponents 即只捕获特定的Actor对象,也可以使用Show Only Components节点来添加指定的组件,可以做到比Actor更细化的控制。后面还会讲到。

8872589d0f8d349a71e15517e5ccaa6c.png

去除不透明的背景,创建UI材质

  1. 前面有讲到将CaptureSource给设置成了Final Color(LDR) in RGB,这个设置实际上是将Alpha属性给丢了,所以我们首先将这个属性重新设置为第一项:

9967304ddb25d779acfc29a069748961.png

2. 创建一个材质, 命名为 MT_Preview,并打开。

  • 把MaterialDomain设置为User Interface,因为这个才是是给UI用的
  • 把BlendMode设置Translucent因为我们需要透明属性
  • 把之前建的RT_Preview拖入图表中,按如图连线
  • 1-x节点叫 OneMinus

ab5f32c1c4e1796e9ee44adb78b27e88.png

3. 回到界面蓝图,之前图片的Image选择的是RT_Preview,是直接使用的纹理,现在改成MT_Preview,使用新建的材质。

865eabf75d8f9f13e1313924ad882dae.png

4. 再次运行游戏,按F键可以看到如下画面,背景中的蓝白色已经去除了。

3d23be263358d9b90da203e3e396ee76.png

到了这里,我们捕获的内容基本上已经符合了预期:

  • 只捕获特定的角色
  • 背景是透明的,便于在界面上显示

然而,它还不能正直使用在游戏中,为什么?

改进

目前我们捕获的对象是场景中的玩家对象,玩家对象会移动,一旦移动我们就捕获不到了。这个问题有两个解决方法,

  • 第一个是可以把捕获控件绑在玩家身上,跟着玩家一起动。这个方案也还有问题,那就是玩家会播很多动作,可能并不是你想要捕获的,除了特殊使用目的,不建议使用。
  • 创建一个专门的捕获Actor,在其中放入特定的Mesh,然后捕获组件只捕获自己Actor中的指定Mesh或者特定Actor。此方案需要管理一个临时Actor。

接下来就以第二个方案来简述实际操作。

  1. 创建Preview的Actor:创建一个蓝图,继承Actor,命名为A_Capture_Preview。并添加SceneCaptureComponent2D组件和SkeltalMesh组件。SceneCaptureComponent2D设置TextureTarget为RT_Preview,SkeltalMesh的Mesh设置为SK_Mannequin,并调整位置和朝向如下图所示

b69c6829a7d095c64557a8c578cb20a0.png

8393b47da5eaa7a1fbe6e7ba781e5296.png

2. 去除之前场景中添加的那个SceneCapture2D对象,我们使用动态创建的方法来构建Preview Actor。首先在A_Capture_Preview的BeginPlay中设置只捕获自己的本身

d7022207612a29305928ffb78f8e5b2f.png

3. 然后我们修改一下Level Blueprint,创建这个捕获Actor,CollisionHandlingOverride可以选择AlwaysSpawn,避免因为某些原因创建失败。

6342b4dfe8f30a532ce2ef9115e192d5.png

4. 运行游戏,按F键,可以看到界面跟之前展示的一样,不同的是:

  • 玩家走动,不会影响界面
  • 场景中多了一个模型,这是我们SpawnActor引入的一个模型

d8ad29b27fdd40512cc7a0f01c5ae260.png

我们SpawnActor只是为了用于捕获,不希望主摄像机可以看到,那么这里又有两个方法:

  • 把临时Actor放远一点(这个方案有些意外的效果,后面再说)
  • 让主摄像机看不到这个临时Actor

我们先来说第二中方案,如何让主摄像机屏蔽或者说过滤掉我们得临时Actor。首先打开ACapturePreview蓝图,选中SkeletalMesh,然后在细节面板上找到Renderring选项卡,点击下面的三角形展开高级选项,如下图:

3e8e929270bb9ef0176160c22437f8d9.png

这里有两个选项,Owner No See(拥有者不可见) Only Owner See(仅拥有者可见) ,利用Owner No See这个选项,再把临时对象的Owner设置成主角Actor,那么主角就看不到这个临时Actor了(对于网络游戏,在客户端创建的临时Actor,其他人本来就看不见,所以也不用担心。)

  • 勾选Owner No See
  • 在Spawn之后,将Owner设置成主角Character

1e0f86a9636b0396e0062ba73d2e20cc.png

再次运行游戏,按F之后,已经看不到临时对象了,界面表现则是和之前一样。

这里可能有人要问了,为什么不选择OnlyOwnerSee,然后把Owner设置成自己呢?这里的问题是Owner不能设置成自己,如果要选择OnlyOwnerSee,则必须将模型与SceneCaptureComponent拆成2个Actor来实现,这也是个方法,但是需要管理两个临时对象,具体的实现就大家自行实现吧。

为什么不把临时Actor放远一点来拍摄?

在一定程度上来说,没啥问题,但是如果Actor离主相机太远,引擎会对Actor进行优化,导致捕获的效果非常糟糕,一方面是LOD的原因,一方面未启用LOD的情况下,离的足够用,也会强制优化。

f758a51d4362ebe9552014bc79834cc2.png

33d7f8caa766eec498b83658da61a2da.png

f1a80e4b04ea13e343761cf974476ed2.png

b6da0c55cbd425c5dc9712b80940187e.png

不断的将距离设置的更大之后,可以发现界面上的效果越来越差,直到不能接受。最后一项Actor离主角的距离是40000000。当然你可能不会设置那么远,只要找一个合适的位置让玩家当前看不到他就是可以接受的。如果你发现界面上的效果达不到预期的时候,就要考虑是否因为距离过大导致的。

另外,也是接下来要讲的,使用正交投影来获取2D头像的时候,对距离比透视投影要敏感非常多,距离设置到10000就有明显的区别。

02d09bb032dc5b0e03c60e3e01f83acd.png

7016cb79935915ea2af8cb9a846a333d.png

所以推荐的做法是前面说的那种,将临时对象放在离主角比较近的地方,但让主角看不到他。

2D头像的获取

3D预览使用透视投影,2D头像由于离的非常近,如果继续使用透视投影,很容易出现比例不协调的问题,比如鼻子可能非常大,其他部位特别小,不符合要求。

这个时候就要使用正交投影,继续使用刚刚的ACapturePreview对象,选中SceneCaptureComponent2D组件,找到Projection选项卡,ProjectionType选择Orthographic,选择之后OrthoWidth变得可用,这个选项类似Field,越大则范围越大,越小则范围越小。在这个模式下,相机的远近不会改变捕获的范围,需要修改OrthoWidth才有效果。例如下面的选项,设置为50,可以看到TextureTarget部分的预览。

ce7c9061ca5d919eb436c3eb66dd06f1.png

注意红框中的两个选项,PrimitiveRenderMode在前面有提到,为了方便在未运行时预览TextureTarget,这里不做修改,使用蓝图代码在运行时修改模式为ShowOnlyListCaptureEveryFrame是每帧捕获的意思,可想而知这个选项会带来相当的消耗。对于2D头像来说,是静态的,只需要捕获一帧就可以了,所以这里取消勾选。

2D头像在3D的基础上只是两点不同:

  • 投影方式修改
  • 去除CaptureEveryFrame

接下来,探讨个稍微复杂点的情况。通过上面的介绍大家基本上明白了,捕获的基本流程:

  • 创建RenderTarget
  • 创建使用RenderTarget的材质
  • 界面图像使用指定材质
  • 创建临时Actor,捕获图像,更新RenderTarget

这个过程中,我们创建了一个RenderTarget,一个材质,一个临时对象。如果我们在游戏中需要同时显示多个动态捕获的头像怎么办?那就得创建N个RenderTarget,N个材质,N个临时对象?下面就来介绍一种全动态RenderTarget+材质的创建方案,对于捕获Actor,如果各个需要的场景对镜头没有区分要求,那其实可以使用同一个,然后动态的修改SkeletonMesh就可以。

全动态生成2D头像

  1. 首先我们还是得手动创建一个RenderTarget,这个RenderTarget仅用于在编辑器中预览使用,可以继续使用前面的RT_Preview。材质也可以继续使用MT_Preview,但是需要稍作修改,在材质中的TextureSample节点上右键,并选择ConvertToParameter , 这个时候可以在左侧ParameterName出修改参数的名称,这个名称记下来,后面动态设置的时候会用到。

cd46d74da5897c78da7a9ec4e8c16dd0.png

c44f14387877a8158c3b37194cbc7d53.png

2. 打开WBP_Head蓝图,新建函数UpdateImgMat,新增Input参数,类型选择为Material Instance Dynamic, 并命名为 ImgMat

c564b69fccdc86ac0a439674f26789fb.png

并实现函数,此函数可以让我们使用动态材质来填充图像。

30d3a90d185ae4b7c1c59ad77c1c1ede.png

3. 打开Level Blueprint,新建一个函数CreateDynamicHead ,这个函数建在哪里不重要,只要有合适的时机即可

711892063a57d29308463f9ca0a15643.png

这三个节点,首先创建一个RenderTarget2D ,然后根据MT_Preview来创建一个动态材质示例,第三个节点就是把前面的RenderTarget2D传递给材质的MainTex参数。这就是我们第一步把材质中的纹理提升为参数的目的。实际上动态材质的核心就是上面三个节点,接下来需要做两件事:

  • 把材质传递给界面控件
  • 把RenderTarget传递给捕获Actor

界面已经提供了接口,调用一下即可,ACapturePreview也需要提供一个接口来接受RenderTarget数据。

4. 打开ACapturePreview,新建函数SetRenderTarget,新建参数RenderTarget,类型为TextureRenderTarget2D

b74b934843a716030ae07b162165db5f.png

函数内容也很简单

8c8079938a290f0f83de138c0b61fd44.png

接下来就是稍微改造一下Level Blueprint中的函数CreateDynamicHead 分成四个步骤:

  • 创建界面
  • 创建临时Capture对象
  • 创建动态的RenderTarget及材质
  • 将材质传递给界面对象,将RenderTarget传递给Catpture对象

a5ca51550563f629aceae86ed7212e9f.png

按键F的响应则只需要直接调用该函数

b3ef875b07390237908767fad3f639f0.png

按F运行游戏,发现头像跟之前没有任何的区别。

6de10018b524783f40a22c62acebc7f2.png

没有区别就对了,这里只是展示一种动态构建的方法,效果并没有改变。对于界面上多个头像的展示,跟前面的代码结构类似,不同的界面,使用不同的预览对象,自行设置就好。由于大家各自使用的RenderTarget和材质都是动态的,所以不会相互干扰,这是主要目的。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号