ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

UE Viewer 深度解析:虚幻引擎 1-4 资源查看与导出的完整实战指南

UE Viewer 深度解析:虚幻引擎 1-4 资源查看与导出的完整实战指南

UE Viewer 深度解析:虚幻引擎 1-4 资源查看与导出的完整实战指南

【免费下载链接】UEViewerViewer and exporter for Unreal Engine 1-4 assets (UE Viewer).项目地址: https://gitcode.com/gh_mirrors/ue/UEViewer

UE Viewer(社区习惯叫它 umodel)是一款专攻虚幻引擎 1-4 代资源的查看与导出工具,它能把 .upk、.uasset、.pak 里那些二进制资产"翻译"成你眼前的三维模型、贴图和动画。本文以实战为主线,带你从打开一个游戏目录开始,一步步拆解它识别版本、解包、解析、渲染到导出的完整链路,也聊聊想给它做二次开发该从哪下手。

一、从一个"打不开"的场景说起

假设你手上有一个用虚幻引擎做的游戏目录,里面堆满了.pak.uasset.uexp文件。用记事本打开,全是乱码;用通用解压工具,提示格式错误。想提取里面的角色模型做学习研究,第一步就卡住了。

UE Viewer 解决的就是这个痛点:它直接读入游戏目录,识别出包文件,把引擎内部的对象(模型、材质、动画、贴图、音频)解析成可查看的形式,并支持导出为行业通用格式。官方定位一句话说得很清楚:Viewer and exporter for Unreal Engine 1-4 assets——注意是 1 到 4 全系列,从 1998 年的初代引擎到 2020 年前后的 UE4,都在覆盖范围内。

上手只需两步:

  1. 打开程序,用文件对话框选中游戏根目录(UE Viewer 会自己扫描出全部包文件和虚拟文件系统)。
  2. 在对象列表里双击资源,右侧即出现模型预览或材质缩略图,右键即可导出。

命令行模式下也支持无界面批处理,例如指定包文件后配合-export参数一键导出全部资产,这对批量处理几百个包文件的场景非常有用。✅ 新手可以先用 GUI 熟悉流程,老手直接上命令行脚本化操作。

二、分水岭之战:它如何认出一款游戏是哪代引擎

引擎从 1 代进化到 4 代,包文件格式变了无数次。UE Viewer 的判断起点,是包文件头部的一个魔法数字:

#define PACKAGE_FILE_TAG 0x9E2A83C1 // 包文件魔数 #define PACKAGE_V2 100 // UE2 包版本分界 #define PACKAGE_V3 180 // UE3 包版本分界

读取文件头后,代码会依次校验魔数、文件版本号(FileVersion)与授权版本号(LicenseeVersion),再结合FPackageFileSummary里记录的名称表、导入表、导出表的数量与偏移,决定后续走哪一套解析逻辑。Unreal/UnrealPackage/UnPackage2.cppUnPackage3.cppUnPackage4.cpp三个文件,就是按引擎代数拆分的"方言翻译器"。

真正的难点在 UE4 后期:很多包文件是unversioned(无版本号),只有一串自定义版本容器。UnPackage.h里的FCustomVersionContainer专门负责这摊事——它把FGuid + 版本号的键值对逐个读出,再和内置的版本映射表比对,猜出实际的引擎版本。而FGenerationInfo的读取同样按版本分支:

