不需要DirectX SDK(因此不需要用户安装DirectX运行库),但是需要VS2008+,因为d3d9.h头文件是VS2008才加进来的。本帖作者使用的是VS2010。
贴图的方法是用GDI+加载图片,然后再将其加载为Direct3D纹理,然后创建一个XYZRHW和TEX1的顶点数据,该数据包含一个TRIANGLESTRIP(三角形带)图形,其中XYZRHW的内容为屏幕坐标X和Y、深度值、常数1,TEX1的内容为纹理坐标U和V。
此处要注意边界处的U和V的计算:
- 右侧边界处U=(width+1)/(float)width
- 下侧边界处V=(height+1)/(float)height
原因是右侧和下侧的1像素并不会显示出来,所以不能直接使用1.0f。
需要打开AlphaBlend功能以实现半透明图片。
如果与3D绘图同时使用,绘制前还应该设置D3DRS_ZWRITEENABLE为FALSE避免影响深度值。
<code class="language-cpp">#define UNICODE 1
#include <windows.h>
#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
HINSTANCE hInst;
HWND hMainWnd;
HMODULE rgb9rast;
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 dev;
LPDIRECT3DVERTEXBUFFER9 vbuf1;
LPDIRECT3DTEXTURE9 bmp1;
struct ScreenBitmapVBData
{
float x, y, z, rhw;
float tu, tv;
};
#define ScreenBitmapVBFVF (D3DFVF_XYZRHW|D3DFVF_TEX1)
HRESULT CreateScreenBitmapVB(LPDIRECT3DDEVICE9 dev, int left, int top, int width, int height, LPDIRECT3DVERTEXBUFFER9 &vbuf)
{
HRESULT hr = S_OK;
// 构造顶点缓冲区数据
ScreenBitmapVBData vbufdata[] =
{
left, top, 0.0f, 1.0f, 0.0f, 0.0f,
left + width, top, 0.0f, 1.0f, (width + 1) / (float)width, 0.0f,
left, top + height, 0.0f, 1.0f, 0.0f, (height + 1) / (float)height,
left + width, top + height, 0.0f, 1.0f, (width + 1) / (float)width, (height + 1) / (float)height,
};
// 创建顶点缓冲区
hr = dev->CreateVertexBuffer(sizeof vbufdata, 0, ScreenBitmapVBFVF, D3DPOOL_MANAGED, &vbuf, NULL);
if (FAILED(hr)) return hr;
// 将缓冲区数据上传到D3D
void *vbufdest;
vbuf->Lock(0, 0, &vbufdest, 0);
memcpy(vbufdest, vbufdata, sizeof vbufdata);
vbuf->Unlock();
return hr;
}
HRESULT LoadTexture(LPDIRECT3DDEVICE9 dev, const WCHAR *filename, LPDIRECT3DTEXTURE9 &tex, int &width, int &height)
{
HRESULT hr = S_OK;
// 使用GDI+读取图片
Bitmap bmp(filename);
if (bmp.GetLastStatus() != Ok) return E_FAIL;
width = bmp.GetWidth();
height = bmp.GetHeight();
// 创建D3D纹理
hr = dev->CreateTexture(width, height, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex, NULL);
if (FAILED(hr)) return hr;
// 将GDI+图片数据加载到D3D纹理
BitmapData srclock;
bmp.LockBits(&Rect(0, 0, width, height), ImageLockModeRead, PixelFormat32bppARGB, &srclock);
D3DLOCKED_RECT dstlock;
tex->LockRect(0, &dstlock, NULL, 0);
for (int i = 0; i < height; i++)
{
char *srcptr = (char*)srclock.Scan0 + srclock.Stride * i;
char *dstptr = (char*)dstlock.pBits + dstlock.Pitch * i;
memcpy(dstptr, srcptr, width * 4);
}
tex->UnlockRect(0);
bmp.UnlockBits(&srclock);
return hr;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
D3DPRESENT_PARAMETERS pp = {};
pp.Windowed = TRUE;
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
pp.EnableAutoDepthStencil = TRUE;
pp.AutoDepthStencilFormat = D3DFMT_D16;
if (msg == WM_CREATE)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (d3d == NULL) DestroyWindow(hWnd);
// 创建硬件加速设备
HRESULT hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &dev);
if (FAILED(hr))
{
// 检查系统中有没有已知的软件加速引擎
if (!rgb9rast)
rgb9rast = LoadLibrary(L"rgb9rast.dll");
if (!rgb9rast)
rgb9rast = LoadLibrary(L"rgb9rast_2.dll");
if (rgb9rast)
{
void *entry = GetProcAddress(rgb9rast, "D3D9GetSWInfo");
if (entry)
{
d3d->RegisterSoftwareDevice(entry);
}
}
// 创建软件加速设备
hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_SW, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &pp, &dev);
if (FAILED(hr)) DestroyWindow(hWnd);
}
// 加载
int width = 0, height = 0;
LoadTexture(dev, L"role.png", bmp1, width, height);
// 创建用于贴图的顶点缓冲区
CreateScreenBitmapVB(dev, 10, 10, width, height, vbuf1);
}
if (msg == WM_SIZE)
{
dev->Reset(&pp); // 响应改变大小,注意D3DPOOL_DEFAULT资源需要重新创建,D3DPOOL_MANAGED资源不需要
return 0;
}
if (msg == WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
dev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 128, 128), 1.0f, 0); // 清除背景
dev->BeginScene();
// 设置渲染状态
dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); // 关闭背面剔除
dev->SetRenderState(D3DRS_LIGHTING, FALSE); // 关闭光照
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); // 打开AlphaBlend
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); // 设置混合源颜色=源颜色*源Alpha
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); // 设置混合目标颜色=目标颜色*(1-源Alpha)
dev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); // 设置线性插值
dev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); // 不影响深度值
// 绘制图片
dev->SetTexture(0, bmp1); // 设置纹理
dev->SetStreamSource(0, vbuf1, 0, sizeof (ScreenBitmapVBData)); // 设置顶点缓冲区
dev->SetFVF(ScreenBitmapVBFVF); // 设置顶点格式
dev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); // 绘制图形(这里我们使用三角形带)
dev->EndScene();
dev->Present(NULL, NULL, NULL, NULL); // 后台缓冲区提交到前台显示
}
if (msg == WM_DESTROY)
{
if (rgb9rast) FreeLibrary(rgb9rast);
if (vbuf1) vbuf1->Release();
if (bmp1) bmp1->Release();
if (dev) dev->Release();
if (d3d) d3d->Release();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst = hInstance;
ULONG_PTR gdiptoken;
GdiplusStartup(&gdiptoken, &GdiplusStartupInput(), NULL);
WNDCLASSEX wcex =
{
// 使用NULL_BRUSH防止闪烁
sizeof wcex, CS_VREDRAW|CS_HREDRAW, WndProc, 0, 0, hInstance, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(NULL_BRUSH), NULL, L"MainWndClass", LoadIcon(NULL, IDI_APPLICATION)
};
RegisterClassEx(&wcex);
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
hMainWnd = CreateWindow(L"MainWndClass", L"Main Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
ShowWindow(hMainWnd, nShowCmd);
UpdateWindow(hMainWnd);
MSG msg = {};
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
WaitMessage();
}
}
GdiplusShutdown(gdiptoken);
return msg.wParam;
}
</gdiplus.h></d3d9.h></windows.h></code>
200字以内,仅用于支线交流,主线讨论请采用回复功能。