在起源引擎中主动调用 ReShade 进行后处理

Modding May 19, 2023

需要修改的地方有 ShaderAPI_DX9 \ MaterialSystem \ Client

效果

这边使用的方法是, 将游戏的 client.dll 作为 ReShade 5.x 的 Addon 加载。

首先需要配置项目, 由于 ReShade 的头文件使用了 namespace reshade::api 这种定义方式, 所以需要将项目的 C++ 版本调整为 C++17 或者更新。请注意: VS2017 之后才支持 C++17。

由于 ReShade 的需要原生 D3D 的 ID3DTexture 指针, 所以修改 IShaderAPI 接口, 增加方法 GetRawTextureHandle 用于获取原生指针:

/** ishaderapi.h **/

#define SHADERAPI_INTERFACE_VERSION "ShaderApi031"
abstract_class IShaderAPI : public IShaderDynamicAPI
{
    // ...
	virtual void* GetRawTextureHandle( ShaderAPITextureHandle_t tex ) = 0;
};

/** shaderapidx8.h **/
class CShaderAPIDx8 : public CShaderDeviceDx8, public IShaderAPIDX8, public IDebugTextureInfo
{
    // ...
    IDirect3DBaseTexture* GetD3DTexture( ShaderAPITextureHandle_t hTexture );
	virtual void* GetRawTextureHandle(ShaderAPITextureHandle_t tex);
    // ...
};

/** shaderapidx8.cpp **/
void* CShaderAPIDx8::GetRawTextureHandle(ShaderAPITextureHandle_t tex)
{
	return this->GetD3DTexture(tex);
}

随后对 ITexture 接口也做这样的操作, 同样的增加 GetRawTextureHandle 方法

/** itexture.h **/
abstract_class ITexture
{
	// ...
	virtual void* GetRawTextureHandle() = 0;
};

/** ctexture.cpp **/
class CReferenceToHandleTexture : public ITextureInternal
{
	virtual void* GetRawTextureHandle() 
    { 
    	return g_pShaderAPI->GetRawTextureHandle( m_pTextureHandles[0] ); 
    }
}

class CTexture : public ITextureInternal
{
	virtual void* GetRawTextureHandle() 
    { 
    	return g_pShaderAPI->GetRawTextureHandle( m_pTextureHandles[0] ); 
    }
}

/** mat_stub.cpp **/
class CDummyTexture : public ITexture
{
	virtual void* GetRawTextureHandle() { return nullptr; }
}

随后就是在 cdll_client_int 的初始化函数中调用 reshade 的 api 注册 addon, 并且在 effect 渲染之前绑定 rendertarget 以及其他渲染用参数即可
参考:

/** reshade.h **/
#include "materialsystem/itexture.h"

namespace reshade_addon {
	void init();
	void init_rts(IMaterialSystem *pMaterialSystem);

	extern CTextureReference opaque_texture;
	extern CTextureReference depth_buffer_texture;
	extern CTextureReference ui_texture;
};

/** reshade.cpp **/
#include "reshade.hpp"
#include "materialsystem/MaterialSystemUtil.h"
#include "materialsystem/itexture.h"
#include "cdll_int.h"

extern "C" __declspec(dllexport) const char* NAME = "Source Engine Client";
extern "C" __declspec(dllexport) const char* DESCRIPTION = "client.dll as module";

extern IVEngineClient* engine;

namespace reshade_addon {
	using namespace reshade;

	CTextureReference opaque_texture;
	CTextureReference depth_buffer_texture;
	CTextureReference ui_texture;

	static void on_present(api::effect_runtime* runtime)
	{
		runtime->set_effects_state(engine->IsInGame());
	}

	static void on_begin_effect(api::effect_runtime* runtime, api::command_list* cmd_list, api::resource_view rtv, api::resource_view rtv_srgb)
	{
		if (!engine->IsInGame()) return;

#define BIND_TEXTURE(texture, name) \
		if (##texture.IsValid()) \
		{ \
			api::resource_view rtv{ uint64_t(##texture->GetRawTextureHandle()) }; \
			runtime->update_texture_bindings(name, rtv); \
		}

		BIND_TEXTURE(opaque_texture, "OPAQUE");
		BIND_TEXTURE(depth_buffer_texture, "DEPTH");
		BIND_TEXTURE(ui_texture, "UI");
	}

	void init_rts(IMaterialSystem* pMaterialSystem)
	{
		int w = 0, h = 0;
		pMaterialSystem->GetBackBufferDimensions(w, h);

		opaque_texture.Init(
			pMaterialSystem->CreateRenderTargetTexture(
				w, h,
				RT_SIZE_FULL_FRAME_BUFFER, IMAGE_FORMAT_RGBA16161616F
			)
		);

		depth_buffer_texture.Init(
			pMaterialSystem->CreateRenderTargetTexture(
				w, h,
				RT_SIZE_FULL_FRAME_BUFFER, IMAGE_FORMAT_R32F, MATERIAL_RT_DEPTH_ONLY
			)
		);

		ui_texture.Init(
			pMaterialSystem->CreateRenderTargetTexture(
				w, h,
				RT_SIZE_FULL_FRAME_BUFFER, IMAGE_FORMAT_RGBA8888
			)
		);
	}

	void init()
	{
		register_addon(GetModuleHandleA("client.dll"));
		register_event<addon_event::reshade_begin_effects>(on_begin_effect);
		register_event<addon_event::reshade_present>(on_present);
	}
}

/** cdll_client_int.cpp **/
int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physicsFactory, CGlobalVarsBase *pGlobals )
{
	reshade_addon::init();

	return true;
}

在你 client 初始化 rendertargets 的地方调用一下 reshade_addon::init_rts 然后在 viewrender.cpp 中在对应的地方设置 rendertarget 即可

这只是一种思路,仅供参考。

标签