绘制单通道位图时,Direct2D 失败

5
我是一名经验丰富的程序员,专门从事计算机图形学,主要使用Direct3D 9.0c、OpenGL和通用算法。目前,我正在评估Direct2D作为处理医学图像数据的专业应用程序的渲染技术。在渲染方面,它是一个x64桌面应用程序,以窗口模式运行(非全屏)。
即使是最初的步骤,我也遇到了一个我认为应该是小菜一碟的任务:在屏幕上渲染单通道位图。
在运行Windows 8.1的机器上,我创建了一个ID2D1DeviceContext,并将Direct3D交换链缓冲表面作为渲染目标。交换链是从HWND和缓冲格式DXGI_FORMAT_B8G8R8A8_UNORM创建的。注意:请参见末尾的代码片段。
随后,我创建了一个像素格式为DXGI_FORMAT_R8_UNORM,alpha模式为D2d1_ALPHA_MODE_IGNORE的位图。当在设备上下文上调用DrawBitmap(...)时,会触发一个断点调试,并显示“D2d DEBUG ERROR - This operation is not compatible with the pixel format of the bitmap”调试消息。
我知道这个输出非常清晰。同时,当将像素格式更改为DXGI_FORMAT_R8G8B8A8_UNORM并使用DXGI_ALPHA_MODE_IGNORE时,一切正常,我可以看到位图呈现出来了。然而,我简直无法相信这一点!自从有了显卡以来,它们就支持单通道纹理 - 每个3D图形应用程序都可以毫不犹豫地使用它们。这是显而易见的。
我试图在这里和谷歌上找到任何东西,但没有成功。我唯一能找到的提示是MSDN Direct2D页面上的(支持的像素格式)。文档通过不提及它来暗示DXGI_FORMAT_R8_UNORM确实不支持作为位图格式。我还发现了一些帖子讨论alpha掩码(使用DXGI_FORMAT_A8_UNORM),但那不是我想要的。 我错过了什么,无法说服Direct2D创建和绘制灰度位图?或者Direct2D真的不支持绘制R8或R16位图吗? 任何帮助都将不胜感激,因为我不知道如何解决这个问题。如果我不能使这些微不足道的基础工作正常,我想我必须停止深入研究Direct2D :-(。
以下是相关的代码片段,请注意,它们可能无法编译,因为我从我的C++/CLI代码即兴移植到了普通的C++上。此外,我抛弃了所有的错误检查和其他噪音:
设备、设备上下文和交换链的创建(D3D和Direct2D):
// Direct2D factory creation
D2D1_FACTORY_OPTIONS options = {};
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
ID2D1Factory1* d2dFactory;
D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, options, &d2dFactory);

// Direct3D device creation
const auto type = D3D_DRIVER_TYPE_HARDWARE;
const auto flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
ID3D11Device* d3dDevice;
D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0, D3D11_SDK_VERSION, &d3dDevice, nullptr, nullptr);

// Direct2D device creation
IDXGIDevice* dxgiDevice;
d3dDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
ID2D1Device* d2dDevice;
d2dFactory->CreateDevice(dxgiDevice, &d2dDevice);

// Swap chain creation
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 2;

IDXGIAdapter* dxgiAdapter;
dxgiDevice->GetAdapter(&dxgiAdapter);
IDXGIFactory2* dxgiFactory;
dxgiAdapter->GetParent(__uuidof(IDXGIFactory), reinterpret_cast<void **>(&dxgiFactory));

IDXGISwapChain1* swapChain;
dxgiFactory->CreateSwapChainForHwnd(d3dDevice, hwnd, &swapChainDesc, nullptr, nullptr, &swapChain);

// Direct2D device context creation
const auto options = D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
ID2D1DeviceContext* deviceContext;
d2dDevice->CreateDeviceContext(options, &deviceContext);

