一个更复杂的程序——透明带阴影的按钮+旋转的企鹅照片和炫彩字体
二进制程序:
效果图:
源代码:
<code class="language-cpp">#define _WIN32_WINNT 0x0601 #include <windows.h> #include <math.h> #include <d3d10_1.h> #include <d2d1.h> #include <dwrite.h> #include <wincodec.h> #include <directxmath.h> #pragma comment(lib, "d2d1.lib") #pragma comment(lib, "dwrite.lib") #pragma comment(lib, "d3d10_1.lib") HINSTANCE hInst; HWND hMainWnd; BOOL realtime = TRUE; // 实时绘图标志 LRESULT __stdcall WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Win32错误处理,这个程序暂时没用到 void ShowSysError(DWORD errcode) { wchar_t *lpMsgBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); MessageBox(NULL, lpMsgBuf, L"错误", MB_ICONERROR); LocalFree(lpMsgBuf); } // 入口点 int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *szCmdLine, int nShowCmd) { HRESULT hr = CoInitialize(NULL); // WIC图片解码器要求初始化COM // 注册窗口类别 WNDCLASS wc; wc.style = CS_VREDRAW | CS_HREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); // 注意!防闪烁:NULL_BRUSH wc.lpszMenuName = NULL; wc.lpszClassName = L"MainWndClass"; if (!RegisterClass(&wc)) return 0; // 创建窗口,可以指定WS_CLIPCHILDREN以支持Win32控件 RECT rc = { 0, 0, 640, 480 }; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, FALSE); hMainWnd = CreateWindow(L"MainWndClass", L"Main Window", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL); if (!hMainWnd) return 0; // 显示并第一次绘制窗口 ShowWindow(hMainWnd, nShowCmd); UpdateWindow(hMainWnd); // 消息循环 MSG msg; memset(&msg, 0, sizeof msg); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { // 进行实时绘图或等待下一个消息 if (realtime) SendMessage(hMainWnd, WM_TIMER, 222, 0); else WaitMessage(); } } CoUninitialize(); // 卸载COM return (int)msg.wParam; } // 窗口消息处理函数 LRESULT __stdcall WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { // 静态对象 static ID2D1Factory *d2dfac; static IDWriteFactory *dwfac; static IWICImagingFactory *wicfac; static ID3D10Device1 *d3ddev; static IDXGISwapChain *swpch; static ID3D10RenderTargetView *d3drtv; static ID2D1RenderTarget *rt; static ID2D1Bitmap *bmpPenguins; static ID3D10Texture2D *tx2dPic; static ID3D10ShaderResourceView *tx2dPicSRV; static ID2D1RenderTarget *rtPic; static ID3D10Effect *eff; static ID3D10InputLayout *inlayoutPic; static ID3D10Buffer *vbPic; // 每次进入都要用到的对象 HRESULT hr; RECT rc; GetClientRect(hWnd, &rc); int width = rc.right - rc.left; int height = rc.bottom - rc.top; // 自绘按钮状态 static RECT rcbtn = { 10, 10, 100, 50 }; static RECT rcbtn2 = { 10 + 5, 10 + 5, 100 + 5, 50 + 5 }; static int trbtn = 0; // D3D效果文件 static char efftxt[] = R"EFFECTS( float4x4 matWorld; float4x4 matViewAndProj; float timerad; Texture2D tx2dPic; SamplerState tx2dPicSampStat { Texture = tx2dPic; Filter = MIN_MAG_MIP_LINEAR; AddressU = Wrap; AddressV = Wrap; }; void PicVS(float4 pos0 : POSITION, float2 tex0 : TEXCOORD, out float4 pos1 : SV_POSITION, out float2 tex1 : TEXCOORD) { pos1 = mul(mul(pos0, matWorld), matViewAndProj); tex1 = tex0; return; } float4 PicPS(float4 pos : SV_Position, float2 tex0 : TEXCOORD) : SV_Target { return tx2dPic.Sample(tx2dPicSampStat, tex0); } RasterizerState rasstate { CULLMODE = 1; }; technique10 Pic { pass P0 { SetVertexShader(CompileShader(vs_4_0, PicVS())); SetGeometryShader(NULL); SetPixelShader(CompileShader(ps_4_0, PicPS())); SetRasterizerState(rasstate); } } )EFFECTS"; // 窗口创建 if (msg == WM_CREATE) { // 创建D2D、DWrite、WIC根工厂 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dfac); hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), (IUnknown**)&dwfac); // 注意!一定要用CLSID_WICImagingFactory1,不要用默认的CLSID_WICImagingFactory(2),否则需要安装IE11 hr = CoCreateInstance(CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, __uuidof(wicfac), (void**)&wicfac); // 创建D3D设备和DXGI交换链,包括后台缓冲区 DXGI_SWAP_CHAIN_DESC swapDesc; memset(&swapDesc, 0, sizeof swapDesc); swapDesc.BufferDesc.Width = width; swapDesc.BufferDesc.Height = height; swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // 注意!D2D兼容选项:BGRA swapDesc.BufferDesc.RefreshRate.Numerator = 60; swapDesc.BufferDesc.RefreshRate.Denominator = 1; swapDesc.SampleDesc.Count = 1; swapDesc.SampleDesc.Quality = 0; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapDesc.BufferCount = 1; swapDesc.OutputWindow = hWnd; swapDesc.Windowed = TRUE; swapDesc.Flags = 0; hr = D3D10CreateDeviceAndSwapChain1(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, D3D10_CREATE_DEVICE_BGRA_SUPPORT, // 重要!D2D兼容选项:BGRA D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &swapDesc, &swpch, &d3ddev); if (FAILED(hr)) { hr = D3D10CreateDeviceAndSwapChain1(NULL, D3D10_DRIVER_TYPE_WARP, NULL, D3D10_CREATE_DEVICE_BGRA_SUPPORT, // 重要!D2D兼容选项:BGRA D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &swapDesc, &swpch, &d3ddev); } // 禁止Alt-Enter IDXGIFactory *dxgifac; hr = swpch->GetParent(__uuidof(dxgifac), (void**)&dxgifac); hr = dxgifac->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); dxgifac->Release(); // 创建渲染对象 SendMessage(hWnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(width, height)); // 解码图片 IWICBitmapDecoder *wicdec; hr = wicfac->CreateDecoderFromFilename(L"Penguins.jpg", NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &wicdec); if (SUCCEEDED(hr)) { // 获取第一帧 IWICBitmapFrameDecode *wicorgfmt; hr = wicdec->GetFrame(0, &wicorgfmt); // 转换为D2D可用的PBGRA格式 IWICFormatConverter *wicd2dfmt; hr = wicfac->CreateFormatConverter(&wicd2dfmt); hr = wicd2dfmt->Initialize(wicorgfmt, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom); // 创建D2D位图 hr = rt->CreateBitmapFromWicBitmap(wicd2dfmt, &bmpPenguins); // 释放临时对象 wicdec->Release(); wicorgfmt->Release(); wicd2dfmt->Release(); } else { MessageBox(hWnd, L"找不到Penguins.jpg", L"警告", MB_ICONWARNING); } // 创建独立纹理以及D3D资源对象 D3D10_TEXTURE2D_DESC tx2dPicDesc; tx2dPicDesc.Width = 320; tx2dPicDesc.Height = 240; tx2dPicDesc.MipLevels = 1; tx2dPicDesc.ArraySize = 1; tx2dPicDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; tx2dPicDesc.SampleDesc.Count = 1; tx2dPicDesc.SampleDesc.Quality = 0; tx2dPicDesc.Usage = D3D10_USAGE_DEFAULT; tx2dPicDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; tx2dPicDesc.CPUAccessFlags = 0; tx2dPicDesc.MiscFlags = 0; hr = d3ddev->CreateTexture2D(&tx2dPicDesc, NULL, &tx2dPic); hr = d3ddev->CreateShaderResourceView(tx2dPic, NULL, &tx2dPicSRV); // 创建对于纹理的渲染对象 IDXGISurface *surfPic; hr = tx2dPic->QueryInterface(__uuidof(surfPic), (void**)&surfPic); hr = d2dfac->CreateDxgiSurfaceRenderTarget(surfPic, D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), 96, 96, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT ), &rtPic); surfPic->Release(); // 创建效果 ID3D10Blob *effcode = NULL, *errmsg = NULL; hr = D3D10CompileEffectFromMemory(efftxt, strlen(efftxt), "main.cpp:efftxtPic", NULL, NULL, 0, 0, &effcode, &errmsg); if (FAILED(hr) && errmsg) { OutputDebugStringA((char*)errmsg->GetBufferPointer()); #ifdef _M_IX86 __asm int 3 #else DebugBreak(); #endif } hr = D3D10CreateEffectFromMemory(effcode->GetBufferPointer(), effcode->GetBufferSize(), 0, d3ddev, NULL, &eff); ID3D10EffectPass *pass = eff->GetTechniqueByName("Pic")->GetPassByName("P0"); D3D10_PASS_DESC passdesc; hr = pass->GetDesc(&passdesc); // 创建输入格式 D3D10_INPUT_ELEMENT_DESC inputlayout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 }, }; hr = d3ddev->CreateInputLayout(inputlayout, 2, passdesc.pIAInputSignature, passdesc.IAInputSignatureSize, &inlayoutPic); // 创建顶点缓冲区 float buf[] = { -2.0f, 1.5f, 0.0f, 0.0f, 0.0f, 2.0f, 1.5f, 0.0f, 1.0f, 0.0f, 2.0f, -1.5f, 0.0f, 1.0f, 1.0f, -2.0f, 1.5f, 0.0f, 0.0f, 0.0f, 2.0f, -1.5f, 0.0f, 1.0f, 1.0f, -2.0f, -1.5f, 0.0f, 0.0f, 1.0f, }; D3D10_BUFFER_DESC bufdesc; bufdesc.ByteWidth = sizeof buf; bufdesc.Usage = D3D10_USAGE_DEFAULT; bufdesc.BindFlags = D3D10_BIND_VERTEX_BUFFER; bufdesc.CPUAccessFlags = 0; bufdesc.MiscFlags = 0; D3D10_SUBRESOURCE_DATA subresdata; subresdata.pSysMem = buf; subresdata.SysMemPitch = 0; subresdata.SysMemSlicePitch = 0; hr = d3ddev->CreateBuffer(&bufdesc, &subresdata, &vbPic); return 0; } // 窗口大小变化 if (msg == WM_SIZE) { // 释放旧对象 if (rt) { rt->Release(); rt = NULL; } if (d3drtv) { d3drtv->Release(); d3drtv = NULL; } if (swpch && d2dfac && d3ddev) { // 调整后台缓冲区 hr = swpch->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0); // 获取后台缓冲区 ID3D10Texture2D *backbuffer; hr = swpch->GetBuffer(0, __uuidof(backbuffer), (void**)&backbuffer); IDXGISurface *surf; hr = backbuffer->QueryInterface(__uuidof(surf), (void**)&surf); // 创建D2D渲染对象 float dpix, dpiy; d2dfac->GetDesktopDpi(&dpix, &dpiy); hr = d2dfac->CreateDxgiSurfaceRenderTarget(surf, D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpix, dpiy, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT ), &rt); // 创建D3D渲染对象 hr = d3ddev->CreateRenderTargetView(backbuffer, NULL, &d3drtv); // 释放临时对象 backbuffer->Release(); surf->Release(); } return 0; } // 鼠标左键按下 if (msg == WM_LBUTTONDOWN) { POINT pt; SetCapture(hWnd); pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); if (PtInRect(&rcbtn, pt)) trbtn = 5; if (PtInRect(&rcbtn2, pt)) trbtn = 5; InvalidateRect(hWnd, NULL, TRUE); return 0; } // 鼠标左键弹起 if (msg == WM_LBUTTONUP) { trbtn = 0; ReleaseCapture(); InvalidateRect(hWnd, NULL, TRUE); return 0; } // 需要绘图(WM_PAINT)或实时绘图(WM_TIMER,222)见入口点内部的消息循环 if (msg == WM_PAINT || (msg == WM_TIMER && wParam == 222)) { // 移除WM_PAINT消息 if (msg == WM_PAINT) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } // 防崩溃 if (!rt) return 0; if (!swpch) return 0; // 创建临时资源 ID2D1SolidColorBrush *brtransblue; ID2D1SolidColorBrush *brtransblack; ID2D1SolidColorBrush *brwhite; IDWriteTextFormat *tfsegoeui; hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Blue, 0.5f), &brtransblue); hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gray, 0.5f), &brtransblack); hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brwhite); hr = dwfac->CreateTextFormat(L"Segoe UI", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 12, L"", &tfsegoeui); hr = tfsegoeui->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); hr = tfsegoeui->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); // 开始底部绘制 rt->BeginDraw(); rt->Clear(D2D1::ColorF(D2D1::ColorF::Wheat)); // 绘制按钮 rt->FillRoundedRectangle(D2D1::RoundedRect( D2D1::Rect(rcbtn2.left, rcbtn2.top, rcbtn2.right, rcbtn2.bottom), 10, 10), brtransblack); rt->FillRoundedRectangle(D2D1::RoundedRect( D2D1::Rect(rcbtn.left + trbtn, rcbtn.top + trbtn, rcbtn.right + trbtn, rcbtn.bottom + trbtn), 10, 10), brtransblue); #define str L"按钮1" rt->DrawText(str, lstrlen(str), tfsegoeui, D2D1::Rect(rcbtn.left + trbtn, rcbtn.top + trbtn, rcbtn.right + trbtn, rcbtn.bottom + trbtn), brwhite); #undef str // 结束底部绘制 hr = rt->EndDraw(); // 释放临时资源 if (tfsegoeui) tfsegoeui->Release(); if (brwhite) brwhite->Release(); if (brtransblack) brtransblack->Release(); if (brtransblue) brtransblue->Release(); if (d3ddev && rtPic) { using namespace DirectX; float timerad = GetTickCount() % 2000 / 2000.0f * 2 * XM_PI; // 创建临时资源 ID2D1SolidColorBrush *brred; IDWriteTextFormat *tfsegoeuibig; hr = rtPic->CreateSolidColorBrush( D2D1::ColorF( pow(sin(timerad), 2), pow(cos(timerad + XM_PI / 6), 2), pow(sin(timerad + XM_PI / 3), 2)), &brred); hr = dwfac->CreateTextFormat(L"Segoe UI", NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 24, L"", &tfsegoeuibig); hr = tfsegoeuibig->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); hr = tfsegoeuibig->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); rtPic->BeginDraw(); // 向纹理绘制图片 if (bmpPenguins) rtPic->DrawBitmap(bmpPenguins, &D2D1::RectF(0, 0, 320, 240)); else rtPic->Clear(D2D1::ColorF(D2D1::ColorF::Blue)); #define str L"画布里的企鹅" rtPic->DrawText(str, lstrlen(str), tfsegoeuibig, D2D1::Rect(0, 0, 320, 240), brred); #undef str // 结束纹理绘制 hr = rtPic->EndDraw(); // 释放临时资源 brred->Release(); tfsegoeuibig->Release(); // 设置必要的D3D状态 // 设置渲染目标 d3ddev->OMSetRenderTargets(1, &d3drtv, NULL); // 设置视口参数 D3D10_VIEWPORT vp; vp.TopLeftX = 0; vp.TopLeftY = 0; vp.Width = width; vp.Height = height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; d3ddev->RSSetViewports(1, &vp); // 生成3D参数 XMMATRIX matWorld = XMMatrixRotationY(timerad); XMMATRIX matView = XMMatrixLookAtLH( XMVectorSet(0.0f, 3.0f, -8.0f, 0.0f), XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)); XMMATRIX matProj = XMMatrixPerspectiveFovLH( XM_PI / 4, width / (float)height, 1.0f, 100.0f); XMMATRIX matViewAndProj = matView * matProj; // 上传参数 eff->GetVariableByName("timerad")->AsScalar()->SetFloat(timerad); eff->GetVariableByName("matWorld")->AsMatrix()->SetMatrix((float*)&matWorld); eff->GetVariableByName("matViewAndProj")->AsMatrix()->SetMatrix((float*)&matViewAndProj); eff->GetVariableByName("tx2dPic")->AsShaderResource()->SetResource(tx2dPicSRV); // 设置输入状态 d3ddev->IASetInputLayout(inlayoutPic); UINT stride = 4 * 5, offset = 0; d3ddev->IASetVertexBuffers(0, 1, &vbPic, &stride, &offset); d3ddev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // 应用效果 eff->GetTechniqueByName("Pic")->GetPassByName("P0")->Apply(0); // 绘制图形 d3ddev->Draw(6, 0); } // D3D上屏 hr = swpch->Present(0, 0); // 释放临时资源 return 0; } // 窗口结束 if (msg == WM_DESTROY) { // 释放静态对象 if (vbPic) { vbPic->Release(); vbPic = NULL; } if (inlayoutPic) { inlayoutPic->Release(); inlayoutPic = NULL; } if (eff) { eff->Release(); eff = NULL; } if (rtPic) { rtPic->Release(); rtPic = NULL; } if (tx2dPicSRV) { tx2dPicSRV->Release(); tx2dPicSRV = NULL; } if (tx2dPic) { tx2dPic->Release(); tx2dPic = NULL; } if (bmpPenguins) { bmpPenguins->Release(); bmpPenguins = NULL; } if (rt) { rt->Release(); rt = NULL; } if (d3drtv) { d3drtv->Release(); d3drtv = NULL; } if (swpch) { swpch->Release(); swpch = NULL; } if (d3ddev) { d3ddev->Release(); d3ddev = NULL; } if (wicfac) { wicfac->Release(); wicfac = NULL; } if (dwfac) { dwfac->Release(); dwfac = NULL; } if (d2dfac) { d2dfac->Release(); d2dfac = NULL; } PostQuitMessage(0); return 0; } // 其它消息交给系统处理 return DefWindowProc(hWnd, msg, wParam, lParam); } </directxmath.h></wincodec.h></dwrite.h></d2d1.h></d3d10_1.h></math.h></windows.h></code>
200字以内,仅用于支线交流,主线讨论请采用回复功能。