ゆとりーなの日記

日記的な事を書いて行くと思はれる

フォグを切りたいならこれでいいんじゃなイカ?

こっそり書いておきます。こっそりです。
取り敢えず適当にフォグをかけるサンプル"test_fog.exe"を作ります。

#include <memory>
#include <Windows.h>
#include <d3d9.h>
#include <d3dx9.h>

namespace std {
template <>
struct default_delete<IDirect3D9> {
  void operator ()(const LPDIRECT3D9 ptr) const {
    ptr->Release();
  }
};

template <>
struct default_delete<IDirect3DDevice9> {
  void operator ()(const LPDIRECT3DDEVICE9 ptr) const {
    ptr->Release();
  }
};

template <>
struct default_delete<IDirect3DTexture9> {
  void operator ()(const LPDIRECT3DTEXTURE9 ptr) const {
    ptr->Release();
  }
};
}

LRESULT CALLBACK wnd_proc(const HWND window_handle, const UINT message, const WPARAM wp, const LPARAM lp) {
  if (message == WM_DESTROY) {
    PostQuitMessage(0);
    return 0;
  }
  return DefWindowProc(window_handle, message, wp, lp);
}

HWND init_app() {
  const WNDCLASSEX wc = {
    sizeof(wc),
    CS_HREDRAW | CS_VREDRAW,
    &wnd_proc,
    0,
    0,
    GetModuleHandle(nullptr),
    LoadIcon(nullptr, IDI_APPLICATION),
    LoadCursor(nullptr, IDC_ARROW),
    reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)),
    nullptr,
    "BASE",
    nullptr
  };
  if (!RegisterClassEx(&wc)) {
    return false;
  }
  return  CreateWindow("BASE", 
                       "フックテスト", 
                       WS_OVERLAPPEDWINDOW,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       nullptr,
                       nullptr,
                       GetModuleHandle(nullptr),
                       nullptr);
}

D3DPRESENT_PARAMETERS initPresentParameters(const HWND window_handle) {
  RECT client_size;
  GetClientRect(window_handle, &client_size);
  const D3DPRESENT_PARAMETERS present_parameters = {
    client_size.right,
    client_size.bottom,
    D3DFMT_UNKNOWN ,
    1,
    D3DMULTISAMPLE_NONE,
    0,
    D3DSWAPEFFECT_DISCARD,
    window_handle,
    TRUE,
    TRUE,
    D3DFMT_D24S8,
    0,
    D3DPRESENT_RATE_DEFAULT,
    D3DPRESENT_INTERVAL_DEFAULT
  };
  return present_parameters;
}

std::unique_ptr<IDirect3DDevice9> create_device(const HWND window_handle, const std::unique_ptr<IDirect3D9> &direct3d) {
  LPDIRECT3DDEVICE9 graphic_device;
  D3DPRESENT_PARAMETERS present_parameters = initPresentParameters(window_handle);
  if (FAILED(direct3d->CreateDevice(D3DADAPTER_DEFAULT, 
                                    D3DDEVTYPE_HAL, 
                                    window_handle, 
                                    D3DCREATE_HARDWARE_VERTEXPROCESSING, 
                                    &present_parameters, 
                                    &graphic_device))) {
    if (FAILED(direct3d->CreateDevice(D3DADAPTER_DEFAULT, 
                                      D3DDEVTYPE_HAL,
                                      window_handle, 
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
                                      &present_parameters,
                                      &graphic_device))) {
      if (FAILED(direct3d->CreateDevice(D3DADAPTER_DEFAULT,
                                        D3DDEVTYPE_REF, 
                                        window_handle, 
                                        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                        &present_parameters,
                                        &graphic_device))) {								
		    return std::unique_ptr<IDirect3DDevice9>(nullptr);
	    }
	  }
  }
  return std::unique_ptr<IDirect3DDevice9>(graphic_device);
}

