通常的3D程序都只有一個視圖,對應整個窗口的客戶區。多視圖就是在一個窗口中放置多個視圖,以便從不一樣的角度觀察模型或者場景。不少圖形軟件都有這個功能,好比你們熟知的3DMax就有四個視圖,分別是前視圖,左視圖,頂視圖和透視圖。還有一些遊戲引擎也有相似的Demo,好比irrlicht引擎中的SplitScreen就是用多視圖實現的,以下圖。函數
用DirectX實現多視圖有幾種方法,可使用多個Viewport,也可使用多個Swap Chain,後者實現起來比較複雜,之後再作介紹,先看如何使用多個viewport來實現。那麼到底什麼是viewport呢?舉個現實中的例子,假設你站在一個密封的房子裏,這個房子只有一個很小的窗口,你如今就站在窗口前面,經過這個窗口你能夠觀察到外面的世界,那麼這個窗口就至關於一個視口,而外面的世界就是3D中的場景。視口有如下幾個屬性,長度和寬度,爲了肯定窗口的位置,咱們還須要一個左上角座標。爲了支持Z-Buffer,還須要兩個深度值,分別是zMin, zMax,表示最小深度和最大深度。好了,這就是視口的定義。在D3D中,視口用下面的結構體來表示,X和Y表示視口的左上角座標,Width和Height表示窗口的寬度和高度,MinZ和MaxZ表示Z-buffer的最小值和最大值。spa
typedef struct D3DVIEWPORT9 {
DWORD X;
DWORD Y;
DWORD Width;
DWORD Height;
float MinZ;
float MaxZ;
} D3DVIEWPORT9, *LPD3DVIEWPORT9;
實現多個視口渲染須要如下幾個步驟。3d
WNDCLASSEX winClass ;
winClass.lpszClassName = "MultiViewports";
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = MsgProc;
winClass.hInstance = hInstance;
winClass.hIcon = NULL ;
winClass.hIconSm = NULL ;
winClass.hCursor = LoadCursor(NULL, IDC_ARROW) ;
winClass.hbrBackground = NULL ;
winClass.lpszMenuName = NULL ;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
RegisterClassEx (&winClass) ;
HWND hWnd = CreateWindowEx(NULL,
winClass.lpszClassName, // window class name
"MultiViewports", // window caption
WS_OVERLAPPEDWINDOW, // window style
32, // initial x position
32, // initial y position
600, // initial window width
600, // initial window height
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
// Create window failed
if(hWnd == NULL)
{
MessageBoxA(hWnd, "Create Window failed!", "Error", 0) ;
return -1 ;
}
首先經過GetClientRect函數得到窗口的尺寸,而後將其橫豎一分爲二,這樣整個窗口被分割爲四部分,分別對應四個視口區域。以下圖。這一步並無實際的代碼對應,而是在建立視口的時候完成的。若是窗口的左上角座標是(x, y), 長寬分別是width和height,那麼對應的四個視口分別是code
視口定義好之後,使用SetViewport函數進行設置,而後就能夠繪製視口對應的場景了。因爲一共須要設置四個視口,爲了不代碼重複,這裏設置一個Draw函數,用來繪製每一個視口中的場景,該函數有兩個參數,第一個是待繪製的視口,第二個是視口的背景顏色。每設置一個視口,就調用這個函數一次。blog
void Draw(D3DVIEWPORT9* viewport, DWORD color)
{
g_pd3dDevice->SetViewport(viewport) ;
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, color, 1.0f, 0 );
// Begin the scene
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// Draw teapot
g_pTeapotMesh->DrawSubset(0) ;
// End the scene
g_pd3dDevice->EndScene();
}
}
在Draw函數內部,使用D3D函數SetViewport來設置viewport,設置完成之後要當即繪製該視口對應的場景,假若一次性設置四個視口,而後在繪製每一個視口對應的場景,那麼後面的場景就會覆蓋前面的,沒法達到預期效果,因此正確的順序是遊戲
設置視口1it
繪製視口1的場景io
設置視口2class
繪製視口2的場景軟件
設置視口3
繪製視口3的場景
設置視口4
繪製視口4的場景
而下面這樣則是不對的
設置視口1
設置視口2
設置視口3
設置視口4
繪製視口1的場景
繪製視口2的場景
繪製視口3的場景
繪製視口4的場景
注意Present函數每一個frame調用一次便可,而不是每次設置viewport都調用,那樣的話屏幕會閃爍。爲了分別從不一樣角度觀察模型,能夠爲每一個視口單獨設置camera,分別對應前視圖,左視圖,頂視圖及透視圖。
1. 設置前視圖
2. 設置左視圖
3. 設置頂視圖
4. 設置透視圖
渲染
好了,看一下效果圖