Наложение на полноэкранное 3D-приложение

Я хочу отображать некоторую настраиваемую графику поверх стороннего полноэкранного приложения Windows.

Вы играли в какие-нибудь игры Steam? У него есть исполняемый файл GameOverlayUI.exe, который позволяет вам получать доступ к окнам Steam из игры. (Элементы графического интерфейса выглядят нарисованными на заказ, я не знаю, является ли это необходимостью; но они выглядят точно так же внутри игры, как и на рабочем столе.) Это даже доходит до «затемнения» игровой графики в фон.

Мне интересно, как можно было написать такое приложение.

Мне также интересно, насколько широким будет спектр решений. Могли бы вы использовать один метод для всех приложений OpenGL? Для всех приложений Direct3D без DirectDraw? Для всех полноэкранных приложений Windows?

Я ищу более «современные» и универсальные решения - наложение командной строки или индексированного цветного приложения 320x240 не проблема.

Изменить: некоторые пояснения: полноэкранное приложение не мое. Это может быть что угодно. Скорее всего, это будет трехмерная игра. Я хочу отобразить, скажем, цифровые часы в правом нижнем углу экрана поверх игровой графики. Я бы по возможности избегал таких уловок, как внедрение кода или отладка процесса.


person aib    schedule 29.05.2009    source источник


Ответы (4)


Ну вот еще один пример. Xfire - это программа чата, которая накладывает свой интерфейс на игры, чтобы вы могли получать сообщения во время игры. Они делают это, редактируя d3d / opengl DLL на уровне памяти процесса, например, вставляя переходы сборки. Я знаю это, потому что их метод приводил к сбою моего мода, и я действительно видел странный ассемблерный код, который они вводили в d3d9.dll.

Итак, вот как это делает Xfire:

  1. нацелить процесс игры
  2. поиск в памяти процесса для d3d.dll / opengl.dll
  3. внедрить в процесс DLL, содержащую пользовательскую логику интерфейса
  4. подключать функции интерфейса к потоку программы, вручную записывая переходы сборки в функции d3d / opengl, находящиеся в памяти процесса

Нет другого мыслимого способа, поскольку вам нужно захватить графическое устройство, принадлежащее этой игре. Готов поспорить, что Steam делает это так же, как Xfire. Так что да, это нелегкий способ сделать это, если только кто-то не создал полезную библиотеку, чтобы делать все, что вам нужно, на низком уровне.

Вы также спросили о затемнении игровой графики под вашим оверлеем. Это простой вызов DrawPrimitive для рисования прозрачного прямоугольника с заливкой на экране.

person Shaun Lebron    schedule 05.11.2009
comment
Будьте осторожны, делая это с многопользовательскими играми, требующими античитерского программного обеспечения, такого как Punkbuster! Внедрение пользовательских библиотек в запущенные игры рассматривается как навязчивое манипулирование игрой и читерство. Программы (такие как Xfire, Teamspeak Overlay, ...) внесены в белый список и больше не будут вызывать нарушений читерства, но это процесс обучения, выполняемый Anti-Cheat-Vendor, который может занять много времени там, где ваша программа не может быть эффективно используется. - person Robert; 30.03.2010

Создал Gist с полным кодом здесь

Это довольно эзотерическое искусство, и есть несколько способов сделать это. Я предпочитаю то, что называется Direct3D Wrapper. Прошло много лет с тех пор, как я сделал это, но однажды я сделал это с игрой. Возможно, я упустил некоторые детали, но я просто надеюсь проиллюстрировать идею.

Все игры Direct3D9 должны вызывать IDirect3DDevice9 :: EndScene после того, как экран будет отрисован и готов к отрисовке. Так что теоретически мы можем поместить внутрь EndScene собственный код, который рисует то, что мы хотим, поверх игры. Мы делаем это, создавая для игры класс, который выглядит как IDirect3DDevice9, но на самом деле это просто оболочка, которая перенаправляет все вызовы функций реальному классу. Итак, теперь в нашем пользовательском классе наша функция-оболочка для EndScene выглядит так:

HRESULT IDirect3DDevice9::EndScene()
{
   // draw overlays here

   realDevice.EndScene();
}

Затем мы компилируем его в DLL с именем d3d9.dll и помещаем в каталог исполняемого файла игры. Настоящий d3d9.dll должен находиться в папке / system32, но игра сначала просматривает текущий каталог, а вместо этого загружает наш. Как только игра загружается, она использует нашу DLL для создания экземпляра нашего класса-оболочки. И теперь каждый раз, когда вызывается EndScene, выполняется наш код, чтобы отрисовывать на экране то, что мы хотим.

Я думаю, вы можете попробовать тот же принцип с OpenGL. Но я думаю, что некоторые современные игры пытаются это предотвратить, чтобы предотвратить подделку.

person Shaun Lebron    schedule 02.11.2009
comment
есть ли оболочка, в которой я могу использовать код в приложении C #? Спасибо - person smedasn; 11.11.2020

Я думал об этом. Для OpenGL я бы подключил SwapBuffers WGL к обходным путям, рисовал свои материалы после игры, а затем сам менял буферы местами.

person Community    schedule 16.11.2009

Я делал наложения как с окнами просмотра DirectX, так и с WPF (на самом деле, одно и то же), используя наложение, чтобы рисовать приятные 2D-элементы GDI + поверх 3D-визуализаций.

Мне удалось использовать панель поверх «фактической» визуализации, установив цвет на прозрачный, за исключением фрагментов, которые я хотел наложить. Работает довольно хорошо, но передача щелчка, перетаскивания и других событий через слой «рисования» в трехмерный элемент управления была немного затруднительной.

person moobaa    schedule 29.05.2009
comment
Прошу прощения, если мой вопрос не был ясен: в моем случае фактическая визуализация создается сторонним приложением. Однажды я сделал то, что вы сказали, в контексте одного приложения. Я не знаю, актуален ли ваш ответ в контексте двух разных приложений; если это так, как мне создать дочернюю панель в контексте полноэкранного родителя? - person aib; 31.05.2009
comment
@moobaa - не могли бы вы уточнить, как эту технику можно использовать между первой частью и сторонним приложением? - person Scuba Steve; 22.10.2020