项目介绍
Reshaper是一个能够注入到游戏进程,并且在游戏运行时在游戏画面上显示一个界面。并主要提供了画面的增强、附加、调整参考等功能(通过Hook 各种3D函数并且加载脚本修改画面)。
ReShade API 允许您与加载 ReShade 的应用程序的资源和渲染命令进行交互。它抽象出 ReShade 支持的各种图形 API (Direct3D 9/10/11/12、OpenGL 和 Vulkan)之间的差异,以便编写适用于各种应用程序的附加组件,无论它们使用何种图形 API。
Reshpaer还带了一个Reshade FX工具用来定义和支持一种脚本格式,并能将这种格式转换为各3D引擎使用的HLSL(DirectX), GLSL(OpenGL) or SPIR-V(行业标准中间语言)等语言。
摘要
主要代码文件介绍
File | Description |
---|---|
dll_log.cpp | 日志功能 |
dll_main.cpp | 代码主入口 |
dll_resources.cpp | 加载dll内嵌资源 (e.g. built-in shaders) |
effect_lexer.cpp | Lexical analyzer for C-like languages 类C语言的词法解析器 |
effect_parser.cpp | ReShade FX语言解析 |
effect_preprocessor.cpp | C-like preprocessor implementation 类C语言预处理器 |
hook.cpp | MinHook封装代码 |
hook_manager.cpp | 提供DLL Hook的接口,dllmain里调用 Automatic hook installation based on DLL exports |
input.cpp | Hook窗口消息队列函数以及鼠标键盘状态拦截 |
runtime.cpp | 核心ReShade运行时,包括特效管理 |
runtime_gui.cpp | 覆盖渲染和所有与用户界面相关的内容 |
overlay
引用 imgui.h 和 reshade.hpp 后,在dll入口函数中使用reshade::register_addon、reshade::register_overlay 注册即可。然后在功能函数中使用imgui来绘制想要展示的内容。
D3D版本问题
3.0之后不再支持DirectX8,可以使用第三方的d3d8.dll将游戏改为D3D9的引擎,然后再使用Reshaper。
Reshade运行模式
常见模式是在安装时将Reshade(64).dll重命名为dxgi、d3d9.dll、opengl32.dll等,放到目标进程同目录,并且生成Reshade.ini。
目标进程在运行时加载改名后的Reshade(64).dll,后续文档简称为Reshade.dll。加载dll中一些功能步骤见下面具体分析。这里简要说明:加载后hook一系列dll(系统和3d引擎),然后在设备初使化时加入自己的功能,在绘制函数使用imgui修改画面,并提供对addon的加载(各addon里也可以添加自己的功能)。
接口
- reshade_api_format.hpp 定义了常用颜色格式,以及格式转换相关
- reshade_api_resource.hpp 会包含reshade_api_format,定义resource_view等,类似于IDirect3DResource9等接口的功能
- reshade_api_pipeline.hpp 会包含reshade_api_resource,定义sharder\pipeline等相关宏定义
- reshade_api_device.hpp 包含reshade_api_pipeline,接口device、command、swapchain等,抽象了各3d SDK共用的一些概念
- reshade_api.hpp 会包含reshade_api_resource,定义effect_runtime,用来控制显示内容
- reshade_events.hpp 包含reshade_api,定义插件响应的事件(addon_event)和回调函数格式。
- reshade_overlay.hpp 提供一个导出的imgui的api封装,供addon里使用。addon在register_addon后初使化这个api封装,实际imgui的函数指针指向Reshade的dll内部。
effect_runtime
effect_runtime是Reshade的重要接口,提供了一些重要接口,也会出现在各种回调和功能函数中,有点类似全局app对象。
重要接口列表:
render_effects(command_list *cmd_list, resource_view rtv, resource_view rtv_srgb = { 0 })
- capture_screenshot get_screenshot_width_and_height
-
is_key|mouse_*
判断当前按键和鼠标状态
(截屏、获取宽高等) -
*_uniform_variable_*
effect变量表读写和枚举相关函数 -
*_texture_variable_*
texture变量表读写和枚举相关函数 -
find|get*_technique_*
technique读写和枚举相关函数 -
find|get*_preprocessor_*
preprocessor相关函数
配置文件
代码ini_file.cpp文件中在reshade::global_config函数里,使用全局变量g_target_executable_path(dll加载时初使化)和ReShade.ini拼接起来做为ini配置文件路径。所以配置文件保存在每一个被hook的进程同目录中。
在addon.cpp中可以使用config_get_value等(内部实际调用Reshade.dll的导出函数ReShadeSetConfigValue和ReShadeSetConfigValue)用来负责设置配置,参数除了ini的section、key、value外就只有一个reshade::api::effect_runtime *runtime
。
dll_main流程
- 加载配置文件
- 通过自己的文件名来判断当前是什么3d引擎,分别判断前缀:d3d\dxgi\opengl32\dinput等。
- 如果自己的文件名无指定引擎并且也不是uwp软件,则加载配置文件(不存在时失败)
- 判断基本目录
- 如果配置文件中INSTALL下有BasePath的定义,则使用它(可以带环境变量)。
- 如果环境变量中定义有RESHADE_BASE_PATH_OVERRIDE,则使用它。
- 如果文件名无指定引擎则默认使用可执行程序所在目录,否则使用本dll所在目录。
- 配置里没有INSTALL-EnableLogging或者为true时开启日志功能。
- 枚举该进程加载的所有模块,看看是否有导出函数为ReShadeVersion的,如果有则退出流程。
- 配置里INSTALL-DumpExceptions为true时,添加崩溃报告生成功能。
- Hook
user.dll
- 根据上面判断的3D引擎,hook不同的dll
- 非opengl情况下,注册所有版本的系统目录下的
d3d[N].dll
和dxgi.dll
的Hook。 - 非opengl情况下,win7下hook非系统目录下的
d3d12.dll
,而非win7下,hook系统目录下的d3d12.dll
。 - 非d3d情况下,下Hook系统目录下的
opengl32.dll
。 - 注册所有名为
vrclient[_x64].dll
的hook。 - 注册系统目录下的
dinput.dll
,如果当前dll名为dinput8,则hook系统目录下的dinput8.dll
。
- 非opengl情况下,注册所有版本的系统目录下的
hook相关
Reshade.dll本身导出了不少与系统同名函数,每个同名函数都是为了Hook其它系统dll或3D引擎dll中的同名函数。
hook_manager.cpp中定义了3种hook方法:
enum class hook_method
{
export_hook,
function_hook, // 函数级Hook,通过MHook库插入汇编跳转代码
vtable_hook // 类虚表替换
};
- 公共函数
reshade::hooks::register_module
- 判断dll是否已经加载,如果未加载则加入到延迟hook列表中,如果已经加载则使用function_hook类型来调*用 install_internal将该dll中的部分函数重定向到Reshade.dll中。
- 如果该dll名称和Reshape改名后同名,则将路径置给全局变量
s_export_hook_path
,然后延迟Hook。
-
enumerate_module_exports
用于加载一个dll句柄对应的所有导出函数信息。 -
install_internal
枚举被hook的dll的所有导出函数,找到和Reshade.dll中名称相同的函数然后hook它到Reshade.dll中。 - export_hook类型指被Reshade.dll改名后占用的dll,比如原始的dxgi.dll文件。
- 在
reshade::hooks::ensure_export_module_loaded
中加载并hooks_export_hook_path
全局变量指向的dll。会在第一次触发自己导出的3D引擎同名函数后,在转调reshade::hooks::call
时触发。 -
reshade::hooks::call
获取Hooked函数对应的原跳板函数,用于真实调用(内部会确保加载原板块)
ui绘制相关
void reshade::runtime::draw_gui()
中负责主要的ui绘制工作。
在各插件中,可以注册reshade::register_overlay("Test", &draw_debug_overlay);
来显示一个新窗口的绘制函数,使用reshade::register_overlay(nullptr, &draw_settings_overlay);
注册一个绘制生成设置项的函数。在函数内直接使用ImGui相关函数绘制即可。
常用事件
- init_device device对象初使化时回调,
reshade::register_event<reshade::addon_event::init_device>(&on_init_device)
,会将device对象传递给回调函数。 - destroy_device device对象释放前回调。
effect
在settings里设置shader目录,点击reload可以加载effect列表。在reshade::runtime::load_effects()
中枚举所有effect,主要是找到所有的fx文件。然后开启多个线程调用reshade::runtime::load_effect
函数挨个加载。
在reshade::runtime::load_effect
加载每一个fx文件,读取源码并和编译,将全部effect放到runtime的_effects成员里。
显示逻辑:在界面点击选中effect后,在下一次触发绘制时触发runtime::on_present();
,会调用到reshade::runtime::update_effects()
,然后会触发reshade::runtime::create_effect
来创建effect。
主要内容:
- 创建textures
- 编译常量
- 创建 query pool
- 创建 pipeline layout
- 创建 global constant buffer
- Initialize techniques and passes
然后runtime::on_present();
继续调用runtime::render_effects
绘制特效。
PS: 有缘再继续更新
网友评论