// create render target bitmap from swap chain
IDXGISurface* swapChainSurface;
swapChain->GetBuffer(0, __uuidof(swapChainSurface), reinterpret_cast<void **>(&swapChainSurface));
D2D1_BITMAP_PROPERTIES1 bitmapProperties;
bitmapProperties.dpiX = 0.0f;
bitmapProperties.dpiY = 0.0f;
bitmapProperties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
bitmapProperties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
bitmapProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
bitmapProperties.colorContext = nullptr;
ID2D1Bitmap1* swapChainBitmap = nullptr;
deviceContext->CreateBitmapFromDxgiSurface(swapChainSurface, &bitmapProperties, &swapChainBitmap);


// set swap chain bitmap as render target of D2D device context
deviceContext->SetTarget(swapChainBitmap);

D2D单通道位图创建:

const D2D1_SIZE_U size = { 512, 512 };
const UINT32 pitch = 512;
D2D1_BITMAP_PROPERTIES1 d2dProperties;
ZeroMemory(&d2dProperties, sizeof(D2D1_BITMAP_PROPERTIES1));
d2dProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
d2dProperties.pixelFormat.format = DXGI_FORMAT_R8_UNORM;
char* sourceData = new char[512*512];

ID2D1Bitmap1* d2dBitmap;
deviceContext->DeviceContextPointer->CreateBitmap(size, sourceData, pitch, d2dProperties, &d2dBitmap);

位图绘制(失败):
deviceContext->BeginDraw();
D2D1_COLOR_F d2dColor = {};
deviceContext->Clear(d2dColor);

// THIS LINE FAILS WITH THE DEBUG BREAKPOINT IF SINGLE CHANNELED
deviceContext->DrawBitmap(bitmap, nullptr, 1.0f, D2D1_INTERPOLATION_MODE_LINEAR, nullptr);  

swapChain->Present(1, 0);
deviceContext->EndDraw();

有没有人曾经需要绘制灰度位图?我真的不知道该如何处理这个问题,如果有任何提示,我将非常感激 :)! - MisterX
1个回答

1

根据我的经验,Direct2D似乎非常有限。

你尝试过Direct2D 效果 (ID2D1Effect)吗?你可以编写自己的效果[似乎相对复杂],或者使用其中一个内置效果[相对简单]。

有一个叫做颜色矩阵效果 (CLSID_D2D1ColorMatrix)的特效。它可能适用于将你的DXGI_FORMAT_R8_UNORM(或DXGI_FORMAT_A8_UNORM,任何单通道都可以)作为输入(特效的输入是ID2D1Image,而ID2D1Bitmap继承自ID2D1Image)。然后设置D2D1_COLORMATRIX_PROP_COLOR_MATRIX以将输入通道复制到所有输出通道。不过我还没有尝试过。


嘿,谢谢你的回答。我需要非常高效地使用内存,所以我不想使用额外的通道,也不想将单通道图像转换为具有相同值的RGB(A)图像。我真的需要能够创建单通道位图,否则Direct2D就无法使用了...或者我需要混合使用D3D和D2D,并使用前者来处理位图?! - MisterX
@MisterX,您提供的supported pixel formats链接似乎表明DXGI_FORMAT_A8_UNORM是您唯一的选择。由于我对效果的经验太少,所以无法确认,但也许效果的输出并没有实现(即直接呈现到渲染目标),或者仅在短暂时间内实现。我并不是说要保留输出,而是建议在渲染时仅链式使用效果。我预计您的渲染目标已经是RGB(A)格式。您需要检查这种方法是否适用于您... - Pablo H
@MisterX 另一方面,使用Direct3D 11.x直接绘制位图一旦您克服了行政难题就很容易。如果您不需要Direct2D的主要功能(文本、层、简单的渲染模型而无需着色器),那么请务必选择D3D路线。对于医疗应用程序来说,您可以使用更高精度的图像,轻松控制传输函数,使用计算着色器等等,这是一个额外的好处。 - Pablo H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接