设为首页|收藏本站|
开启左侧

[问答] UE4中的虚拟纹理 —— Virtual Texturing in UE4

[复制链接]
74166 7
大埔人在惠 发表于 2022-5-20 17:31:03 | 只看该作者 打印 上一主题 下一主题
 
前言:本文记录的是笔者个人对于在UE4中使用虚拟纹理的一些总结和归纳,文中所有观点和结论仅代表个人见解,作为自学笔记和日后反思参考,文中可能存在诸多谬误之处,并非作为学习虚幻引擎的绝对有效性指导。
目录

0. Virtual Texture 概述
0.1 什么是 Virtual Texture
0.2 UE4 中的 Virtual Texture
0.3 开启 Virtual Texture
0.4 Virtual Texture 实现流程简述
0.5 调试相关
1. Streaming Virtual Texture ( SVT )
1.1 什么是 SVT
1.2 何时(不)使用 SVT
1.3 SVT 细节
2. Runtime Virtual Texture ( RVT )
2.1 什么是 RVT
2.2 RVT 应用
2.3 RVT 细节
2.4 RVT 参考
0. Virtual Texture 概述

本文作为了解虚幻引擎 Virtual Texture 功能的实用快速指南编写。
0.1 什么是 Virtual Texture

虚拟纹理(Virtual Texture)是一种以时间换空间的纹理流送技术,它最大的好处在于能够让我们使用多种高分辨率纹理,而不像传统流送一样受到内存和带宽限制。
传统流送(Mipmap Streaming)在需要使用某个纹理时,会一次性将对应mip的整个纹理全部加载到GPU内存中,即使使用这个纹理的对象只会被看到一小部分,这样的做法有时会造成很大的流送冗余。而虚拟纹理(Virtual Texture)通过预先将纹理划分为更小的区块,在流送纹理时先分析看到的对象,只流送能看见的那部分纹理,以最大限度地去除流送冗余,节省出更多的内存空间来。
由于纹理流送的方式发生了改变,能够使用的理论(虚拟)内存成数量级增长。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第1张图片
由于要进行对象分析,将纹理流送网格化,以及从收集到的纹理网格集(Physical Texture)中进行采样,在仅流送方面虚拟纹理会比传统纹理的消耗更大一些,根据屏幕内容的变化速率不同,该消耗可能在1~3ms之间。
消耗总是相对的,虚拟纹理能够在某些时候带来远超流送消耗的效率收益,比如将超大范围的多层地表纹理烘焙为运行时虚拟纹理,将每帧都要执行的复杂层计算转变为对纹理的直接采样,能够节省大量性能消耗。
0.2 UE4 中的 Virtual Texture

虚幻引擎4(UE4)支持两种虚拟纹理方法:运行时虚拟纹理(RVT) 和 流送虚拟纹理(SVT)。
流送虚拟纹理(SVT)运行时虚拟纹理(RVT)
- 支持超高纹理分辨率。
- 按需将纹素数据缓存于内存中。- 在硬盘中烘焙和加载纹素数据。- 非常适用于生成时间较长的纹理数据,如光照贴图或美术师创建的大型细节纹理。
- 支持超高纹理分辨率。
- 按需将纹素数据缓存于内存中。- 运行时由GPU生成的纹素数据。- 非常适用于可按需渲染的纹理数据,如过程纹理或合成分层材质。
流送虚拟纹理 可降低使用超大尺寸纹理时的纹理内存开销,包括支持虚拟纹理光照贴图和UDIM(U维度)。与现有的基于mip纹理流送相比,流送虚拟纹理是一种从硬盘流送纹理的替代方法。
运行时虚拟纹理 可有效渲染过程生成或分层的复杂材质,使运行时虚拟纹理适用于渲染复杂的地形材质。其能改善地形样条、网格体和材质贴花,及一般地形与对象混合的渲染性能和工作流程。
0.3 开启 Virtual Texture


  • 启用虚拟纹理
