OpenGL/D3D:如何获得Windows中全屏运行的游戏的屏幕抓图? [英] OpenGL/D3D: How do I get a screen grab of a game running full screen in Windows?

查看:550
本文介绍了OpenGL/D3D:如何获得Windows中全屏运行的游戏的屏幕抓图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个全屏运行的OpenGL游戏(左4死2).我想以编程方式获取它的屏幕抓取,然后将其写入视频文件.

Suppose I have an OpenGL game running full screen (Left 4 Dead 2). I'd like to programmatically get a screen grab of it and then write it to a video file.

我尝试了GDI,D3D和OpenGL方法(例如glReadPixels),并且在捕获流中接收到黑屏或闪烁.

I've tried GDI, D3D, and OpenGL methods (eg glReadPixels) and either receive a blank screen or flickering in the capture stream.

有什么想法吗?

对于价值而言,与我试图实现的目标类似的典范例子是Fraps.

For what it's worth, a canonical example of something similar to what I'm trying to achieve is Fraps.

推荐答案

有几种解决此问题的方法.它们中的大多数都很棘手,这完全取决于您要定位的图形API的类型以及目标应用程序使用的功能. 大多数DirectX,GDI +和OpenGL应用程序都是双缓冲或三重缓冲的,因此它们都调用:

There are a few approaches to this problem. Most of them are icky, and it totally depends on what kind of graphics API you want to target, and which functions the target application uses. Most DirectX, GDI+ and OpenGL applications are double or tripple-buffered, so they all call:

void SwapBuffers(HDC hdc)

在某个时候.每当绘制窗口时,它们还将在其消息队列中生成WM_PAINT消息.这给您两个选择.

at some point. They also generate WM_PAINT messages in their message queue whenever the window should be drawn. This gives you two options.

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

  • You can install a global hook or thread-local hook into the target process and capture WM_PAINT messages. This allows you to copy the contents from the device context just before the painting happens. The process can be found by enumerating all the processes on the system and look for a known window name, or a known module handle.

您可以将代码注入到目标进程的SwapBuffers本地副本中.在Linux上,通过LD_PRELOAD环境变量或显式调用ld-linux.so.2可以很容易地做到这一点,但是Windows上没有等效的方法.幸运的是,Microsoft Research提供了一个名为 Detours 的框架,可以为您完成此任务.您可以在此处找到:链接.

You can inject code into the target process's local copy of SwapBuffers. On Linux this would be easy to do via the LD_PRELOAD environmental variable, or by calling ld-linux.so.2 explicitly, but there is no equivalient on Windows. Luckily there is a framework from Microsoft Research which can do this for you called Detours. You can find this here: link.

演示团队Farbrausch开发了一个名为kkapture的演示捕获工具,该工具利用了Detours库.他们的工具针对的是不需要用户输入的应用程序,因此他们基本上通过挂钩所有可能的时间函数(例如timeGetTime(),GetTickCount()和QueryPerformanceCounter()),以固定的帧速率运行演示.完全是rad.可以在此处.我认为您很感兴趣.

The demoscene group Farbrausch made a demo-capturing tool named kkapture which makes use of the Detours library. Their tool targets applications that require no user input however, so they basically run the demos at a fixed framerate by hooking into all the possible time functions, like timeGetTime(), GetTickCount() and QueryPerformanceCounter(). It's totally rad. A presentation written by ryg (I think?) regarding kkapture's internals can be found here. I think that's of interest to you.

有关Windows挂钩的详细信息,请参见此处此处.

For more information about Windows hooks, see here and here.

这个想法引起了我的兴趣,因此我使用Detours连接到OpenGL应用程序并弄乱了图形.这是添加了绿色雾的雷神之锤2:

This idea intrigued me, so I used Detours to hook into OpenGL applications and mess with the graphics. Here is Quake 2 with green fog added:

绕行有两个层次.实际的挂钩仅在与目标进程相同的进程空间中工作.因此,Detours具有将DLL注入进程并强制其DLLMain也运行的功能,以及应该在该DLL中使用的功能.运行DLLMain时,DLL应该调用DetourAttach()来指定要挂接的函数,以及"detour"函数,这是您要覆盖的代码.

Detours works on two levels. The actual hooking only works in the same process space as the target process. So Detours has a function for injecting a DLL into a process and force its DLLMain to run too, as well as functions that are supposed to be used in that DLL. When DLLMain is run, the DLL should call DetourAttach() to specify the functions to hook, as well as the "detour" function, which is the code you want to override with.

所以它基本上是这样的:

So it basically works like this:

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

尽管有一些警告.运行DllMain时,尚看不到稍后通过LoadLibrary()导入的库.因此,您不一定在DLL附件事件期间设置所有内容.一种解决方法是跟踪迄今为止已被覆盖的所有功能,并尝试初始化这些您可以调用的功能中的其他功能.这样,您将在LoadLibrary将新功能映射到进程的内存空间后立即发现它们.我不太确定这对于wglGetProcAddress的效果如何. (也许这里的其他人对此有想法吗?)

There are some caveats though. When DllMain is run, libraries that are imported later with LoadLibrary() aren't visible yet. So you can't necessarily set up everything during the DLL attachment event. A workaround is to keep track of all the functions that are overridden so far, and try to initialize the others inside these functions that you can already call. This way you will discover new functions as soon as LoadLibrary have mapped them into the memory space of the process. I'm not quite sure how well this would work for wglGetProcAddress though. (Perhaps someone else here has ideas regarding this?)

某些LoadLibrary()调用似乎失败.我使用Quake 2进行了测试,由于某种原因,DirectSound和waveOut API未能初始化.我还在对此进行调查.

Some LoadLibrary() calls seem to fail. I tested with Quake 2, and DirectSound and the waveOut API failed to initalize for some reason. I'm still investigating this.

这篇关于OpenGL/D3D:如何获得Windows中全屏运行的游戏的屏幕抓图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