Я хочу получить быстрый доступ к пикселям игры, которая использует DirectX9 или 10. Поэтому я использовал библиотеку D3D9. Нужно ли использовать библиотеку D3D10, когда это игра DirectX10? Если это правда, следующая часть не имеет значения.
Следующий код работает, за исключением того, что я получаю только нули в qDebug("%.2f, %.2f, %.2f, %.2f", pixel[0].r, pixel[0].g, pixel[0].b, pixel[0].a);
хотя у него должен быть другой цвет, и я понятия не имею, почему. Есть ли ошибка при обмене данными?
Инициализация:
IDirect3D9 *m_d3d;
IDirect3DDevice9 *m_d3ddev;
D3DDISPLAYMODE *m_d3ddm;
HRESULT hr;
m_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (m_d3d == NULL)
{
qFatal("Cannot create Direct3D!");
}
m_d3ddm = (D3DDISPLAYMODE *)calloc(1, sizeof(D3DDISPLAYMODE));
hr = m_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, m_d3ddm);
if (hr != D3D_OK)
{
m_d3d->Release();
qFatal("Cannot get DisplayMode!");
}
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = m_target;
d3dpp.BackBufferWidth = m_d3ddm->Width;
d3dpp.BackBufferHeight = m_d3ddm->Height;
d3dpp.BackBufferFormat = m_d3ddm->Format;
hr = m_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_target, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &m_d3ddev);
if (FAILED(hr))
{
m_d3d->Release();
qFatal("Failed CreateDevice!");
}
else
{
qDebug("Created Device with %i * %i", m_d3ddm->Width, m_d3ddm->Height);
}
Захватить:
IDirect3DSurface9 *renderTarget= NULL;
IDirect3DSurface9 *destTarget = NULL;
hr = m_d3ddev->GetRenderTarget(0, &renderTarget);
if (FAILED(hr))
{
qFatal("Failed GetRenderTarget!");
}
hr = m_d3ddev->CreateOffscreenPlainSurface(m_d3ddm->Width, m_d3ddm->Height, m_d3ddm->Format, D3DPOOL_SYSTEMMEM, &destTarget, NULL);
if (FAILED(hr))
{
qFatal("Failed CreateOffscreenPlainSurface!");
}
hr = m_d3ddev->GetRenderTargetData(renderTarget, destTarget);
if (FAILED(hr))
{
qFatal("Failed GetRenderTargetData!");
}
D3DLOCKED_RECT lr;
ZeroMemory(&lr, sizeof(D3DLOCKED_RECT));
hr = destTarget->LockRect(&lr, 0, D3DLOCK_READONLY);
if (FAILED(hr))
{
qFatal("Cannot lock rect!");
}
ARGB *pixel = (ARGB *)lr.pBits;
if (!pixel)
{
qFatal("No data!");
}
qDebug("%.2f, %.2f, %.2f, %.2f", pixel[0].r, pixel[0].g, pixel[0].b, pixel[0].a);
hr = destTarget->UnlockRect();
if (FAILED(hr))
{
qFatal("Cannot unlock rect!");
}
renderTarget->Release();
destTarget->Release();
очищающий
m_d3ddev->Release();
m_d3d->Release();
delete m_d3ddm;
delete m_d3d;
delete m_d3ddev;
Этот код представляет собой симуляцию того, что вы должны иметь. Он показывает, что на самом деле захватывается буфер. Я создал устройство точно так же, как вы. Я получаю задний буфер таким же образом ..
Чтобы проверить, действительно ли задний буфер захвачен, я установил фон окна в темно-синий цвет. Затем, когда я вызываю функцию захвата, я сравниваю все цвета в буфере с исходным цветом фона. Это позволяет мне узнать, отличается ли один пиксель от фона …
Если это отличается, мы «возможно» не смогли захватить фон. В остальном у нас все хорошо ..
Я протестировал нижеприведенное, и он действительно отлично захватывает буфер: .. Я даже сохранил буфер в растровое изображение, чтобы увидеть, захватывает ли он его и делает ли он …
В общем, в вашем коде нет ничего плохого, но я не могу гарантировать, что вы используете его правильно. Другими словами, вы не указали мне, где вы вызываете свой код захвата обратного буфера. можно назвать, но лучшие места находятся либо в ловушке EndScene, либо в ловушке Present. Если вы используете это в своем собственном приложении, то вызовите его в функции onDraw / onUpdate.
#include <tchar.h>
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <iostream>
HRESULT capture(IDirect3DDevice9* m_d3ddev, void* buffer, int& width, int& height, D3DFORMAT format);void onUpdate(HWND hwnd, IDirect3D9* d3d9, IDirect3DDevice9* d3ddev9, D3DFORMAT format)
{
d3ddev9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
d3ddev9->BeginScene();
//All drawing goes here..
d3ddev9->EndScene();
d3ddev9->Present(NULL, NULL, NULL, NULL); //Swaps the back and front buffers. Displays all drawing on the screen..RECT rect;
GetClientRect(hwnd, &rect);
int width = rect.right - rect.left, height = rect.bottom - rect.top;
unsigned char* buffer = new unsigned char[width * height * 4];
capture(d3ddev9, buffer, width, height, format);
unsigned char* px = buffer;
for(int i = 0; i < height; ++i)
{
for(int j = 0; j < width; ++j)
{
unsigned char B = *px++;
unsigned char G = *px++;
unsigned char R = *px++;
unsigned char A = *px++;
if(D3DCOLOR_XRGB(R, G, B) != D3DCOLOR_XRGB(0, 40, 100))
{
MessageBox(NULL, "FAILED.. Colour differs from original background..", NULL, 0);
}
}
}
delete[] buffer;
}
int onDisplay(HWND hwnd)
{
IDirect3D9* d3d9 = NULL;
IDirect3DDevice9* d3ddev9 = NULL;
D3DPRESENT_PARAMETERS Parameters = {0};
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE* dispMode = new D3DDISPLAYMODE();
d3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, dispMode);
Parameters.Windowed = true;
Parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
Parameters.hDeviceWindow = hwnd;
Parameters.BackBufferFormat = dispMode->Format;
d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &Parameters, &d3ddev9);
delete dispMode;
MSG messages;
ShowWindow(hwnd, SW_SHOW);
while(true)
{
if(PeekMessage(&messages, NULL, 0, 0, PM_REMOVE) > 0)
{
if(messages.message != WM_QUIT)
{
TranslateMessage(&messages);
DispatchMessageW(&messages);
continue;
}
break;
}
else
{
onUpdate(hwnd, d3d9, d3ddev9, Parameters.BackBufferFormat);
}
}
if(d3ddev9) d3ddev9->Release();
if(d3d9) d3d9->Release();
return messages.wParam;
}LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
WNDCLASSEX wincl;
wincl.hInstance = hThisInstance;
wincl.lpszClassName = "D3DWindow";
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if(!RegisterClassEx(&wincl)) return 0;
HWND hwnd = CreateWindowEx(0, "D3DWindow", "D3D9: Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, hThisInstance, NULL);
return onDisplay(hwnd);
}HRESULT capture(IDirect3DDevice9* m_d3ddev, void* buffer, int& width, int& height, D3DFORMAT format)
{
IDirect3DSurface9 *renderTarget= NULL;
IDirect3DSurface9 *destTarget = NULL;
HRESULT hr = m_d3ddev->GetRenderTarget(0, &renderTarget);
hr = m_d3ddev->CreateOffscreenPlainSurface(width, height, format, D3DPOOL_SYSTEMMEM, &destTarget, NULL);
if(FAILED(hr))
{
printf("Failed CreateOffscreenPlainSurface!");
}
hr = m_d3ddev->GetRenderTargetData(renderTarget, destTarget);
if(FAILED(hr))
{
printf("Failed GetRenderTargetData!");
}
D3DLOCKED_RECT lr;
ZeroMemory(&lr, sizeof(D3DLOCKED_RECT));
hr = destTarget->LockRect(&lr, 0, D3DLOCK_READONLY);
if(FAILED(hr))
{
printf("Cannot lock rect!");
}
if(lr.pBits)
{
memcpy(buffer, lr.pBits, width * height * 4);
}
hr = destTarget->UnlockRect();
if(FAILED(hr))
{
printf("Cannot unlock rect!");
}
renderTarget->Release();
destTarget->Release();
return hr;
}