friend FArchive& operator<<(FArchive &Ar, FGenerationInfo &I) { Ar << I.ExportCount << I.NameCount; #if UNREAL4 if (Ar.Game >= GAME_UE4_BASE && Ar.ArVer >= 196) return Ar; // UE4 早期就移除了网络对象计数 #endif #if UNREAL3 if (Ar.ArVer >= 322) Ar << I.NetObjectCount; // UE3 特定字段 #endif return Ar; }

这段代码解决的问题很实际:同一个结构体在不同引擎里字段个数不同,直接按固定布局读必然错位。用条件编译 + 版本判断逐字段读取,才能保证一套代码兼容四代引擎。⚠️ 这也是逆向类工具最容易翻车的地方——引擎升级时悄悄加一个字段,解析器就要跟着调整。

三、虚拟文件系统:pak 包背后的"档案管理员"

现代 UE4 游戏几乎不会把资产裸放在磁盘上,而是打成一个或多个.pak文件。pak 本质上是个"档案柜":里面每一条文件记录(FPakEntry)都记录了压缩前大小、压缩后大小、压缩方式、加密标志和块偏移。Unreal/FileSystem/UnArchivePak.h用枚举完整追踪了 pak 格式的 11 个演进版本:

enum { PakFile_Version_Initial = 1, // UE4 早期 PakFile_Version_CompressionEncryption = 3, // UE4.3+ 支持压缩加密 PakFile_Version_PathHashIndex = 10, // UE4.26+ 文件路径以 64 位哈希存储 PakFile_Version_Fnv64BugFix = 11, // 哈希算法修正 PakFile_Version_Latest = PakFile_Version_Last - 1 };

注意第 10 版:连文件名都加密成了哈希,不还原哈希表就根本不知道包里有什么。UE Viewer 的做法是先读 pak 索引,再对路径哈希做预计算字典匹配,把"哈希-文件名"对应关系还原出来,然后像操作普通目录树一样浏览。

包内文件普遍被压缩和加密,这一层由UnCoreCompression.cppUnCoreDecrypt.cpp处理,底层的算法库全部收在libs/下:zlib、lz4、lzo、mspack(LZX 解压)、oodle,以及用于 AES 解密的 rijndael 实现。有些游戏还会魔改格式,比如《真人快打 X》和《火箭联盟》在 64 位偏移上动了手脚,UnPackage.hFCompressedChunk里就专门为它们写了分支读取逻辑。💡 这类"每个游戏都有小脾气"的适配,正是 UE Viewer 多年积累的核心资产——它不只是引擎通用格式的解析器,还是数百款具体游戏的兼容层。

四、把资产"翻译"成通用结构

文件读取通了,接下来是重头戏:把二进制流还原成有意义的对象。这一步的通用手法是给每个对象定义operator<<(FArchive&, T&)序列化函数,让数据按声明顺序被读出——UnCoreSerialize.cpp负责调度,FArchive像一根"数据水管",对象往里一放就能按位读出字段。

以网格为例,Unreal/UnrealMesh/UnMesh1.cppUnMesh4.cpp分别对应四代引擎的网格布局。UE4 版本在UnMesh4.h里定义了骨架与蒙皮的数据结构:

struct FReferenceSkeleton { TArray<FMeshBoneInfo> RefBoneInfo; // 骨骼名 + 父骨骼索引 TArray<FTransform> RefBonePose; // 骨骼绑定姿态 TMap<FName, int32> NameToIndexMap; // 名字到索引的快速查找表 }; #define NUM_INFLUENCES_UE4 4 // UE4 默认每顶点 4 组骨骼权重 #define MAX_TOTAL_INFLUENCES_UE4 8 // 上限 8 组

材质侧同样按代拆分:Unreal/UnrealMaterial/里有UnMaterial2.hUnMaterial3.h,表达式节点网络则由UnMaterialExpression.h统一描述,材质球由哪些节点、哪些贴图输入构成,都会被还原成一张可读的节点图。纹理方面,UE4 移动端常用 BC7、ASTC、ETC2 等压缩格式,UnTexture4.cpp配合libs/detexlibs/astc完成解压,桌面端 DXT 系列则走libs/nvtt,最终都能还原成可导出的 RGBA 像素。✅ 到这里,"看得懂"已经实现,下一步是"画出来"。

五、渲染器:让不可见的数据变成画面

解析出的顶点、法线、UV、骨骼权重只是一堆数字,要让人直观确认"对不对",得先画出来。UE Viewer 的渲染层放在Core/GL/CoreGL.cpp负责 OpenGL 上下文与绘制循环,GLBind.cpp负责跨平台的 GL 函数绑定,GLText.cpp负责场景中的文字标注。

着色器在Unreal/Shaders/下以.ush片段存放,例如Normal.ush(法线计算)、BloomPass.ush(泛光后处理)、Generic.ush(通用材质)。有意思的是构建流程:make.pl脚本会在编译期把这些片段拼接、宏展开、生成最终的着色器源码——相当于给 GPU 代码也做了一次"预编译"。查看器方面,Viewers/目录下有MeshViewerSkelMeshViewerMaterialViewerObjectViewer等,分别对应骨骼网格、静态网格和材质预览,支持旋转、缩放、切换 LOD 和线框模式。

对用户来说,这一步的意义是零成本验证:导出前先在查看器里转一圈,确认网格没破面、贴图颜色正常、骨骼动画能播放,再决定要不要导出。很多误导出垃圾数据的尴尬,都能在这一步提前发现。

六、导出:从"看懂"到"带走"

查看只是过程,导出才是终点。Exporters/目录下每个文件对应一类格式:ExportPsk.cpp负责 ActorX 的 PSK/PSA(3ds Max 生态最常用),ExportGLTF.cpp负责 glTF/GLB(游戏引擎和 Blender 通吃),ExportTexture.cpp输出 TGA/DDS/PNG,还有ExportSound.cppExport3D.cpp等。

导出系统的设计很克制,核心是一个注册表机制(Exporters.h):

typedef void (*ExporterFunc_t)(const UObject*); void RegisterExporter(const char* ClassName, ExporterFunc_t Func); bool ExportObject(const UObject* Obj); // 按对象类型分发到对应导出器

想新增一种导出格式,只需注册一个"类型名 → 导出函数"的映射,不用改动框架本体。导出时还会通过GetExportPath()按游戏名/包名组织目录结构,避免几百个文件堆在一起分不清归属。配合前面提到的批处理模式,整包资产的导出可以一条命令跑完。

七、构建与二次开发:给这个引擎换零件

如果你不满足于"用",想改源码,得先了解它那套特立独行的构建系统。UE Viewer 不用 CMake,而是用Tools/genmake(一个 Perl 脚本)把common.project这类人类友好的项目描述文件,翻译成各平台的 Makefile。build.sh在这个基础上又做三件事:生成带版本号的Version.h、预处理着色器、支持单文件增量编译。

扩展方向大致有三条,对应三个目录:

想做的事改哪里典型例子
支持新游戏的格式变体Unreal/GameSpecific/UnMeshBatman.cppUnMeshBioshock.cpp
增加新的导出格式Exporters/新增一个ExportXxx.cpp并注册
增加独立小工具Tools/PackageExtractTypeInfoUmdExtract

GameSpecific/里的文件最值得研究:它们不是通用逻辑,而是针对某一款游戏(蝙蝠侠、生化奇兵、Rune 等)的字段差异打的补丁。读这些 diff,相当于在看一份"虚幻引擎格式演化史"的活教材。

八、它的边界与你的下一步

UE Viewer 的边界同样清晰:它定位是资源查看与导出,不是通用逆向框架——动画曲线、物理资产、蓝图逻辑都不在覆盖范围内;遇到引擎魔改很深的网游,也需要社区持续适配。但正因定位聚焦,它把"UE1-4 资源解析"这件事做到了社区里最完整的程度,几十个算法库、四代引擎的格式分支、数百款游戏的兼容补丁,全部沉淀在Unreal/libs/这两个目录里。

想上手研究的话,推荐三条路径:一是直接跑起来,用真实游戏目录体验从打开到导出的全流程;二是读UnPackage.hUnMesh4.h,理解"版本分支读取"的逆袭思维,这对任何做格式兼容的开发者都有启发;三是用git clone https://gitcode.com/gh_mirrors/ue/UEViewer拉下源码,从Tools/genmakeUnCoreSerialize.cpp开始,按"构建 → 打开包 → 解析对象 → 导出"的顺序逐层跟进。等你亲手导出一个角色的那一刻,虚幻引擎这扇大门就算真正对你敞开了。

【免费下载链接】UEViewerViewer and exporter for Unreal Engine 1-4 assets (UE Viewer).项目地址: https://gitcode.com/gh_mirrors/ue/UEViewer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表