OpenGL / D3D:如何在Windows中获取全屏运行的游戏的屏幕截图?

10

假设我有一个全屏运行的OpenGL游戏(如《求生之路2》)。我想通过编程方式获得它的屏幕截图,然后将其写入视频文件。

我尝试过GDI、D3D和OpenGL方法(例如glReadPixels),但要么收到空白屏幕,要么在捕获流中出现闪烁。

有什么建议吗?

值得一提的是,类似于我正在尝试实现的东西的范例是Fraps。


2
您是否可以访问源代码?(即L4D2只是一个例子吗?) - Calvin1602
3个回答

13

这个问题有几种解决方法。其中大部分都不太好,完全取决于你想要针对哪种图形API以及目标应用程序使用哪些函数。 大多数DirectX、GDI+和OpenGL应用程序都是双缓冲或三重缓冲的,因此它们都会调用:

void SwapBuffers(HDC hdc)

在某个时刻,它们还会在消息队列中生成WM_PAINT消息,每当窗口需要绘制时。这给了你两个选择。

  • 你可以安装全局钩子或线程本地钩子到目标进程中并捕获WM_PAINT消息。这允许你在绘画发生之前从设备上下文中复制内容。可以通过枚举系统上的所有进程并查找已知的窗口名称或已知的模块句柄来找到该进程。

  • 你可以将代码注入到目标进程的SwapBuffers的本地副本中。在Linux上,可以通过LD_PRELOAD环境变量或显式调用ld-linux.so.2轻松完成此操作,但在Windows上没有相应的方法。幸运的是,微软研究部门有一个名为Detours的框架可以为您完成此操作。你可以在这里找到它:link

Farbrausch是一个demoscene小组,他们开发了一个名为kkapture的演示捕获工具,该工具利用了Detours库。他们的工具针对不需要用户输入的应用程序,因此他们通过挂钩所有可能的时间函数(如timeGetTime()、GetTickCount()和QueryPerformanceCounter())以固定帧速率运行演示。这真是太酷了。关于kkapture内部的演示,ryg(我想是他)写了一篇演示,可以在这里找到。我认为这对你很有兴趣。

有关Windows hooks的更多信息,请参见这里这里

编辑:

这个想法让我很感兴趣,所以我使用Detours来挂钩OpenGL应用程序并操纵图形。这是添加了绿色雾的Quake 2:Quake 2 Hook

关于Detours的更多信息,因为我现在已经亲身使用过:

Detours在两个层面上工作。实际的hooking只在与目标进程相同的进程空间中起作用。因此,Detours有一个函数可以将DLL注入到进程中并强制运行其DLLMain,以及应在该DLL中使用的函数。当运行DLLMain时,DLL应调用DetourAttach()来指定要挂钩的函数,以及“detour”函数,即您想要覆盖的代码。

所以它基本上是这样工作的:

  • 您有一个启动器应用程序,其唯一任务是调用DetourCreateProcessWithDll()。它的工作方式与CreateProcessW相同,只是有一些额外的参数。这将向进程中注入一个DLL并调用其DllMain()。
  • 您实现了一个DLL,该DLL调用Detour函数并设置跳板函数。这意味着调用DetourTransactionBegin()、DetourUpdateThread()、DetourAttach(),然后是DetourTransactionEnd()。
  • 使用启动器将您实现的DLL注入到进程中。

然而,有一些注意事项。当运行DllMain时,稍后使用LoadLibrary()导入的库尚不可见。因此,在DLL附加事件期间不能必然设置所有内容。解决方法是跟踪到目前为止被覆盖的所有函数,并尝试在您已经可以调用的这些函数内初始化其他函数。这样,您将会在LoadLibrary将它们映射到进程的内存空间中时立即发现新的函数。但我不确定这对于wglGetProcAddress是否有效。(也许这里还有其他人有关于此的想法?)

一些LoadLibrary()调用似乎会失败。我测试了Quake 2,DirectSound和waveOut API由于某种原因无法初始化。我仍在调查此问题。


提到farbrausch就加1分,再给你一个虚拟奖励,因为你的回答很好。 - sum1stolemyname

0

0
我过去曾经编写过屏幕截图程序(DirectX7-9时代)。 我发现老旧的DirectDraw非常有效,并且能够可靠地抓取硬件加速/视频屏幕内容的位,而其他方法(D3D、GDI、OpenGL)似乎会留下空白或混乱。 它也非常快速。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接