我是DirectX 10编程的新手,我一直尝试用我有限的技能做以下事情(虽然我有很强的OpenGL背景)
我正在尝试显示2个不同的纹理四边形,每个显示器1个。为此,我明白我需要一个D3D10设备,多个(2)交换链和单个VertexBuffer
虽然我认为我能够创造所有这些,但我仍然不确定如何处理所有这些。我需要多个ID3D10RenderTargetView吗?我应该如何以及在哪里使用OMSetRenderTargets(...)?
除了MSDN之外,这些概念的文档或解释相当有限,因此非常欢迎任何帮助。这是我的一些代码:
这是渲染代码
for(int i = 0; i < screenNumber; i++){
//clear scene
pD3DDevice->ClearRenderTargetView( pRenderTargetView, D3DXCOLOR(0,1,0,0) );
//fill vertex buffer with vertices
UINT numVertices = 4;
vertex* v = NULL;
//lock vertex buffer for CPU use
pVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**) &v );
v[0] = vertex( D3DXVECTOR3(-1,-1,0), D3DXVECTOR4(1,0,0,1), D3DXVECTOR2(0.0f, 1.0f) );
v[1] = vertex( D3DXVECTOR3(-1,1,0), D3DXVECTOR4(0,1,0,1), D3DXVECTOR2(0.0f, 0.0f) );
v[2] = vertex( D3DXVECTOR3(1,-1,0), D3DXVECTOR4(0,0,1,1), D3DXVECTOR2(1.0f, 1.0f) );
v[3] = vertex( D3DXVECTOR3(1,1,0), D3DXVECTOR4(1,1,0,1), D3DXVECTOR2(1.0f, 0.0f) );
pVertexBuffer->Unmap();
// Set primitive topology
pD3DDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
//set texture
pTextureSR->SetResource( textureSRV[textureIndex] );
//get technique desc
D3D10_TECHNIQUE_DESC techDesc;
pBasicTechnique->GetDesc( &techDesc );
// This is where you actually use the shader code
for( UINT p = 0; p < techDesc.Passes; ++p )
{
//apply technique
pBasicTechnique->GetPassByIndex( p )->Apply( 0 );
//draw
pD3DDevice->Draw( numVertices, 0 );
}
//flip buffers
pSwapChain[i]->Present(0,0);
}
这是创建渲染目标的代码,我不确定是不是很好
for(int i = 0; i < screenNumber; ++i){
//try to get the back buffer
ID3D10Texture2D* pBackBuffer;
if ( FAILED( pSwapChain[1]->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*) &pBackBuffer) ) ) return fatalError("Could not get back buffer");
//try to create render target view
if ( FAILED( pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView) ) ) return fatalError("Could not create render target view");
pBackBuffer->Release();
pD3DDevice->OMSetRenderTargets(1, &pRenderTargetView, NULL);
}
return true;
}
答案 0 :(得分:30)
我希望我明白你想做什么 - 在两个不同的显示器上呈现不同的内容,同时使用单个图形卡(图形适配器)将其输出映射到这些显示器。为此,您将需要一个设备(用于单个图形卡/适配器)并枚举用户机器上的输出数量。
因此,总的来说 - 这意味着一个设备,两个输出,两个窗口,因此 - 两个交换链。
这是我的小实验的快速结果:
使用DirectX 10+,这属于DXGI(DirectX图形基础设施),它管理与DirectX 10+开发相关的常见低级物流,正如您可能知道的那样,抛弃了枚举功能集等的旧要求。 - 要求每张支持DX10 +的卡分享API定义的所有功能。唯一不同的是卡的范围和能力(换句话说,糟糕的性能比应用程序崩溃和刻录更好)。这一切都在DirectX 9中,但微软的人们决定将其推出并称之为DXGI。现在,我们可以使用DXGI功能来设置我们的多监视器环境。
我是否需要多个ID3D10RenderTargetView?
是的,您确实需要多个渲染目标视图,计数取决于您拥有的监视器数量(如交换链和窗口)。但是,为了避免出现呕吐词,让我们尽可能简单地写出来并在需要的地方写下其他信息:
现在,虽然字数很丰富,但有些代码值得更多。这旨在让您大致了解实现简单多监视器应用程序的内容。因此,假设只有一个适配器(现在是一个相当大胆的声明)和多个输出 - 并且没有故障。我会把有趣的部分留给你。 回答第二个问题是在楼下......
请注意,不涉及内存管理。我们假设在出于说明目的不需要时,所有内容都会被神奇地清理干净。做个好记忆的公民。
获取适配器
IDXGIAdapter* adapter = NULL;
void GetAdapter() // applicable for multiple ones with little effort
{
// remember, we assume there's only one adapter (example purposes)
for( int i = 0; DXGI_ERROR_NOT_FOUND != factory->EnumAdapters( i, &adapter ); ++i )
{
// get the description of the adapter, assuming no failure
DXGI_ADAPTER_DESC adapterDesc;
HRESULT hr = adapter->GetDesc( &adapterDesc );
// Getting the outputs active on our adapter
EnumOutputsOnAdapter();
}
获取适配器上的输出
std::vector<IDXGIOutput*> outputArray; // contains outputs per adapter
void EnumOutputsOnAdapter()
{
IDXGIOutput* output = NULL;
for(int i = 0; DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(i, &output); ++i)
{
// get the description
DXGI_OUTPUT_DESC outputDesc;
HRESULT hr = output->GetDesc( &outputDesc );
outputArray.push_back( output );
}
}
现在,我必须假设您至少了解Win32 API注意事项,创建窗口类,注册系统,创建窗口等等......因此,我不会限定其创建,只有精心制作它是如何与多个窗口相关的。此外,我将仅考虑全屏情况,但在窗口模式下创建它是可能的,而且相当简单。
为我们的输出创建实际窗口
由于我们假设只存在一个适配器,我们只考虑链接到该特定适配器的枚举输出。最好以简洁的小结构组织所有窗口数据,但是为了这个答案的目的,我们只是把它们推到一个简单的结构中,然后再推入另一个std :: vector对象,我的意思是处理各个窗口(HWND)及其大小(虽然对于我们的情况它是常量)。
但是,我们必须解决这样一个事实:我们有一个交换链,一个渲染目标视图,每个窗口一个深度/模板视图。那么,为什么不在描述我们每个窗口的那个小结构中提供所有这些呢?有道理,对吧?
struct WindowDataContainer
{
//Direct3D 10 stuff per window data
IDXGISwapChain* swapChain;
ID3D10RenderTargetView* renderTargetView;
ID3D10DepthStencilView* depthStencilView;
// window goodies
HWND hWnd;
int width;
int height;
}
尼斯。好吧,不是真的。但仍然......继续前进!现在为输出创建窗口:
std::vector<WindowDataContainer*> windowsArray;
void CreateWindowsForOutputs()
{
for( int i = 0; i < outputArray.size(); ++i )
{
IDXGIOutput* output = outputArray.at(i);
DXGI_OUTPUT_DESC outputDesc;
p_Output->GetDesc( &outputDesc );
int x = outputDesc.DesktopCoordinates.left;
int y = outputDesc.DesktopCoordinates.top;
int width = outputDesc.DesktopCoordinates.right - x;
int height = outputDesc.DesktopCoordinates.bottom - y;
// Don't forget to clean this up. And all D3D COM objects.
WindowDataContainer* window = new WindowDataContainer;
window->hWnd = CreateWindow( windowClassName,
windowName,
WS_POPUP,
x,
y,
width,
height,
NULL,
0,
instance,
NULL );
// show the window
ShowWindow( window->hWnd, SW_SHOWDEFAULT );
// set width and height
window->width = width;
window->height = height;
// shove it in the std::vector
windowsArray.push_back( window );
//if first window, associate it with DXGI so it can jump in
// when there is something of interest in the message queue
// think fullscreen mode switches etc. MSDN for more info.
if(i == 0)
factory->MakeWindowAssociation( window->hWnd, 0 );
}
}
可爱,现在已经完成了。由于我们只有一个适配器,因此只有一个设备可以随身携带,所以请照常创建。在我的例子中,它只是一个全局接口指针,可以在任何地方访问。我们不打算在这里找到年度代码,为什么不呢,呃?
创建交换链,视图和深度/模板2D纹理
现在,我们友好的交换链......您可能习惯于通过调用&#34;裸体&#34;来实际创建它们。功能D3D10CreateDeviceAndSwapChain(...)
,但如您所知,我们已经制作了我们的设备。我们只想要一个。和多个交换链。嗯,这是一个泡菜。幸运的是,我们的DXGIFactory界面在其生产线上有交换链,我们可以通过补充的朗姆酒桶免费获得。然后在交换链上为每个窗口创建一个:
void CreateSwapChainsAndViews()
{
for( int i = 0; i < windowsArray.size(); i++ )
{
WindowDataContainer* window = windowsArray.at(i);
// get the dxgi device
IDXGIDevice* DXGIDevice = NULL;
device->QueryInterface( IID_IDXGIDevice, ( void** )&DXGIDevice ); // COM stuff, hopefully you are familiar
// create a swap chain
DXGI_SWAP_CHAIN_DESC swapChainDesc;
// fill it in
HRESULT hr = factory->CreateSwapChain( DXGIDevice, &swapChainDesc, &p_Window->swapChain );
DXGIDevice->Release();
DXGIDevice = NULL;
// get the backbuffer
ID3D10Texture2D* backBuffer = NULL;
hr = window->swapChain->GetBuffer( 0, IID_ID3D10Texture2D, ( void** )&backBuffer );
// get the backbuffer desc
D3D10_TEXTURE2D_DESC backBufferDesc;
backBuffer->GetDesc( &backBufferDesc );
// create the render target view
D3D10_RENDER_TARGET_VIEW_DESC RTVDesc;
// fill it in
device->CreateRenderTargetView( backBuffer, &RTVDesc, &window->renderTargetView );
backBuffer->Release();
backBuffer = NULL;
// Create depth stencil texture
ID3D10Texture2D* depthStencil = NULL;
D3D10_TEXTURE2D_DESC descDepth;
// fill it in
device->CreateTexture2D( &descDepth, NULL, &depthStencil );
// Create the depth stencil view
D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
// fill it in
device->CreateDepthStencilView( depthStencil, &descDSV, &window->depthStencilView );
}
}
我们现在拥有所需的一切。您需要做的就是定义一个迭代所有窗口的函数,并适当地绘制不同的东西。
我应该如何以及在哪里使用OMSetRenderTargets(...)?
在刚才提到的函数中,迭代所有窗口并使用适当的渲染目标(由我们的每个窗口数据容器提供):
void MultiRender( )
{
// Clear them all
for( int i = 0; i < windowsArray.size(); i++ )
{
WindowDataContainer* window = windowsArray.at(i);
// There is the answer to your second question:
device->OMSetRenderTargets( 1, &window->renderTargetView, window->depthStencilView );
// Don't forget to adjust the viewport, in fullscreen it's not important...
D3D10_VIEWPORT Viewport;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
Viewport.Width = window->width;
Viewport.Height = window->height;
Viewport.MinDepth = 0.0f;
Viewport.MaxDepth = 1.0f;
device->RSSetViewports( 1, &Viewport );
// TO DO: AMAZING STUFF PER WINDOW
}
}
当然,不要忘记每个窗口运行所有交换链和交换缓冲区。这里的代码只是为了这个答案的目的,它需要更多的工作,错误检查(failsafes)和沉思让它按照你喜欢的方式工作 - 换句话说 - 它应该给你一个简化的概述,而不是生产解决方案。
祝你好运,编码愉快! Sheesh,这是巨大的。