2015-01-18 4 views
2

Я хочу получить быстрый доступ к пикселям игры, которая использует directX9 или 10. Поэтому я использовал библиотеку D3D9. Должен ли я использовать библиотеку D3D10, когда это игра directX10? Если это так, следующая часть не имеет значения.Получите пиксели от IDirect3DSurface9?

Следующий код работает, за исключением того, что я получаю нули только в qDebug("%.2f, %.2f, %.2f, %.2f", pixel[0].r, pixel[0].g, pixel[0].b, pixel[0].a);, хотя он должен иметь другой цвет, и я не знаю, почему. Есть ли ошибка при обмене данными?

Initialization:

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); 
} 

Capture:

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; 
+0

Вы печатаете только первый пиксель. Вы абсолютно уверены, что один пиксель не черный? Попробуйте напечатать больше из них в for-loop. – Brandon

+0

@Brandon Да, я проверил его, и это абсолютно черный экран :(Я пользуюсь неправильным буфером? –

+0

Какую функцию вы вызываете в приведенном выше коде для захвата? ' Present'? 'EndScene'? Ваш код захвата выглядит хорошо для меня. Фактически я использую тот же самый код в своем крючке Direct-X. – Brandon

ответ

3

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

Чтобы проверить, действительно ли захвачен задний буфер, я установил фон окна в темно-синий цвет. Затем, когда я вызываю функцию захвата, я сравниваю все цвета в буфере с исходным цветом фона. Это позволяет мне знать, отличается ли один пиксель от фона.

Если это другое, мы "возможно "не удалось захватить фон. В противном случае, мы хорошо ..

Я проверил ниже, и это действительно захватывает BackBuffer прекрасно .. Я даже спас BackBuffer в растровое изображение, чтобы увидеть, если она захватывает его, и он делает ..

В общем, нет ничего плохого в вашем коде, но я не могу гарантировать, что вы используете его правильно. Другими словами, вы не показали мне, где вы называете ваш код захвата обратного буфера. Есть много мест, которые он может но лучшие места находятся либо на крючке, либо на 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; 
} 
+0

Обновлено, чтобы сохранить тот же исходный код, который у вас есть в вашем OP .. Это показывает, что нет абсолютно ничего плохого в коде, который вы показываете в OP .. – Brandon

+0

Хорошо, когда я напрямую использую 'Clear()', 'BeginScene()' и т. Д. В моем коде, тогда я могу прочитать правильный цвет. Теперь мой вопрос: как я могу получить эту информацию из другого приложения/игры? Что мне нужно вызвать? получить доступ к буфере? –

+0

Вам нужно подключить функцию EndScene другой игры или настоящую функцию. На крючке вы вызываете захват для захвата обратного буфера. – Brandon

Смежные вопросы