项目设置(Project Settings)中的引擎(Engine)>渲染(Rendering)>虚拟纹理(Virtual Textures)下,选中启用虚拟纹理支持(Enable virtual texture support)

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第2张图片


  • 启用虚拟纹理光照贴图
项目设置(Project Settings)中的引擎(Engine)>渲染(Rendering)下,设置启用虚拟纹理光照贴图(Enable virtual texture lightmaps),以启用对光照贴图的虚拟纹理支持。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第3张图片
启用以下控制台变量,控制项目中虚拟纹理光照贴图的使用方式:
控制台变量说明
r.IncludeNonVirtualTexturedLightmaps控制是否生成/保存非VT光照贴图。包含非VT光照贴图会限制图谱大小,从而失去VT光照贴图部分优势。
r.VT.EnableLossyCompressLightmaps启用虚拟纹理光照贴图的有损压缩。与常规颜色纹理相比,有损压缩的光照贴图纹理质量较低。
0.4 Virtual Texture 实现流程简述

关于探讨 Virtual Texture 如何实现的文章有很多,有兴趣的读者可以移步下方的连接作深入了解:

简述术语一览

  • Virtual Texture(VT):虚拟纹理。虚拟纹理将传统mip纹理划分为更小的页(Virtual Texture Page)储存,以实现对页的单独引用而避免流送过多的冗余纹理。
  • Feedback:存储当前屏幕像素对应的 VT Page 信息,用于决定哪些 VT 数据需要被处理和生成。
  • Physical Texture:虚拟纹理对应的物理纹理资源,是对 VT 采样过程中的实际引用对象。Physical Texture 也划分为许多页(Physical Texture Page),用来采样和储存虚拟纹理。
  • PageTable:虚拟纹理页表,用来寻址 VT Physical Texture Page 数据。使用中的 Virtual Texture Page 与 PageTable Texel 是一一对应的关系。

VT的实现过程可以大致分为两个阶段:准备阶段采样阶段
准备阶段会处理与VT相关的判断逻辑以及生成VT相关数据。其中大致包括:

  • 生成Feedback,并执行逻辑判断需要处理哪些 Virtual Texture Page。
  • 将 Virtual Texture Page 加载到 Physical Texture Page。
  • 生成和更新 PageTable 数据。
采样阶段利用已生成的VT数据生成场景对象的 Physical Texture UV 并进行采样:

  • 根据屏幕空间坐标以及相关信息(PageTable数据)生成 Physical Texture UV。
  • 对 Physical Texture 执行采样。
虚幻引擎中,PageTable最多存在16个,单个PageTable尺寸上限为4096x4096。
0.5 调试相关

使用stat virtualtexturing查看虚拟纹理场景的开销详情(以毫秒计)和页面表计数器。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第4张图片
使用stat virtualtexturememory显示与当前场景中虚拟纹理的使用有关的内存计数器。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第5张图片
1. Streaming Virtual Texture ( SVT )

1.1 什么是 SVT

流送虚拟纹理 (SVT)是一种在项目中从硬盘流送纹理的替代方法,与虚幻引擎4(UE4)中现有基于mip的纹理流送相比,其既有优点也有缺点。
传统基于mip的纹理流送对材质UV使用执行离线分析,然后在运行时根据对象可见性和距离决定要加载的纹理mip级别。由于流送数据考虑的是全纹理mip级别,所以此过程有局限性。使用高分辨率纹理时,加载更高等级的mip纹理可能会极大地影响性能和内存开销。此外,通过CPU使用基于CPU对象可见性和剔除做出基于mip纹理流送的决定,可见性更为保守。意味更有可能进行加载来避免对象突然出现在视图中。因此即使对象的一小部分可见,则视整个对象均为可见。加载对象包括可能需要流送传入的相关纹理。
虚拟纹理系统仅会根据UE的要求,流送需要显示的纹理部分。将所有mip级别拆分为固定尺寸的小图块即可实现这一点。GPU决定屏幕上所有可见像素所访问的可见图块。这意味着,当UE要求显示某个对象时,它会与GPU通信,GPU会将所需图块加载到GPU内存缓存。无论纹理大小,SVT的固定图块大小仅考虑可见图块。GPU会使用标准深度缓冲计算可视性,促使SVT仅对可见部分(影响像素的部分)发出请求。
在虚幻引擎中使用 SVT 十分简单,直接将传统纹理转换为 SVT 即可,是开箱即用的功能。
1.2 何时(不)使用 SVT

