OpenGL/D3D: Как получить скриншот игры, работающей в полноэкранном режиме в Windows?

Предположим, у меня есть игра OpenGL, работающая в полноэкранном режиме (Left 4 Dead 2). Я хотел бы программно получить снимок экрана, а затем записать его в видеофайл.

Я пробовал методы GDI, D3D и OpenGL (например, glReadPixels) и либо получаю пустой экран, либо мерцание в потоке захвата.

Есть идеи?

Для чего стоит, каноническим примером чего-то похожего на то, чего я пытаюсь достичь, является Fraps.

3 ответа

Есть несколько подходов к этой проблеме. Большинство из них неприглядны, и это полностью зависит от того, на какой графический API вы хотите нацеливаться и какие функции использует целевое приложение. Большинство приложений DirectX, GDI+ и OpenGL имеют двойную или тройную буферизацию, поэтому все они вызывают:

void SwapBuffers(HDC hdc)

в какой-то момент. Они также генерируют сообщения WM_PAINT в своей очереди сообщений всякий раз, когда должно быть нарисовано окно. Это дает вам два варианта.

  • Вы можете установить глобальный хук или локальный хук поток в целевой процесс и захватывать сообщения WM_PAINT. Это позволяет вам копировать содержимое из контекста устройства непосредственно перед тем, как происходит рисование. Процесс может быть найден путем перечисления всех процессов в системе и поиска известного имени окна или известного дескриптора модуля.

  • Вы можете добавить код в локальную копию целевого процесса SwapBuffers. В Linux это было бы легко сделать через переменную окружения LD_PRELOAD или явно вызвав ld-linux.so.2, но в Windows не существует эквивалента. К счастью, есть Microsoft Research, которая может сделать это для вас, под названием Detours. Вы можете найти это здесь: ссылка.

Демосценовая группа Farbrausch создала демо-захват инструмента под названием kkapture, который использует библиотеку Detours. Однако их инструмент предназначен для приложений, которые не требуют ввода данных пользователем, поэтому они в основном запускают демонстрации с фиксированной частотой кадров, подключаясь ко всем возможным функциям времени, таким как timeGetTime(), GetTickCount() и QueryPerformanceCounter(). Это полностью рад. Презентация, написанная ryg (я думаю?) О внутренностях kkapture, может быть найдена здесь. Я думаю, что это вас интересует.

Для получения дополнительной информации о хуках Windows смотрите здесь и здесь.

РЕДАКТИРОВАТЬ:

Эта идея заинтриговала меня, поэтому я использовал Detours, чтобы подключиться к приложениям OpenGL и связываться с графикой. Вот Quake 2 с добавленным зеленым туманом: Quake 2 Hook

Еще немного информации о том, как работает Detours, поскольку я использовал его из первых рук:

Обход работает на двух уровнях. Фактическое перехватывание работает только в том же пространстве процесса, что и целевой процесс. Таким образом, в Detours есть функция для внедрения DLL в процесс и принудительного запуска его DLLMain, а также функций, которые должны использоваться в этой DLL. Когда DLLMain запущен, DLL должна вызвать DetourAttach(), чтобы указать функции для перехвата, а также функцию "обход", которая является кодом, который вы хотите переопределить.

Так что это в основном работает так:

  • У вас есть приложение запуска, единственной задачей которого является вызов DetourCreateProcessWithDll(). Он работает так же, как CreateProcessW, только с несколькими дополнительными параметрами. Это внедряет DLL в процесс и вызывает его DllMain().
  • Вы реализуете DLL, которая вызывает функции Detour и устанавливает функции батута. Это означает вызов DetourTransactionBegin(), DetourUpdateThread(), DetourAttach(), за которым следует DetourTransactionEnd().
  • Используйте панель запуска для внедрения DLL, которую вы внедрили в процесс.

Есть некоторые предостережения, хотя. Когда DllMain запущен, библиотеки, которые импортируются позже с помощью LoadLibrary(), еще не видны. Таким образом, вы не можете обязательно все настроить во время события вложения DLL. Обходной путь - отслеживать все функции, которые были переопределены до сих пор, и пытаться инициализировать другие функции внутри этих функций, которые вы уже можете вызывать. Таким образом, вы обнаружите новые функции, как только LoadLibrary отобразит их в область памяти процесса. Я не совсем уверен, насколько хорошо это будет работать для wglGetProcAddress, хотя. (Возможно, у кого-то еще есть идеи по этому поводу?)

Некоторые вызовы LoadLibrary() кажутся неудачными. Я тестировал с Quake 2, и DirectSound и waveOut API по какой-то причине не смогли инициализироваться. Я все еще расследую это.

Я нашел проект sourceforge под названием taksi:

http://taksi.sourceforge.net/

Такси не обеспечивает захват звука, хотя.

В прошлом я писал экранные грабберы (эпоха DirectX7-9). Я обнаружил, что старый добрый DirectDraw работает замечательно хорошо и надежно захватывает биты содержимого с аппаратным ускорением / видеоэкрана, который другие методы (D3D, GDI, OpenGL), кажется, оставляют пустым или зашифрованным. Это было очень быстро тоже.

Другие вопросы по тегам