【Unity】移动端草海解决方案

      草海是开放大世界渲染的必不可少的因素,Unity 原生的 Terrain 草海效率较低,而且无法与 RVT 结合起来,无法在移动端上实现。因此我们自己搓出来一套草海系统,使用 C# 多线程辅助运算,并能支持割草、烧草等进阶玩法。草的位置、密度、类型还需要根据美术和地形的信息进行自适应。

        最终效果可以看这个演示视频(虽然这个视频美术表现力不行,但是已经可以看到 GpuTerrain基础下的实时运算的草海效果了。对于游戏开发者而言,显然能明白这个演示已经实现了既定目标)

GpuTerrain+草海_哔哩哔哩_bilibili地形用GpuTerrain渲染;草海是自定义实时生成运算+Gpu渲染。, 视频播放量 103、弹幕量 0、点赞数 0、投硬币枚数 0、收藏人数 0、转发人数 0, 视频作者 魔术师dix, 作者简介 我们这么努力,不是为了改变世界,而是为了不让世界改变我们。,相关视频:Island_G效果演示(8月版本),原型工程1.0演示视频,玲珑岛12贴图版本_1.0,Unity3D 战斗系统核心技术: 高度可扩展的技能与多Buff系统(收藏级),【中配】A*寻路算法:算法解释 - Sebastian Lague,很多3a游戏的马还不如塞尔达的真实生动,新动作又被虐了,Skyrim Special Edition-定海神针-金箍棒,【Unity3D/资产发布】拟真向URP海面渲染系统——HigWaterSystem,游戏,还是现实?icon-default.png?t=N7T8https://www.bilibili.com/video/BV1nj411D7tm

方案设计

        这里我按照 1*1 的范围将地图进行分块,每一块视为一整片草,进行整体剔除和计算。

        如上图的示例,将草分块之后,进行AOI剔除,最后得到所有可能在视野中的草(注意:这里没有进行视锥体剔除),之后再使用生成式算法得到最终的草数据(精确到每一株草的实体)。然后再直接将数据打包传入 GPU,使用之前提到过的 GPU 驱动的渲染上屏,完成草海渲染。

        这里就涉及到之前的一些实现过的架构了:

  • 需要得到地面的高度和法线,与 开放大世界的 GpuTerrain + RVT-CSDN博客 共用一张高度法线图。
  • 上屏方案 : GPU驱动的大规模静态物件渲染-CSDN博客

        除此之外,草海还需要一张密度图(表示每个格子的草的密度和类型),和 GpuTerrain 的高度法线图每个像素一一对应。

        有了上述数据之后,草海就能正常运转了。在项目前期,我们是将草海的生成式算法放到C#多线程中的,目的是为了方便调试和交互。后续功能稳定了,可以将这个算法移动到 ComputeShader 中,效率可以更高(这也是最终成型方案)。

框架与数据管理

        制作地图时,生成棋盘格,每个格子 1*1 米,每512 *512 个棋盘生成视为一个地块。统一棋盘格的尺寸,对应高度图、植被密度图的数据。

        一般来讲,对于视野远端的地块,不需要生成格子,也不需要计算。对于近处的地块,才需要生成草海,当然距离越近性能越好。按照一些文章的说法,80米的视野范围就能满足需求,如果加上视锥体剔除,视角设置为60度,那么算下来大概需要 3696 个格子参与生成计算(后面按照4000个格子计算)。

        4000 个格子,每个格子生成 64 棵草(密度最大),每颗草的数据(Transfrom数据)大约为 30 byte,算下来总共占用约 7.5 MB 内存,即便是移动端也可以接受。(为了减少对GPU的数据传输,我们其实使用了4个80*80的区域,内存占用更多,原因下文有解释)

草海系统的生命周期管理

        对于 Patch 而言,最高运算的部分在生成(以及根据玩法对数据的修改部分),因此在运行时并没有复杂逻辑,就只是在原地不动。生成之后最大的性能瓶颈就是在玩法(例如割草)上对实体的遍历。但因为能实现限制 Patch 进行刷新,所以遍历的量并不会很大。

