ue4 classuobject没有成员beginplay_[UE4]仿War3地形纹理绘制

本文介绍了如何在UE4中模仿War3的地形纹理绘制,通过自定义资源存储地图信息,使用HierarchicalInstancedStaticMeshComponent,设置RenderTarget,并处理网格单位的纹理信息。在实现过程中,涉及到了地形纹理的混合、Shader的使用以及如何处理网格边缘缝隙等问题。
摘要由CSDN通过智能技术生成

fa5ddd35044bf177f55f6f700d704310.png

学习ue4有段时间了,准备实现一些以前看得比较有趣的效果,最近研究如何实现实时绘制War3地表效果,War3本身的绘制功能十分强大,这里就只实现了拙劣的简化版本,先来看视频效果,录屏软件有时候会占用鼠标输入,压缩的效果也不好

1123571a482273ebdce9e828e42f938b.png
绘制地形纹理https://www.zhihu.com/video/1146443021070598144

不同于魔兽争霸地图一个网格单位支持3种混合纹理(下图分别是草地,坑挖泥土和草色泥土一起混合),如果是手游上则可以折中为2种混合纹理减少压力同时也满足大部分MOD地图的需求

488bbf567f7deedb5c5dfbb882c62e7f.png
中间网格实现了三种混合纹理

基本思路为

1.定义地图信息存储对象

对于每一个地图我们希望能存储一些信息,如地图大小,默认war3支持如下大小

123b29acdf6c7461a1bce5a3159a04e0.png

可以看到war3以32为大小创建,假设我们创建一个128*128网格的地图,那么就需要一个可创建资源来保存128*128个网格的信息(如高度和地形纹理),在ue4中创建一个自定义资源比较简单

首先我们定义一个类需要继承UObject即可,用来保存每个网格的信息

06f971bd67b08369df5cbd8ec2a275d0.png

然后在Editor模块继承UFactory,FactoryCreateNew直接NewObject返回就可以了,之后右键在Miscellaneous分类就可以创建一个WLevelData数据储存我们的地形信息

1af5f377808355322ef91eaf1aead8ac.png

d6da8a1303cc4ce93d01ad3eabbfb0b3.png
自定义资源文件

使用自定义资源的好处就是即使是Runtime下,资源在Editor中也可以修改,我们通过绘制功能修改自定义资源中每个单位网格存储的信息,载入并编辑地图时候读取存储信息

2.HierarchicalInstancedStaticMeshComponent仿War3每个网格单位

这里先讨论平面,因为一开始拿平面实现是最容易debug的,从内置的模型可以看出魔兽争霸每个网格单位大小128*128

756f3833ff9946cfce22a8729631b0d7.png
图截取自模型提取

所以我们需要准备几个128*128的基本单位作HierarchicalInstancedStaticMeshComponent(hismc)的mesh,这里虚幻有个坑,如果hismc包含N多Instance(如实现一个256*256地图),那么你如果你在蓝图编辑器或detail panel点击任何一个component,内存会直接爆掉,推荐隐藏该选项并且千万别点击展开detail,测试环境为4.22

77c15ff00ade5f48706b34f9de8e3994.png

然后按照128为间距添加instance即可

我们还需要按需求配置hismc的参数,比较重要的是

InstanceEndCullDistance,我在BeginPlay里把该值设为2100

52f64347c0cec89d83c30bfde4e4952c.png
游戏中11*11大概为整个屏幕

第二个就是bEnableDensityScaling = 0;

b22b11413815fad555f64cb92b39f3ea.png
源码注释说带碰撞的设置为0

3.RenderTarget存储每个网格单位的地形纹理信息,通过坐标转换采样

如果需要实现两个地形纹理混合我们需要在每个网格定义如下

93888d27b4a3d3c12e022a797900a5b0.png

c1bdfb0c6e47b88fe46b3ae394539ef4.png
请无视灵魂笔迹

我们先分析自带的岩石贴图,我们把他拆解为2个部分,mask部分和solid部分,PatternIndex就是指代每个网格索引的贴图样式,从上图就是0到15,说一下mask部分,可能大部分人第一眼看过去不知道这是什么的,举例12索引为例子,12的二进制写作1100, 1的二进制可以写作0001,按2*2排

12 | 1

87e11acb391c127ed9bfb81c2db826f6.png

11 | 00

00 | 01

其实1就对应存在,0不存在然后地形pattern按照0,1,2,3 | 4,5,6,7 | 12,13,14,15 排列

pattern如此使用,然后index则对应选择哪个纹理(如岩石,草地)

我们打开World Editor可以看到一个默认地图带的以下几个可绘制纹理

dd299ef7a57da0d3bf9391dc80c3dbad.png
这里边界为美观我们拿可以模型代替

11265c94de9ba64fddfe04fcd87fbbdf.png

每个地形纹理占用两个数,偶数index代表mask部分,奇数index代表solid部分,如果需要其他地形纹理需要合并为一个flipbook

