【从UnityURP开始探索游戏渲染】专栏-直达
纹理流送技术,其核心在于动态加载纹理的 Mipmap 级别,而非一次性加载所有层级的纹理数据。传统 Mipmap 会预生成并加载所有层级的纹理(从原始尺寸到最小尺寸),占用显存为原始纹理的 4/3 倍。
Mipmap Streaming 优化机制:
分级加载:
- 根据物体与摄像机的距离,仅加载当前所需的 Mip 层级,其他层级按需从磁盘异步加载。
DDX/DDY 计算:
- GPU 通过内部值 DDX 和 DDY(基于像素的 UV 坐标变化率)动态决定采样所需的 Mip 层级,匹配像素覆盖的 Texel 大小。
纹理金字塔管理:
- Unity 维护一个纹理金字塔(14 个层级,最高支持 8192x8192),运行时仅激活必要的层级。
解决的问题
显存优化:
- 避免一次性加载所有 Mip 层级,显著降低显存占用,尤其对移动端(如 HUAWEI P30 测试案例)和高分辨率纹理场景至关重要。
带宽效率:
- 减少 GPU 带宽压力,仅传输可见层级的纹理数据,提升渲染性能。
摩尔纹消除:
- 通过动态匹配 Mip 层级,避免远距离物体因像素与 Texel 不匹配产生的锯齿和摩尔纹。
原理详解
当物体远离摄像机时主要流程:
- GPU 通过 DDX/DDY 计算当前像素覆盖的 Texel 面积,选择 Mip 10(512x512)。
- Unity 释放更高层级的显存(如 Mip 11-12),从磁盘按需加载更低层级(如 Mip 9)。
- 若物体突然靠近,高优先级层级的 Mip 会优先加载,避免视觉卡顿。
Mip 层级选择计算
- GPU 通过 DDX/DDY 导数计算当前像素的 UV 变化率,推导出纹理采样所需的理想 Mip 层级(记为
MipLevelideal
)。例如,当物体远离摄像机时,UV 变化率降低,MipLevelideal
值增大(选择更低分辨率的层级)。
层级动态加载与卸载
- Unity 仅将
MipLevelideal
及其相邻层级(如 ±1 级)加载到显存,其他层级保留在磁盘。例如:- 若
MipLevelideal=4
(对应 256x256 纹理),则加载 Mip 3-5 级,卸载其他层级。 - 通过
Texture2D.streamingMipmaps
属性可强制指定加载特定层级(如MipLevelideal+2
)。
- 若
内存预算控制
- 系统根据
QualitySettings.streamingMipmapsMemoryBudget
全局预算动态调整层级。若总纹理内存超限,自动降低非关键纹理的 Mip 层级(如将MipLevelideal
强制偏移 +1)。
具体示例:开放世界地形纹理流送
假设场景中存在 2048x2048 的地形纹理(Mip 0-11 级),摄像机由近及远移动:
-
近距离阶段
- 计算得
MipLevelideal=2
(512x512),加载 Mip 1-3 级。 - 显存占用:
512² + 256² + 1024²
(约 1.75MB)。
- 计算得
-
中距离阶段
- 摄像机拉远,
MipLevelideal
变为 5(64x64),卸载 Mip 1-3,加载 Mip 4-6。 - 显存降至
64² + 32² + 128²
(约 24KB)。
- 摄像机拉远,
-
突发情况处理
若摄像机快速切近,通过
Texture.streamingMipmapPriority
提高优先级,强制预加载 Mip 0-2 级以避免卡顿。
关键 API 与配置
-
纹理设置 :在 Inspector 中启用
Streaming Mip Maps
并设置Mip Map Priority
(默认 0,范围 -128 到 127)。 -
代码控制:
csharpcsharp // 强制某纹理使用 Mip 5 级 Texture2D tex = GetComponent<Renderer>().material.mainTexture as Texture2D; tex.streamingMipmaps = true; tex.RequestMipLevel(5);// 异步加载
-
摄像机覆盖 :通过
Streaming Controller
组件设置Mipmap Bias
,全局偏移所有纹理的MipLevelideal
(如 +2 级以降低画质)
纹理金字塔的共享机制
Unity 的纹理金字塔(Mipmap 层级)是基于纹理资源本身维护的,而非每个物体单独维护。所有使用同一纹理的物体共享同一套纹理金字塔数据,运行时根据物体的屏幕空间覆盖率和摄像机距离动态激活所需的 Mip 层级。
资源级管理
- 每个导入的纹理(如 2048x2048 的 PNG)在 Unity 中生成独立的 Mipmap 金字塔(14 个层级)。这些层级存储在磁盘和内存中,作为纹理资源的固有属性,而非物体属性。
动态层级激活
- GPU 通过
DDX/DDY
计算当前像素的 UV 变化率,推导出适合的 Mip 层级(如远距离物体使用 Mip 5 级)。 - Unity 的 Mipmap Streaming 系统仅加载当前需要的层级(如 Mip 4-6),其他层级保留在磁盘或按需异步加载。
显存优化
- 多个物体共享同一纹理时,显存中仅存储该纹理的激活层级。例如:
- 物体 A 和 B 使用纹理
Tex_01
,当前需 Mip 3 级,显存仅保留 512x512 版本。 - 物体 C 使用同一纹理但需 Mip 5 级,系统复用已有金字塔数据,无需重复加载。
- 物体 A 和 B 使用纹理
示例场景分析
假设场景中有 100 个岩石模型共用同一 4K 纹理:
- 未启用 Streaming:所有 14 个 Mip 层级(总计约 5.3MB)加载到显存,无论物体远近。
- 启用 Streaming :
- 近处岩石使用 Mip 2(1024x1024),远处使用 Mip 6(256x256)。
- 显存仅保留 Mip 2-4 和 Mip 5-7,其他层级卸载,总占用降至 1.2MB。
性能影响与配置
- 全局控制参数 :
QualitySettings.streamingMipmapsMemoryBudget
限制所有纹理的流送内存总和,超限时自动降低非关键纹理的层级。 - 优先级设置 :通过
Texture.mipMapPriority
调整纹理加载顺序,确保重要纹理(如角色贴图)优先获取高精度层级。
这种设计避免了重复资源存储,同时通过动态流送优化显存和带宽
使用场景与限制
适用场景
- 开放世界或大场景:远处物体自动使用低分辨率 Mip 层级,减少不必要的细节加载。
- 移动端项目:显存和带宽受限的设备(如 Unity 测试案例中的 HUAWEI P30)。
- 高分辨率纹理:如 4K/8K 纹理,传统全加载方式显存消耗过大。
限制
- 内存额外开销:需存储所有 Mip 层级到磁盘,占用约 33% 额外空间。
- 加载延迟风险:动态流送可能导致远处物体短暂显示低清纹理(需优化加载优先级)。
- UI 纹理不适用:UI 元素通常需保持高清,关闭 Mipmap 更高效。
具体示例与实现
示例 1:基础配置
在 Unity URP 中启用 Mipmap Streaming:
- 纹理导入设置 :勾选
Generate Mip Maps
和Streaming Mipmaps
,设置Mip Map Priority
(优先级越高越早加载)。 - 代码控制 :通过
Texture.streamingMipmaps
API 动态启用/禁用流送。
示例 2:性能对比
- 未启用 Streaming:2048x2048 纹理加载所有 12 个 Mip 层级(显存占用约 5.3MB)。
- 启用 Streaming:仅加载 Mip 10(512x512)时显存占用降至 0.8MB,随距离变化动态加载其他层级。
总结
Mipmap Streaming 通过动态管理纹理金字塔,平衡了显存占用与渲染质量,是 URP 管线中优化大规模场景的关键技术。其核心优势在于按需加载,但需注意磁盘空间和加载延迟的权衡
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)