草海编辑工具

        因为草海已经脱离了 Unity 的Terrain,所以需要一编辑工具。

  • 根据 GpuTerrain + RVT 数据直接还原地形的工具,方便美术开发。
  • 类似于 Terrain 的刷子工具,可以绘制草海密度图。
  • 草海类型配置:配置不同类型的草,以及其出现频率,可以实时预览。
  • 运行时调试工具,方便运行时查找问题。
草海密度图示例

        

其他细节问题        

        这里记录一些细节问题,可以帮助大家实现时做参考。

面片草还是网格草?

  • 面片草顶点数少,但是依赖 AlphaTest,这一步在很多移动设备上非常昂贵。
  • 网格草顶点数高,但因为是 实体渲染,性能表现远远优于 AlphaTest。

        此外,对于某些表现效果(例如高光、碰撞弯曲)上,面片制作起来困难;在视角上,面片草存在穿帮死角,大致上只适合固定视角。所以还是建议使用网格草。

草海密度图存的什么数据?

        草海密度图我这里将每个像素的 rgba 各设置为一种类型的草的密度,每一个草的密度取值从 0~256表示其密度。在生成草海数据时,查询到某个位置有颜色,则根据通道(例如 R通道 表示杂草,G表示花、B表示地衣、小石头)密度算出需要生成特定类型、特定数量的实体。生成完成之后,将数据收集起来统一传给 GPU。

        实际上,每一个通道的256的值对于密度还是太多了,本来一个地块最多64个实体,精度肯定超标了。后续如果要优化,完全可以将密度的精度降低到8,再将其他位记录其他地图数据。

生成式算法

        成式算法其实是根据项目相关,所以不必去网上特意查找。一般来讲,只需要设置一个随机方式(噪声图、或者固定随机种子),根据算法进行随机。只要满足一个要求就行:每一次生成的位置、旋转、缩放都是相同的结果。

        同时,在生成时可能需要根据地形信息有所变化,这点也需要考虑到。

草海实时重建的频率

        在计算哪些草块(Pitch)需要计算时,为了减少对 GPU 交互的频率,改为周围 80m 的棋盘格都计算了一起扔给 GPU。因此这样占用的内存大了4倍,但数据交互频率显著降低,就不用每帧都生成一次数据了。否则,一旦加上视锥体剔除,那么一旦相机旋转了,每帧都要实时计算一次、传值,数据大了还是很不划算的(实际上,视锥体剔除我们也试过,数据量少了很多,但传输数据的频率就大幅提升了)。

        当然这是妥协,如果在 GPU 里计算草海就没这个问题了。所以,最终的完整版本还是要在 ComputeShader 里生成草海。

图片数据读取

        GPU端直接通过 Texture.GetPixel 也太蠢了,效率还低。这里我们的做法是将图片转换成 NativeArray 然后再在多线程读取。这里需要注意的时,如果是多线程读取 NativeArray ,需要一点点操作:

NativeArray<Color32> HeightMap = texture.GetPixelData<Color32>(0);//获取图片的原始数据//根据已有的数据生成不安全的只读 NativeArray,否则多线程无法使用。
NativeArray<Color32> ConvertedHeightMap= NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<Color32>(HeightMap.GetUnsafeReadOnlyPtr(), HeightMap.Length, Allocator.Invalid);#if ENABLE_UNITY_COLLECTIONS_CHECKS 
//只有编辑器下才存在 SetAtomicSafetyHandle 
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref ConvertedHeightMap, AtomicSafetyHandle.Create());
#endif

        按照上图示例,就可以在多线程中使用 Unity 的 NativeArray 了。要注意的是,SetAtomicSafetyHandle 只在编辑器下使用,在真机上是会报错的。参考:NativeArrayUnsafeUtility seems to not work in builds - Unity Engine - Unity Discussions

割草烧草如何实现

        类似于塞尔达的割草、烧草效果,如果是在 CPU 端是比较好弄的。直接可以将技能(或者玩家交互)的计算结果记录下来,然后调用一次草海刷新,然后就能根据新的数据计算出结果,再传给 GPU 即可。

        如果是在 GPU 端实现,有一个简单的方案:从头顶照一个相机下来使其能覆盖所有的草。然后所有的技能一般都会有一些特效显示,这个相机就专门照这个特效。然后 GPU 直接就能读取到这个相机生成的图片,就可以直接拿来计算了,根本不用写 CPU 和 GPU 的交互,非常地方便。