通过世界坐标转换到我们存储的RenderTarget uv中,这一步获取每个网格的Layer12TexIndex和PatternIndex,即上面提到的FWTerrainUnitData

5bfb39d81819046a9c83c385549ba85a.png

然后根据这四个值进行uv偏移计算,需要注意的是如果layer2(即顶层地形纹理)的PatternIndex为0时候,其实可以省略一次LUT,war3地形贴图是针对特定相机位置制作的,这里我们用unlit材质就可以了,实现下来shader也不昂贵,百元机也可以60fps, 如果针对性能好一些的手机实现PBR等,则需要自己创建类似war3的地形纹理贴图(diffuse,normal,roughness)等

86ee89d7aebb14f6a64a585840bfff28.png

5b915d624c804fc546886fe9d207d997.png
可怕的连连看

这里需要调整2个地方来降低网格边缘的缝隙效果

1.调整为合成地表贴图为NoMipmaps,否则移动时候边缘效果非常差

00572898fa5abdd147e4218f7de6c2b4.png

2.膨胀单位网格uv边缘,最早在shaderbit看到 Automated and Improved UV Dilation ,

2692c4a35d2ee851b8192b05430f0ad0.png

然而我们处理这种地形只需要对uv边缘处理就可以了,我这里用的贴图是1024*1024,对应每个小块则为1024/16=64,则对clamp(uv,1.f/64, 1-1.f/64);就可以了,针对特定相机位置的RTS游戏足够了

4.绘制中的添加和删除纹理信息到RenderTarget

虚幻的蓝图自带的draw canvas to render target其实功能已经很强了,如果我们需要把FWTerrainUnitData转换为Pixel就已经够用了,然而追求绘制效率(有耐心的话可以alt+G一路看draw canvas下去)和避免连连看,还是上手cpp吧,首先如果是要实时绘制,需要在Runtime申请一个RenderTarget,如果是已经绘制完作为成品则直接在编辑器中create static texture传贴图参数就可以了

这里不得不提ue4另一个坑,在编辑器内新建的RenderTarget,当作为shader纹理参数时候,默认的sampler type是color,且无法更改, 当在电脑测试时候作为索引是没有问题的,手机上则会乱序.解决办法可以用一张默认贴图去掉sRGB当做默认值,然后游戏开始改贴图参数

游戏中创建一个索引贴图如下

52512afcf30ecb2c9afe223beccef608.png

注意点就是SRGB=false, LODGroup为Pixels2D,这里我们用的索引为0-15,用rgba8压缩就足够了,减少手机压力

压缩0-15索引到rgba8如下,我测试的时候发现如果是Runtime创建的RGBA8 RT,则索引为0-31在shader是可以解压正常使用的,但是0-63就出现乱序了

5b6ac8b775e4f03d2f2b6bd8593ff37c.png

更新索引贴图的部分区域如下,如我们每次绘制,只需要更新2*2区域即可

7ab9c4d972a13b997dd725a8dafbe93a.png

添加贴图的思路

首先我们需要将触碰点的世界坐标转换到纹理uv,然后拉该网格边邻的2*2的信息,注意边界不允许绘制

e115492b7dcdce2671a2940d79752bf2.png

每次我们增加这四个网格依次为1,2,4,8,因为这几个数对应2进制能形成基本图案

00 00

01 10

01 10

00 00

f9fe68f4ef5ee8cc10bf4cc797a22ea1.png
对应1,2,4,8

然而添加是有约束的,约束条件为如果4个网格中任意一个添加对应数量后索引>15,则绘制不成立,典型的就是0,0,0,0->1,2,4,8->2,4,8,16 可以看到16>15,则第二次绘制不成立, 如果绘制成立,我们就Flush 2*2网格部分到RenderTarget

8fec94b1375610909e700e4484d22e3b.png

cfd34ddff991a97176c8f79c0c4d1097.png

其次当pattern index变为mask部分的15时候,我们需要递进为solid部分,就是Texture Index+=1,然后赋予其pattern index一个随机值避免纹理重复,solid部分的pattern index不是均匀的分布的,war3中大部分地形纹理用的是0,4,8,12

我们需要增加0,4,8,12相对于其他索引的比例

74b2889f0ca014623c028e04ef0b7377.png

850fe0f5b01cced9bf5e48c30f7482d9.png

可以看到war3编辑器中草地带坑的pattern部分占比例非常少,我们通过随机数模拟该效果,Seed的值传入该网格的在TArray的索引来保证一致性

305eb70447ef4913263a8d7415e105e1.png

移除纹理的思路

移除纹理发生在当绘制纹理和底层纹理重复时候,这时候就需要减少顶层纹理pattern index 1,2,4,8, 可惜我并没找到约束规律,而是通过打表枚举所有可能的情况然后检查, 希望评论区讨论

05d02ce42a741a0f22316166316bb71e.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值