3D引擎中LOD技术的理论基础
虚幻引擎中细节优化相关的技术有 level of details 和 level streaming 两种,他们的共同点在于:当相机视角靠近某一片区域时,那里的细节才会被临时加载,从而节省资源(cpu,内存,外存)。
- level of details:或LOD,细节层次,给每个网格体准备不同视距下的形状
- level streaming:类似Photoshop的图层概念,将物体分入不同的场景,选择性加载
level streaming技术在上一篇《虚幻引擎之场景动态加载》讲过了,本文介绍LOD技术:
- Level Of Details:细节层次
- 手动LOD导入
- 自动LOD生成
- LOD碰撞检测的一致性
- LOD打包:选择性降级
Level Of Details:细节层次
理论基础:当物体远离眼睛(相机)时,它占用的屏幕像素减少,许多几何细节(三角形)有所删减,但每一帧仍然计算它全部的细节,被删减的那部分所占用的算力被浪费。此时可以准备一个“删减版”的几何体,替代“完整版”的几何体,节省计算资源(cpu/gpu)的同时,屏幕上的输出效果一样。但存储LODs需要额外的一些内存资源。
LOD技术的应用场景在于,当一个mesh占用屏幕像素很小时(如相机远离mesh),减少mesh的三角形数,节省资源。随便打开一个static mesh,进入mesh编辑器,默认处于LOD0。LOD0是最高细节的lod,拥有最多的三角形和点数,LOD1、LOD2依次递减。引擎根据mesh占用屏幕的像素与屏幕像素的比例来实时判断与切换不同层级的LOD,这个比例叫screen size。
https://docs.unrealengine.com/en-US/Engine/Content/Types/StaticMeshes/HowTo/LODs/index.html
手动LOD导入
在mesh编辑器的detail面板中可以导入fbx等3d模型,同时设置Screen Size。比如当一个mesh离屏幕非常远时想要自动隐藏,可以导入一个空的网格体,然后设置screen size为很小的值(比如0.01)。
mesh editor >> Details >> LOD Settings >> LOD Import
https://docs.unrealengine.com/en-US/Engine/Content/Importing/FBX/HowTo/ImportingLODs/index.html
自动LOD生成
理论基础:和手动导入的lod不同,自动生成的lods全部基于lod0,可以被临时计算出来,无需存储,从而可以减小打包后的体积(节省外存)。官方推荐的做法是自动生成LOD,因为引擎为我们提供了基于四叉树的边坍缩算法,可以根据不同的screen size,自动生成不同层级的lod,我们只需要设置lod的数量即可。
Tip:设置了自动LOD以后,移动相机几乎察觉不到lod的切换(这也是lod的本意),但可以通过viewport左上角的实时数据来观察效果。
mesh editor >> Details >> LOD Settings >> Number of LODs
既可以手动调lod参数,也可以使用模板快速创建lods,引擎提供了几个可用的模板,在LOD Group中可以选择(会覆盖当前设置)。想修改默认的模板,需要修改相应的引擎参数:
mesh editor >> Details >> LOD Settings >> LOD Group
模板文件路径:
[project] >> Intermediate/ >> Config/ >> CoalescedSourceConfigs/ >> Engine.ini >> [StaticMeshLODSettings]
1.[StaticMeshLODSettings]
2.LevelArchitecture=(NumLODs=4,LightMapResolution=32,LODPercentTriangles=50,PixelError=12,SilhouetteImportance=4,Name=LOCTEXT("LevelArchitectureLOD","Level Architecture"))
3.SmallProp=(NumLODs=4,LODPercentTriangles=50,PixelError=10,Name=LOCTEXT("SmallPropLOD","Small Prop"))
4.LargeProp=(NumLODs=4,LODPercentTriangles=50,PixelError=10,Name=LOCTEXT("LargePropLOD","Large Prop"))
5.Deco=(NumLODs=4,LODPercentTriangles=50,PixelError=10,Name=LOCTEXT("DecoLOD","Deco"))
6.Vista=(NumLODs=1,Name=LOCTEXT("VistaLOD","Vista"))
7.Foliage=(NumLODs=1,Name=LOCTEXT("FoliageLOD","Foliage"))
8.HighDetail=(NumLODs=6,LODPercentTriangles=50,PixelError=6,Name=LOCTEXT("HighDetailLOD","High Detail"))
例如,“SmallProp”模板的效果如下:
https://docs.unrealengine.com/en-US/Engine/Content/Types/StaticMeshes/HowTo/AutomaticLODGeneration/index.html
LOD碰撞检测的一致性
lod只是在屏幕上的输出做优化,并不影响它的碰撞属性,不同层级的lod的碰撞属性必须一致,通常是基于LOD0(即物体本来的形状)
mesh editor >> Details >> General Settings >> LOD for collision
https://docs.unrealengine.com/en-US/Engine/Content/Types/StaticMeshes/HowTo/LODCollision/index.html
LOD打包:选择性降级
理论基础:对于某些低端平台,可能用不到lod0,甚至lod1,可以选择性删除最高级的lod以节省内存/外存空间。
- 节省内存(运行时):mesh editor >> Details >> LOD Settings >> Minimum LOD
- 节省外存(打包后):[project] >> Config/ >> DefaultEngine.ini >> [Console Variables]
在[Console Variables]字段下的代码:
1.[ConsoleVariables]
2.; Strip render data for LODs below the MinLOD for the target platform during cooking.
3.r.StaticMesh.StripMinLodDataDuringCooking=1
4.r.SkeletalMesh.StripMinLodDataDuringCooking=1
https://docs.unrealengine.com/en-US/Engine/Content/Types/StaticMeshes/HowTo/StripUnUsedLOD/index.html
<完>
- POJ 2478Farey Sequence
- cookie、session、token三者使用
- SpringCloud注册中心集群搭建
- SpringCloud配置中心集群搭建
- HDU1846 Brave Game
- 拉格朗日插值
- python爬虫入门(二)Opener和Requests
- python爬虫入门(三)XPATH和BeautifulSoup4
- python爬虫入门(四)利用多线程爬虫
- LOJ #115. 无源汇有上下界可行流
- 数据库改名系列(数据库名,逻辑名,物理文件名)
- BZOJ1468: Tree
- 洛谷P3806 【模板】点分治1
- 探索ASP.NET MVC5系列之~~~5.缓存篇(页面缓存+二级缓存)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android布局之表格布局TableLayout详解
- 简单实现Android倒计时效果
- Android实现单页面浮层可拖动view的一种方法
- 排查 Node.js 服务内存泄漏,没想到竟是它?
- Android判断网络状态的代码
- Android开发实现布局中为控件添加选择器的方法
- Android控制文本输入框最多输入10个字符长度
- Elasticsearch 内部数据结构深度解读
- 关于 Elasticsearch 段合并,这一篇说透了!
- 解了这十道C语言题,你敢说你精通C语言?
- 微服务中使用Maven BOM来管理你的服务版本
- 设计模式之代理模式(文末赠书)
- 使用Spring IoC容器:选BeanFactory还是ApplicationContext?
- Stream使用这么久,它是如何提高遍历集合效率?
- FastDFS 分布式文件系统入门