模型上种草如何实现

        目前只实现了地形上种草,而模型上种草没有实现。

        模型上种草其实也有过设计,需要提前将模型进行烘培,且需要让美术指定模型的哪些地方是可以种草的(类似于刷 UV 图)。然后在渲染模型时,根据模型的草皮图、法线等直接生成草海,后续的步骤和地皮生成草海类似了。

参考文章

移动端草海的渲染方案(一)_terrain detail 交互-CSDN博客

移动端草海的渲染方案(二)_nature renderer-CSDN博客

移动端草海的渲染方案(三)-CSDN博客

移动端草海的渲染方案(四)_恶毒的狗 草海-CSDN博客

移动端草海的渲染方案(五)-CSDN博客

TA实践分享:材质与渲染——植物与风(Unity+UE)

【C#】并行编程实战:序章_并行编程实战基于c#8-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/52330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库内容保密检查系统:及时发现“潜在”安全威胁

日前&#xff0c;国内专注于保密与非密领域的分级保护、等级保护、业务连续性安全和大数据安全产品解决方案与相关技术研究开发的领军企业——国联易安自主研发的国联数据库内容保密检查系统V1.0通过国保局涉密检测&#xff0c;获得涉密信息系统产品检测证书。其主要具备以下主…

输入名字转换成对象

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>输入名字转换成对象</title> </head>…

力扣算法总结

直接放别人简单易懂的总结&#xff0c;比我自己描述得好 代码随想录 (programmercarl.com) 崔添翼 (Tianyi Cui) 背包问题九讲 2.0 beta1.2 - jggnice! - 博客园 (cnblogs.com) 1.01背包 优化成一维就是利用之前上一维计算出来的数据&#xff0c;在不损害后面还要用的上一维…

【前端基础篇】JavaScript之BOM介绍

文章目录 浏览器对象模型&#xff08;BOM&#xff09;介绍1. 什么是BOM&#xff1f;2. Window 对象2.1 弹出框2.1.1 警告框2.1.2 确认框2.1.3 提示框 2.2 定时事件2.2.1 延时器2.2.2 定时器 2.3 Window 对象其他常用属性与方法2.3.1 获取窗口尺寸2.3.2 打开新窗口与关闭窗口2.3…

【JS|第25期】探索HTTP POST请求:请求体的演变与应用

日期&#xff1a;2024年8月16日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

iOS RunLoop

一:什么是Runloop Runloop&#xff0c;正如其名&#xff0c;loop表示某种循环&#xff0c;和run放在一起就表示一直在运行着的循环 二:Runloop的创建? iOS并没有提供Runloop的创建方法,因为创建了现场自然会有一个Runloop. 所以只有获取Runloop的方法: NSRunLoop * runloo…

C# 循环访问目录树详解与示例

文章目录 一、目录树遍历的概念二、使用System.IO命名空间三、DirectoryInfo和FileInfo类四、递归遍历目录树五、示例&#xff1a;列出目录树中的所有文件和文件夹六、异常处理七、迭代方法八、总结 在C#中&#xff0c;访问文件系统是常见的需求之一。有时我们需要遍历目录树以…

kafak集群搭建-基于zookeeper方式

kafak集群搭建-基于zookeeper方式 1、准备3个kafka2、修改配置文件2.1、修改配置文件 3、启动测试3.1、先启动zookeeper3.2、再启动三台kafka 4、SpringBoot集成kafka集群4.1、项目配置文件application.yml4.2、kafka配置类4.3、启动SpringBoot程序 5、kafka集群架构分析6、查看…

【kubernetes】相关pod的创建和命令

【书写方法】&#xff1a; 管理使用k8s集群时&#xff0c;创建资源的Yaml文件非常重要&#xff0c;如何快速手写呢&#xff1f; 根据命令提示书写&#xff1a; kubectl explain [资源名称]例如打算写pod资源文件时&#xff0c;可查看如下&#xff1a; # 查看pod下所有字段 …

Google Colab快速使用