由于要进行额外的逻辑判断和采样计算,抉择何时使用虚拟纹理时需要慎重,以下列举了应当避免使用虚拟纹理的情况:

  • 纹理分辨率很低(<1K)时。虚拟纹理的目的是以时间换空间,足够轻量的纹理不值得这么做。
  • 没有额外的性能预算时。至少为虚拟纹理准备1~3ms性能预算。
  • 无法支付足够的常驻内存时。虚拟纹理将常驻一部分显存以存放 Physical Texture 和 PageTable Texture。
  • 只有极少的纹理需要使用虚拟纹理时。

何时能够使用虚拟纹理:

  • 当有大量高分辨率纹理(>2K)时。
  • 场景中有大量遮挡元素且满足第一条时。
  • 每个纹理只接受一次虚拟纹理采样。使用不同的UV,则虚拟纹理堆栈获取将增加开销。
  • 能够不计代价地提升纹理质量时。
1.3 SVT 细节

转换和导入SVT

可以通过在传统mip纹理上点击右键,将其转换为 SVT:

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第6张图片
在转换过程中,纹理资源将在纹理编辑器设置中启用虚拟纹理流送(Virtual Texture Streaming)。引用选中纹理的材质转换纹理采样节点以使用虚拟采样器类型,而不是非虚拟采样器类型。
也可在纹理编辑器中手动将其转换为 SVT:

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第7张图片
若未使用上述右键转换菜单选项而启用此项,将立即导致引用转换纹理的所有现有材质失效。应打开引用违规纹理的所有材质,并将纹理取样节点设为使用正确 虚拟(Virtual) 采样器类型。例如,虚拟纹理应使用 虚拟颜色(Virtual Color) 而非 颜色(Color) 采样器类别。
纹理取样节点未使用正确采样器类型时,UE会在 统计数据(Stats) 面板和此节点底部显示一条错误消息:

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第8张图片

  • 错误消息显示指定的VT Texture Sample表达式的错误 采样器类型
  • 将纹理取样样本的 采样器类型(Sampler Type) 更改为 虚拟(Virtual) 类型之一。
  • VT纹理取样正确渲染,由表达式右下角"VT"指示。

可以设置在导入纹理时自动将一定分辨率以上的纹理转换为 SVT:

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第9张图片
ProjectSettings > Editor > Texture Import > Virtual Textures > Auto Virtual Textureing Size 中设置。

可以手动设置 SVT 图块的大小:

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第10张图片
ProjectSettings > Engine > Rendering > Virtual Textures > Tile size / Tile border size 中设置。
也可以通过命令行直接修改该参数:
r.VT.TileSize
r.VT.TileBorderSize UDIM支持

"U尺寸"("UDIM")是一类纹理命名规范,利用其能将多个纹理图像映射到静态网格体或骨架网格体模型上的单独UV区域。使用UDIM命名规范时,UE会将图像文件组导入为单个虚拟纹理资源。
支持UDIM的虚拟纹理有以下好处:

  • 适用于多数单独的较小纹理,而非极大纹理。
  • 各UDIM图像可启用不同分辨率的非统一像素密度虚拟纹理。
