如何在C++中使用OBS graphics-hook捕获游戏画面?
游戏画面捕获实现方案(针对硬件加速游戏)
核心原理
这类导致黑屏的游戏通常采用DirectX 11/12或OpenGL进行硬件加速渲染,画面直接输出到GPU纹理,而非GDI设备上下文(DC),普通的BitBlt、PrintWindow等GDI捕获方法无法读取GPU中的渲染数据。OBS游戏捕获的核心是通过Hook渲染API或共享GPU纹理的方式,直接从GPU获取帧数据。
实现步骤(以DirectX 11为例)
1. 注入目标游戏进程
要捕获游戏的渲染数据,需要将捕获代码注入到游戏进程中(DirectX资源存在进程隔离)。可通过CreateRemoteThread+LoadLibrary实现注入:
// 简化注入代码 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); LPVOID pRemoteBuf = VirtualAllocEx(hProcess, NULL, strlen(dllPath)+1, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProcess, pRemoteBuf, dllPath, strlen(dllPath)+1, NULL); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess);
2. Hook DirectX 11的SwapChain::Present方法
游戏帧渲染完成后会调用Present提交到屏幕,Hook该方法即可获取当前帧纹理:
- 先通过游戏窗口HWND枚举系统中的
IDXGISwapChain,找到对应窗口的实例; - 用Detours库(或手动Hook)替换
Present函数:
// 原始Present函数指针 typedef HRESULT(WINAPI* PFN_PRESENT)(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags); PFN_PRESENT g_pOriginalPresent = nullptr; // 自定义Hook后的Present函数 HRESULT WINAPI HookedPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) { // 获取后台缓冲区纹理 ID3D11Texture2D* pBackBuffer = nullptr; pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); // 此处可对pBackBuffer进行数据处理(如复制、保存) // 优先用GPU纹理操作,避免CPU-GPU拷贝损耗 pBackBuffer->Release(); // 调用原始Present方法 return g_pOriginalPresent(pSwapChain, SyncInterval, Flags); } // 初始化Hook void InitHook(IDXGISwapChain* pSwapChain) { g_pOriginalPresent = (PFN_PRESENT)GetProcAddress(GetModuleHandleA("dxgi.dll"), "Present"); // 用Detours安装Hook DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)g_pOriginalPresent, HookedPresent); DetourTransactionCommit(); }
3. 纹理数据读取与导出
若需将GPU纹理读到CPU内存,可创建CPU可访问的staging纹理,复制后读取:
// 创建CPU可读纹理 D3D11_TEXTURE2D_DESC desc; pBackBuffer->GetDesc(&desc); desc.Usage = D3D11_USAGE_STAGING; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.BindFlags = 0; desc.MiscFlags = 0; ID3D11Texture2D* pStagingTexture = nullptr; pDevice->CreateTexture2D(&desc, nullptr, &pStagingTexture); // 复制后台缓冲区到staging纹理 pContext->CopyResource(pStagingTexture, pBackBuffer); // 映射纹理到CPU内存 D3D11_MAPPED_SUBRESOURCE mappedResource; pContext->Map(pStagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource); // 读取mappedResource.pData中的RGBA像素数据,可保存为图片或传输 pContext->Unmap(pStagingTexture, 0); pStagingTexture->Release();
4. 跨进程传输帧数据(可选)
若不想在游戏进程内处理数据,可创建共享GPU纹理,将后台缓冲区复制到共享纹理后,在捕获进程中打开该纹理直接读取GPU数据,大幅降低性能损耗。
关键注意事项
- 需针对游戏使用的渲染API(DX9/11/12、OpenGL)适配不同的Hook方案;
- 注入和Hook可能触发游戏反作弊系统,需做兼容性适配;
- 优先使用成熟Hook库(如Detours)简化实现,避免手动编写汇编;
- 性能优化:尽量用GPU内纹理操作,减少CPU-GPU数据拷贝,尤其是高分辨率场景。
内容的提问来源于stack exchange,提问作者Gürkan KARADAĞ
相关产品推荐
相关产品推荐