Google Colab快速使用 1. 引言2. Jupyter笔记本的创建3. 上传代码和数据集4. Colab常规指令 1. 引言 Google Colab是谷歌提供的免费Jupyter&#xff0c;很类似于Linux系统这些在终端界面操纵的感觉&#xff0c;不需要深度学习环境配置就可以使用&#xff0c;完全基于云端运行。…

在Windows Server 2012 R2上安装.NET Framework 3.5

在Windows Server 2012 R2上安装.NET Framework 3.5&#xff0c;可以按照以下步骤进行&#xff1a; 打开服务器管理器&#xff1a; 首先&#xff0c;登录到Windows Server 2012 R2的服务器。然后&#xff0c;打开“服务器管理器”。添加角色和功能&#xff1a; 在“服务器管理…

针对防火墙IPSec业务不通或业务丢包问题,防火墙如何做流量统计、远程抓包、报文示踪

问题描述 针对防火墙IPSec业务不通或业务丢包问题&#xff0c;防火墙如何做流量统计、远程抓包、报文示踪 解决方案 1&#xff09;配置流统和远程抓包用的ACL&#xff1b; system [sysname] acl 3555 [sysname-acl-adv-3555] rule permit icmp source 10.82.100.215 0 destin…

结构型模式之代理模式

一、概述 1、代理模式&#xff1a;给某一个对象提供一个代理或占位符&#xff0c;并由代理对象来控制对原对象的访问。 2、代理对象在客户端和目标对象之间起到中介作用 3、引入一个新的代理对象&#xff0c;代理模式的主要目的是在不改变原始对象接口的前提下&#xff0c;增…

[C语言]一、C语言基础(函数)

G:\Cpp\C语言精讲 6. 函数 6.1函数的基本使用 6.1.1 为什么需要函数 《街霸》游戏中&#xff0c;每次人物出拳、出脚或跳跃等动作都需要编写50-80行的代码&#xff0c;在每次出拳、出脚或跳跃的地方都需要重复地编写这50-80行代码&#xff0c;这样程序会变得很臃肿&#xff…

通过Python绘制不同数据类型适合的可视化图表

在数据可视化中&#xff0c;对于描述数值变量与数值变量之间的关系常见的有散点图和热力图&#xff0c;以及描述数值变量与分类变量之间的关系常见的有条形图&#xff0c;饼图和折线图&#xff0c;可以通过使用Python的matplotlib和seaborn库来绘制图表进行可视化表达&#xff…

如何使用ssm实现ssm框架的购物网站+vue

TOC ssm113ssm框架的购物网站vue 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。…

如何评估Redis的性能

如果系统中出现了大 key、热 key 等&#xff0c;往往会导致 Redis 变慢&#xff0c;但是这个慢该如何界定&#xff1f;多久算慢&#xff1f;1秒还是3秒&#xff1f; 这个肯定是没有标准答案&#xff0c;因为这个和你的硬件设备有关。 硬件差一些&#xff0c;平时响应时间都是…

[C++] 初识 智能指针

标题&#xff1a;[C] 初识 智能指针 水墨不写bug 目录 一、前言 二、智能指针 1. 什么是RAII&#xff1f; 2.智能指针分类 三、智能指针简介 1.std::auto_ptr 2.std::unique_ptr 3.std::shared_ptr 正文开始&#xff1a; 一、前言 C智能指针的出现是有一定的背景的&am…

今天你City了吗?维乐Angel Revo带你穿梭都市自由随风~

当7月的热浪在都市中翻滚&#xff0c;你是否渴望逃离钢筋水泥的束缚&#xff0c;寻找一片属于自己的绿意盎然&#xff1f;今天你City了吗&#xff1f;快带上VELO Angel Revo一起抓住夏日的尾巴&#xff0c;用一场骑行与这座城市的风景共舞&#xff01;      轻巧出行&#…

Centos安装Jenkins教程详解版(JDK8+Jenkins2.346.1)

本教程基于 JDK8 和 Jenkins2.346.1 JDK安装 下载OpenJDK8文件 wget https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jdk/x64/linux/OpenJDK8U-jdk_x64_linux_hotspot_8u422b05.tar.gz解压到指定目录 # 创建目录 mkdir -p /usr/local/software# 解压文件到指定目录&#…