例如,若导入由4个图像文件构成的UDIM虚拟纹理(两个2048x2048纹理和两个128x128纹理),并以2x2模式排列,则逻辑上虚拟纹理将采样此类图像,如同单个4098x4098纹理。UE会拉伸128x128小图像以填充2048x2048大图像所填充的相同区域,而不影响硬盘或运行时内存的使用。在本例中,将128x128小纹理填充到2048x2048纹理分辨率不会消耗内存。
利用此命名约定,开始在项目中使用UDIM纹理与虚拟纹理:
BaseName.####.[支持文件格式]例如:
MyTexture.1001.png
UE4中的虚拟纹理 —— Virtual Texturing in UE4 第11张图片
UDIM纹理U向计数限制为10个(尾数0~9),V向没有限制。
欲了解UDIM流程的更多信息,参见Foundry的UDIM工作流程教程。

性能和开销

使用控制台命令r.VT.Borders 1,可在使用流送虚拟纹理的材质上绘制mip可视化网格。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第12张图片

在材质的虚拟纹理中采样比传统纹理采样开销更大。你可以将虚拟纹理的开销分为两类:

  • 查找 针对材质图表中采样的所有虚拟纹理。
  • 当你的项目使用相同UV和采样器源时,堆栈 可合并虚拟纹理。
虚拟纹理固定比传统纹理采样开销更大。固定至少有两个纹理获取和部分数学指令。但通过合并使用相同UV和采样器源的VT纹理采样的堆栈(最多8个),可分摊部分开销。
在该简单材质范例中,有两个VT纹理采样表达式使用正在采样的默认UV。添加 虚拟纹理查找 以查找各纹理采样,由于两者均使用单个UV,因此被合并为单个 虚拟纹理堆栈

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第13张图片
如果你的项目使用不同的UV,则使用两个虚拟纹理堆栈获取将增加开销:

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第14张图片
第一个实例共使用三个纹理获取:两个查找和一个堆栈。由于VT采样使用相同的UV,UE合并其堆栈可省略一个纹理获取(texture fetch)。第二个示例共有四个纹理获取:两个查找和两个堆栈。VT纹理采样使用的底色UV和法线纹理采样的不同,意味无法将这两者合并为一个堆栈。
2. Runtime Virtual Texture ( RVT )

2.1 什么是 RVT

运行时虚拟纹理(RVT)在运行时使用GPU按需创建其纹理数据,工作方式与传统纹理映射类似。较大区域上的RVT缓存着色数据非常适用于使用贴花类材质的地形和适配地形的样条。
能够渲染超高分辨率是RVT的优势,除此之外RVT的功能与蓝图 RenderTarget 类似。
RVT能够将某个方向一定范围内复杂的多层材质的采样结果合并映射为一个(套)纹理,并利用该纹理进行采样输出。对于地形这种始终不发生改变但又复杂的多层材质,能够节省大量采样和层与层之间进行计算的开销。
2.2 RVT 应用

优化多层材质性能

RVT被设计的初衷之一就是用来优化复杂的多层地表材质,直接将混合结果渲染为纹理用于最终采样输出,能够大大降低该材质的采样和计算开销。缺点是由于渲染和采样结果都是二维的,对于在渲染时平行于渲染方向的纹理、以及处于可见表面之下的其它表面无能为力,例如从上向下渲染纹理时,垂直的悬崖和凹陷进崖壁中的洞穴无法得到正确的纹理。

更加强大的贴花

利用RVT整合贴花也是一种应用思路,缺点同样是无法作用于三维表面。利用RVT制作贴花能够突破传统Decal只能在体积中贴花的限制,RVT原理类似RenderTarget,可以让我们能利用样条线变形制作道路等有特殊形状的模型控制贴花。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第15张图片

左:用于渲染RVT纹理的网格体    右:应用了RVT纹理的材质

官方文档详细示例:

虚拟阴影

利用RVT渲染具有方向性的特点,可以方便地制作高精度的虚拟投射阴影。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第16张图片
通过将圆柱体以一定角度渲染到RVT,再从地形材质中调用该RVT实现这一效果。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第17张图片

圆柱体材质

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第18张图片

地形材质

采集场景数据

通过RVT采集场景高度数据,能够实现一些精细的距离场效果,且可以突破距离场395.75cm的范围限制。
通过RVT采集表面法线数据,能够让草网格跟随地形偏移,以及实现材质的法线混合等效果。
2.3 RVT 细节

