用DirectX實現多視圖渲染

什麼是多視圖

通常的3D程序都只有一個視圖,對應整個窗口的客戶區。多視圖就是在一個窗口中放置多個視圖,以便從不一樣的角度觀察模型或者場景。不少圖形軟件都有這個功能,好比你們熟知的3DMax就有四個視圖,分別是前視圖,左視圖,頂視圖和透視圖。還有一些遊戲引擎也有相似的Demo,好比irrlicht引擎中的SplitScreen就是用多視圖實現的,以下圖。函數

什麼是視口(viewport)?

用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

viewport1 = {0, 0, width / 2, height / 2, 0.0f, 1.0f} ;
viewport2 = {width / 2, 0, width / 2, height / 2, 0.0f, 1.0f} ;
viewport3 = {0, height / 2, width / 2, height / 2, 0.0f, 1.0f} ;
viewport4 = {width / 2, height / 2, width / 2, height / 2, 0.0f, 1.0f} ;

設置視口並渲染

視口定義好之後,使用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. 設置前視圖

複製代碼
複製代碼
// Setup camera, front view
D3DXVECTOR3 eyePt(0.0f, 0.0f, -5.0f) ;
D3DXVECTOR3 lookAt(0.0f, 0.0f, 0.0f) ;
D3DXVECTOR3 upVec(0.0f, 1.0f, 0.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;
// Draw top-left viewport
D3DVIEWPORT9 viewport1 = {0, 0, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport1, 0xffff0000) ;
複製代碼
複製代碼

2. 設置左視圖

複製代碼
複製代碼
// Setup camera, left view
eyePt = D3DXVECTOR3(-5.0f, 0.0f, 0.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;

// Draw top-right viewport
D3DVIEWPORT9 viewport2 = {vpWidth, 0, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport2, 0xff00ff00) ;
複製代碼
複製代碼

3. 設置頂視圖

複製代碼
複製代碼
// Setup camera, top view
eyePt = D3DXVECTOR3(0.0f, 5.0f, 0.0f) ;
upVec = D3DXVECTOR3(0.0f, 0.0f, 1.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;

// Draw bottom-left viewport
D3DVIEWPORT9 viewport3 = {0, vpHeight, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport3, 0xff0000ff) ;
複製代碼
複製代碼

4. 設置透視圖

複製代碼
複製代碼
// Setup camera, perspective view
eyePt = D3DXVECTOR3(-3.0f, 3.0f, -3.0f) ;
upVec = D3DXVECTOR3(1.0f, 2.0f, 1.0f) ;
SetupCamera(&eyePt, &lookAt, &upVec) ;

// Draw bottom-right viewport
D3DVIEWPORT9 viewport4 = {vpWidth, vpHeight, vpWidth, vpHeight, 0.0f, 1.0f} ;
Draw(&viewport4, 0xffffff00) ;
複製代碼
複製代碼

渲染

// Present the back-buffer contents to the display
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

好了,看一下效果圖

相關文章
相關標籤/搜索