struct vertex {
  float x;
  float y;
  float z;
  DWORD diffuse;
  float u;
  float v;
};

std::unique_ptr<IDirect3DTexture9> create_image_texture(const char * const name, const std::unique_ptr<IDirect3DDevice9> &device) {
  LPDIRECT3DTEXTURE9 texture;
  if (FAILED(D3DXCreateTextureFromFile(device.get(), name, &texture))) {
    return std::unique_ptr<IDirect3DTexture9>(nullptr);
  }
  return std::unique_ptr<IDirect3DTexture9>(texture);
}

int main() {
  const HWND window_handle = init_app();
  if (!window_handle) {
    return 0;
  }
  ShowWindow(window_handle, SW_RESTORE);
  const std::unique_ptr<IDirect3D9> direct3d(Direct3DCreate9(D3D_SDK_VERSION));
  if (!direct3d) {
    return 0;
  }
  const std::unique_ptr<IDirect3DDevice9> device = create_device(window_handle, direct3d);
  if (!device) {
    return 0;
  }
  const std::unique_ptr<IDirect3DTexture9> texture = create_image_texture("reimu.png", device);
  device->SetRenderState(D3DRS_LIGHTING, FALSE);
  device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
  device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
  device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
  device->SetRenderState(D3DRS_FOGENABLE, TRUE);
  device->SetRenderState(D3DRS_FOGCOLOR, D3DXCOLOR(1.f, 1.f, 1.f, 1.f));
  device->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_EXP2);
  device->SetRenderState(D3DRS_RANGEFOGENABLE, TRUE);
  const float density = 0.003f;
  device->SetRenderState(D3DRS_FOGDENSITY, *reinterpret_cast<const DWORD *>(&density));
  MSG message;
  for (;;) {
    if (PeekMessage(&message, nullptr, 0, 0, PM_NOREMOVE)) {
      const BOOL result = GetMessage(&message, nullptr, 0, 0);
      if (!(result && ~result)) {
        break;
      }
      TranslateMessage(&message);
      DispatchMessage(&message);
    } else {
      if (SUCCEEDED(device->BeginScene())) {
        device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_XRGB(0, 0, 0), 1.f, 0);
        RECT rect;
        D3DXMATRIX view;
        D3DXMATRIX proj;
        D3DXMATRIX world;
        D3DXVECTOR3 form(0.f, 0.f, 200.f);
        GetClientRect(window_handle, &rect);
        const vertex v1[4] = {
          {0.f, -64.f, 0.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 0.f, 0.f},
          {128.f, -64.f, 0.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 1.f, 0.f},
          {0.f, 64.f, 0.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 0.f, 1.f},
          {128.f, 64.f, 0.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 1.f, 1.f},
        };
        device->SetTexture(0, texture.get());
        device->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
        device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v1, sizeof(v1[0]));
        const vertex v2[4] = {
          {-128.f, -64.f, -256.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 0.f, 0.f},
          {0.f, -64.f, -256.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 1.f, 0.f},
          {-128.f, 64.f, -256.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 0.f, 1.f},
          {0.f, 64.f, -256.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f), 1.f, 1.f},
        };
        device->SetTexture(0, texture.get());
        device->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
        device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v2, sizeof(v2[0]));
        D3DXMatrixLookAtLH(&view, &D3DXVECTOR3(0.f, 0.f, 200.f), &D3DXVECTOR3(0.f, 0.f, 0.f), &D3DXVECTOR3(0.f, -1.f, 0.f));
        device->SetTransform(D3DTS_VIEW, &view);
        D3DXMatrixPerspectiveFovLH(&proj, D3DXToRadian(45.f), static_cast<float>(rect.right) / rect.bottom, 100.f, 1000.f);
        device->SetTransform(D3DTS_PROJECTION, &proj);
        device->EndScene();
      }
      device->Present(nullptr, nullptr, nullptr, nullptr);
    }
  }
  return message.wParam;
}