组件关系梳理


UE4中的虚拟纹理 —— Virtual Texturing in UE4 第19张图片

点击查看大图

RVT一共由四个部分组成:

  • RVT Output:RVT Output 节点存在于材质中,控制RVT“要输出什么”。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第20张图片

RVT Output


  • RVT Actor:启用了Draw in Virtual Textures 的场景对象,控制“向哪些RVT输出”,以及针对该场景对象的mip设置。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第21张图片

RVT Actor DetAIls


  • RVT资产:储存RVT的数据文件,决定“如何生成RVT数据”的特征(大小、类型及其他特性)。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第22张图片

RVT Details


  • RVT Volume:RVT体积决定RVT渲染和映射的范围和方向,以及是否启用SVT优化。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第23张图片

RVT Volume

为 RVT 启用 SVT
当RVT涵盖具有众多Actor的大型场景时,渲染到RVT低分辨率mip将十分缓慢。此外,在此情况下,场景Actor需要永久常驻,以便渲染至表现场景远处部分的低mip,这一过程极耗内存。因此,烘焙和流送RVT的低分辨率mip则更为高效。仍会在运行时渲染较高分辨率mip。由此,单个虚拟纹理可充分利用流送虚拟纹理和运行时虚拟纹理这两种方法。
简而言之,RVT中的SVT类似于定向光中的级联阴影。启用RVT中的SVT需要创建 Virtual Texture Builder 文件,在未指定 Virtual Texture Builder文件时点击Build按钮可以创建 Virtual Texture Builder文件。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第24张图片
在RVT资源中,使用流送到虚拟纹理的低mip数(Number of low mips to stream to the virtual texture)属性设置要流送的低mip数。低mip值越高,生成的流送虚拟纹理越大,将在摄像机较近距离处使用此纹理。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第25张图片
接下来需要编译流送虚拟纹理。共有两种操作方法:

  • 使用编辑器工具栏中的编译(Build)菜单,并选择编译虚拟纹理(Build Virtual Textures)。此选项将在当前开启的关卡中编译所有虚拟纹理。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第26张图片

  • 使用RVT体积组件并点击在编辑器中使用流送低Mip(Use Streaming Low Mips in Editor)属性旁的编译(Build)。此选项仅编译选定RVT体积的虚拟纹理。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第27张图片
编辑器默认固定使用运行时生成的RVT数据进行渲染,而非使用流送虚拟纹理数据。此操作无需在相关编译后更新流送虚拟纹理。
要在RVT低mip中渲染流送虚拟纹理,在RVT体积 细节(Details) 面板中选择 在编辑器中使用流送低mip(Use Streaming Low Mips in Editor) 属性。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第28张图片

调试相关

流式虚拟纹理低 Mips 调试
为了帮助可视化正在使用流式虚拟纹理的距离,请使用RVT VolumeDetails面板中的Build Debug复选框。这可以在在编辑器中使用流低 Mips属性旁边找到。设置此选项后,流式虚拟纹理会使用色调重建数据,以便轻松查看正在使用流式虚拟纹理 mips 的位置。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第29张图片

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第30张图片

其他调试内容详可参考官方文档“优化统计和调试”章节:
2.4 RVT 参考

RVT资产

