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

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

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

Есть идеи?

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

Ответы [ 3 ]

14 голосов
/ 18 августа 2010

Есть несколько подходов к этой проблеме. Большинство из них неприглядны, и это полностью зависит от того, какой тип графического 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 см. здесь и здесь .

EDIT:

Эта идея заинтриговала меня, поэтому я использовал 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 по какой-то причине не смогли инициализироваться. Я все еще расследую это.

0 голосов
/ 22 августа 2010

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

0 голосов
/ 16 августа 2010

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

http://taksi.sourceforge.net/

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

...