起動するとこんな感じの画面が出せます。

で、SetRenderStateをフックして、「フォグなんか使わせるかー!」の如くD3DRS_FOGENABLEが来たら只管FALSEをオリジナルに渡す関数を仕込ませた"d3d9.dll"を作ります。

#include <cstddef>
#define CINTERFACE
#include <d3d9.h>
#include <Shlwapi.h>
const std::size_t kBufferMax = 1024;

IDirect3D9 *(WINAPI *original_direct3d_create)(UINT) = nullptr;
HRESULT (WINAPI *original_create_device)(IDirect3D9 *,
                                         UINT,
                                         D3DDEVTYPE,
                                         HWND, 
                                         DWORD,
                                         D3DPRESENT_PARAMETERS *,
                                         IDirect3DDevice9 **) = nullptr;
HRESULT (WINAPI *original_set_render_state)(IDirect3DDevice9 *, D3DRENDERSTATETYPE, DWORD) = nullptr;

BOOL init() {
  char system_path_buffer[kBufferMax]; 
  GetSystemDirectory(system_path_buffer, kBufferMax);
  PathAppend(system_path_buffer, "D3D9.DLL");
  const HMODULE original_module = LoadLibrary(system_path_buffer);
  if (!original_module) {
    return FALSE;
  }
  original_direct3d_create = reinterpret_cast<IDirect3D9 *(WINAPI *)(UINT)>(GetProcAddress(original_module, "Direct3DCreate9"));
  if (!original_direct3d_create) {
    return FALSE;
  }
  return TRUE;
}

HRESULT WINAPI set_render_state(IDirect3DDevice9 * const device, const D3DRENDERSTATETYPE type, const DWORD flag) {
  return (*original_set_render_state)(device, type, type == D3DRS_FOGENABLE ? FALSE : flag);
}

HRESULT WINAPI create_device(IDirect3D9 * const direct3d,
                            const UINT adapter, 
                            const D3DDEVTYPE type, 
                            const HWND window,
                            const DWORD flag, 
                            D3DPRESENT_PARAMETERS * const param, 
                            IDirect3DDevice9 ** const device) {
  const HRESULT hr = (*original_create_device)(direct3d, adapter, type, window, flag, param, device);
  if (SUCCEEDED(hr)) {
    original_set_render_state = (*device)->lpVtbl->SetRenderState;
    DWORD old_protect;
    VirtualProtect(reinterpret_cast<void *>((*device)->lpVtbl), sizeof((*device)->lpVtbl), PAGE_EXECUTE_WRITECOPY, &old_protect);
    (*device)->lpVtbl->SetRenderState = set_render_state;
    VirtualProtect(reinterpret_cast<void *>((*device)->lpVtbl), sizeof((*device)->lpVtbl), old_protect, &old_protect);
  }
  return hr;
}

extern "C" {
IDirect3D9 * WINAPI Direct3DCreate9(UINT SDKVersion) {
  IDirect3D9 *direct3d = (*original_direct3d_create)(SDKVersion);
  original_create_device = direct3d->lpVtbl->CreateDevice;
  DWORD old_protect;
  VirtualProtect(reinterpret_cast<void *>(direct3d->lpVtbl), sizeof(direct3d->lpVtbl), PAGE_EXECUTE_WRITECOPY, &old_protect);
  direct3d->lpVtbl->CreateDevice = create_device;
  VirtualProtect(reinterpret_cast<void *>(direct3d->lpVtbl), sizeof(direct3d->lpVtbl), old_protect, &old_protect);
  return direct3d;
}
}

BOOL APIENTRY DllMain(HINSTANCE, DWORD reason, LPVOID) {
  return reason == DLL_PROCESS_ATTACH ? init() : TRUE;
}

出来た"d3d9.dll"を先の"test_fog.exe"と同じディレクトリに置いてから起動すると、

なんと、フォグが消えました。