RVT图块数量(Size of the virtual texture in tiles)
此 RVT 的大小(以图块为单位)。RVT 的最终分辨率是该值乘以平铺大小。当前最多支持 4096 个磁贴。该值应用于关联 RVT 体积的宽度或高度中的最大值。将选择较小轴的尺寸以匹配 RVT 体积的纵横比。
每个图块的尺寸(Size of each virtual texture tile)
此 RVT 使用的图块大小。虚拟纹理数据被渲染并存储在瓦片中。使用较小的瓦片尺寸意味着生成每个瓦片的成本更低,但需要生成更多的瓦片。
每个图块的边框填充(Border padding for each virtual texture tile)
用于每个图块的填充纹素数。较高的数字对内存和性能的影响较小。值为 0 将导致来自平铺边缘的双线性采样伪影的着色接缝。值 2 应解决此问题,需要更高的值才能启用各向异性采样。
启用自适应页表(Enable adaptive page table)
设置后,RVT 使用稀疏自适应页表。这支持大于页表大小的平铺计数。这反过来意味着更大的虚拟纹理分辨率是可能的。对自适应页表进行采样时性能开销很小,因此仅在需要大块计数时才应设置。只有开启此选项,材质节点中的启用自适应页表(Enable adaptive page table)设置才会正确应用。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第31张图片

左:未开启自适应页表    右:开启自适应页表。点击查看大图。

RVT体积

隐藏原型(Hide Primitives)
启用此选项可隐藏主通道中的相关基元。这将适用于所有设置为渲染到此卷上的 RVT 且其“在主通道中绘制”虚拟纹理设置设置为“从虚拟纹理”的图元。

UE4中的虚拟纹理 —— Virtual Texturing in UE4 第32张图片

左:未开启圆柱体RVT体积隐藏原型    右:开启圆柱体RVT体积隐藏原型

其它RVT参考可查看下方官方文档:


上一篇:美国回国攻略
下一篇:华人纽约地铁遭白人打骂,无人相劝,后续情况如何?
@



1.西兔生活网 CTLIVES 内容全部来自网络;
2.版权归原网站或原作者所有;
3.内容与本站立场无关;
4.若涉及侵权或有疑义,请点击“举报”按钮,其他联系方式或无法及时处理。
 

精彩评论7

正序浏览
跳转到指定楼层
沙发
威ヤ 发表于 2022-5-20 17:31:30 | 只看该作者
 
看了半天,没看懂,难受死我了
回复 支持 反对

使用道具 举报

 
板凳
莫遇 发表于 2022-5-20 17:32:20 | 只看该作者
 
如果你说的是虚拟纹理原理,这个视频或许可以帮到你:
Getting started with streaming virtual texturing for games | Unite Now 2020
回复 支持 反对

使用道具 举报

 
地板
河源♀在外地 发表于 2022-5-20 17:32:52 | 只看该作者
 
OK谢谢[赞同]
回复 支持 反对

使用道具 举报

 
5#
爷们儿来也了 发表于 2022-5-20 17:33:52 | 只看该作者
 
只要开了虚拟纹理,CPU占用100%,然后项目打不开。。。这是为啥?要升级硬件?求教。。。
回复 支持 反对

使用道具 举报

 
6#
八卦的亡灵法师 发表于 2022-5-20 17:34:01 | 只看该作者
 
我目前没有遇到过这种情况,如果你是在打开一个复杂项目时遇到这种情况,我建议你新建一个项目开启虚拟纹理试一试是否依旧如此。如果还是一样的话至少说明不是项目在自动转换纹理的问题,很可能是环境配置或者硬件问题。
回复 支持 反对

使用道具 举报

 
7#
_sadico 发表于 2022-5-20 17:34:31 | 只看该作者
 
请问您一下,我有个地形是用的虚拟纹理,那我要怎么关掉虚拟纹理,不在play时才显示材质
回复 支持 反对

使用道具 举报

 
8#
pan… 发表于 2022-5-20 17:35:31 | 只看该作者
 
虚拟纹理的更新应该是实时的,不会只在play时才显示才对。如果是rvt没有更新,可以试着稍微挪动捕捉体积,它会自动更新。
要关闭虚拟纹理可以在项目设置里搜索virtualtexture,可以找到开启/关闭的选项。
回复 支持 反对

使用道具 举报

 
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

排行榜
活跃网友
返回顶部快速回复上一主题下一主题返回列表APP下载手机访问
Copyright © 2016-2028 CTLIVES.COM All Rights Reserved.  西兔生活网  小黑屋| GMT+8, 2024-5-13 17:29