在使用Direct3D设置投影矩阵时经常用到下面两个函数:D3DXMatrixPerspectiveFovLH()和D3DXMatrixPerspectiveOffCenterLH(),但是这两个函数究竟有什么区别?他们之间如何转换?这两个问题也是困扰我好久,最近整理了一下网上搜的的资料,下面是本人对这两个函数的理解:D3DXMatrixPerspectiveFovLH()函数是设置整个视椎体,而D3DXMatrixPerspectiveOffCenterLH()函数可以自定义视椎体大小,并且它的参数是在相机空间中度量的,这个函数设置的是视椎体近平面的值。这样说可能很难理解,哈哈。举个例子,这个例子是从网上找到的,参考地址:http://www.cppblog.com/topjackhjj/articles/88219.html
近日因公司项目需要,写了个屏幕分割渲染的功能。
如下图所示:假设我们有一个场景,场景里面大多数是静态对象(对应图中黑色点),而只有小量的动态对象并且相对集中(对应图中红色点),那么我们在渲染的时候,只要一开始将整个屏幕渲染一次,在后面只更新动态对象所在的区域就可以了。
在测试例子中,我们将整个屏幕分成左右2块,各自分别对应一个投影矩阵和一个视口,他们共用一个视图矩阵和世界矩阵。在如何决定子屏幕所使用的投影矩阵时,主要采用 D3DXMatrixPerspectiveOffCenterLH()或 D3DXMatrixPerspectiveOffCenterRH()这2个函数。具体描述如下:
D3DXMatrixPerspectiveOffCenterLH()
Builds a customized, left-handed perspective projection matrix.
D3DXMATRIX * D3DXMatrixPerspectiveOffCenterLH(
D3DXMATRIX * pOut,
FLOAT l,
FLOAT r,
FLOAT b,
FLOAT t,
FLOAT zn,
FLOAT zf);
Parameters
pOut
[in, out] Pointer to the D3DXMATRIX structure that is the result of the operation.
l
[in] Minimum x-value of the view volume.
r
[in] Maximum x-value of the view volume.
b
[in] Minimum y-value of the view volume.
t
[in] Maximum y-value of the view volume.
zn
[in] Minimum z-value of the view volume.
zf
[in] Maximum z-value of the view volume.
Return Values
Pointer to a D3DXMATRIX structure that is a customized, left-handed perspective projection matrix.
Remarks
All the parameters of the D3DXMatrixPerspectiveOffCenterLH function are distances in camera space. The parameters describe the dimensions of the view volume.
The return value for this function is the same value returned in the pOut parameter. In this way, the D3DXMatrixPerspectiveOffCenterLH function can be used as a parameter for another function.
This function uses the following formula to compute the returned matrix.
2*zn/(r-l) 0 0 0
0 2*zn/(t-b) 0 0
(l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1
0 0 zn*zf/(zn-zf) 0
D3DXMatrixPerspectiveOffCenterRH()
Builds a customized, right-handed perspective projection matrix.
D3DXMATRIX * D3DXMatrixPerspectiveOffCenterRH(
D3DXMATRIX * pOut,
FLOAT l,
FLOAT r,
FLOAT b,
FLOAT t,
FLOAT zn,
FLOAT zf);
Parameters
pOut
[in, out] Pointer to the D3DXMATRIX structure that is the result of the operation.
l
[in] Minimum x-value of the view volume.
r
[in] Maximum x-value of the view volume.
b
[in] Minimum y-value of the view volume.
t
[in] Maximum y-value of the view volume.
zn
[in] Minimum z-value of the view volume.
zf
[in] Maximum z-value of the view volume.
Return Values
Pointer to a D3DXMATRIX structure that is a customized, right-handed perspective projection matrix.
Remarks
All the parameters of the D3DXMatrixPerspectiveOffCenterRH function are distances in camera space. The parameters describe the dimensions of the view volume.
The return value for this function is the same value returned in the pOut parameter. In this way, the D3DXMatrixPerspectiveOffCenterRH function can be used as a parameter for another function.
This function uses the following formula to compute the returned matrix.
2*zn/(r-l) 0 0 0
0 2*zn/(t-b) 0 0
(l+r)/(r-l) (t+b)/(t-b) zf/(zn-zf) -1
0 0 zn*zf/(zn-zf) 0
在上述情况下使用D3DXMatrixPerspectiveFovLH()函数是搞不定的,它无法手动指定视椎体大小。只能使用D3DXMatrixPerspectiveOffCenterLH()函数。通过这个例子我们还可以使用D3DXMatrixPerspectiveOffCenterLH()这个函数来做一些效率优化的东西,比如说某些场景无需渲染,可以通过设置视椎体直接卡掉。
代码如下(其中纹理文件可以自己替换):
////////////main.h//////////////////////////// #include <Windows.h> #include <mmsystem.h> #include <d3dx9.h> #include <time.h> #pragma warning( disable : 4996 ) // disable deprecated warning #include <strsafe.h> #pragma warning( default : 4996 ) #include <math.h> #pragma comment( lib, "d3d9.lib" ) #pragma comment( lib, "d3dx9.lib" ) #pragma comment( lib, "d3dxof.lib" ) #pragma comment( lib, "dxguid.lib" ) #pragma comment( lib, "winmm.lib" ) //----------------------------------------------------------------------------- // Global variables //----------------------------------------------------------------------------- LPDIRECT3D9 g_pD3D = NULL; // Used to create the D3DDevice LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Our rendering device LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold vertices LPDIRECT3DTEXTURE9 g_pTexture = NULL; // Our texture BOOL g_bDrawLeft = FALSE; BOOL g_bDrawRight = FALSE; FLOAT g_fAngle = 0.0f; const float C_PI = 3.1415926; // A structure for our custom vertex type. We added texture coordinates struct CUSTOMVERTEX { D3DXVECTOR3 position; // The position D3DCOLOR color; // The color FLOAT tu, tv; // The texture coordinates }; // Our custom FVF, which describes our custom vertex structure #ifdef SHOW_HOW_TO_USE_TCI #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE) #else #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) #endif HRESULT InitD3D( HWND hWnd ); HRESULT InitGeometry(); VOID Cleanup(); VOID SetupMatrices(); VOID DrawScene(); VOID Render(); LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT ); /////////////////////////////////main.cpp////////////////////////////// #include "main.h" //----------------------------------------------------------------------------- // Name: InitD3D() // Desc: Initializes Direct3D //----------------------------------------------------------------------------- HRESULT InitD3D( HWND hWnd ) { // Create the D3D object. if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) return E_FAIL; // Set up the structure used to create the D3DDevice. Since we are now // using more complex geometry, we will create a device with a zbuffer. D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp) ); d3dpp.Windowed = TRUE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; d3dpp.EnableAutoDepthStencil = TRUE; d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // Create the D3DDevice if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice ) ) ) { return E_FAIL; } // Turn off culling g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); // Turn off D3D lighting g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); // Turn on the zbuffer g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); return S_OK; } //----------------------------------------------------------------------------- // Name: InitGeometry() // Desc: Create the textures and vertex buffers //----------------------------------------------------------------------------- HRESULT InitGeometry() { // Use D3DX to create a texture from a file based image if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "soil_wall.jpg", &g_pTexture ) ) ) { MessageBox(NULL, "Could not find banana.bmp", "Textures.exe", MB_OK); return E_FAIL; } // Create the vertex buffer. if( FAILED( g_pd3dDevice->CreateVertexBuffer( 4*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL ) ) ) { return E_FAIL; } // Fill the vertex buffer. We are setting the tu and tv texture // coordinates, which range from 0.0 to 1.0 CUSTOMVERTEX* pVertices = NULL; if( FAILED( g_pVB->Lock( 0, 0, (void**)&pVertices, 0 ) ) ) return E_FAIL; pVertices[0].position = D3DXVECTOR3( -10.0f, 0.0f, 10.0f ); pVertices[0].color = 0xffffffff; pVertices[0].tu = 0.0f; pVertices[0].tv = 0.0f; pVertices[1].position = D3DXVECTOR3( 10.0f, 0.0f, 10.0f ); pVertices[1].color = 0xffffffff; pVertices[1].tu = 10.0f; pVertices[1].tv = 0.0f; pVertices[2].position = D3DXVECTOR3( -10.0f, 0.0f, -10.0f ); pVertices[2].color = 0xffffffff; pVertices[2].tu = 0.0f; pVertices[2].tv = 10.0f; pVertices[3].position = D3DXVECTOR3( 10.0f, 0.0f, -10.0f ); pVertices[3].color = 0xffffffff; pVertices[3].tu = 10.0f; pVertices[3].tv = 10.0f; g_pVB->Unlock(); return S_OK; } //----------------------------------------------------------------------------- // Name: Cleanup() // Desc: Releases all previously initialized objects //----------------------------------------------------------------------------- VOID Cleanup() { if( g_pTexture != NULL ) g_pTexture->Release(); if( g_pVB != NULL ) g_pVB->Release(); if( g_pd3dDevice != NULL ) g_pd3dDevice->Release(); if( g_pD3D != NULL ) g_pD3D->Release(); } //----------------------------------------------------------------------------- // Name: SetupMatrices() // Desc: Sets up the world, view, and projection transform matrices. //----------------------------------------------------------------------------- VOID SetupMatrices() { D3DXMATRIXA16 matWorld; D3DXMatrixIdentity( &matWorld ); g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); g_fAngle += C_PI / 180.0f; if ( g_fAngle >= 2*C_PI ) g_fAngle = 0.0f; D3DXVECTOR3 vEyePt( 5.0f*sin(g_fAngle), 10.0f*sin(g_fAngle), 5.0f*cos(g_fAngle) ); D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f ); D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f ); D3DXMATRIXA16 matView; D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec ); g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); } VOID DrawScene() { // Render the vertex buffer contents g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) ); g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ); g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); } //----------------------------------------------------------------------------- // Name: Render() // Desc: Draws the scene //----------------------------------------------------------------------------- VOID Render() { // Begin the scene if( SUCCEEDED( g_pd3dDevice->BeginScene() ) ) { g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 ); g_pd3dDevice->SetTexture( 0, g_pTexture ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); // Setup the world, view, matrices SetupMatrices(); D3DXMATRIXA16 matProj; D3DVIEWPORT9 vp; if( g_bDrawLeft ) { D3DXMatrixPerspectiveOffCenterLH( &matProj, -1, 0, -1, 1, 1.0f, 100.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); vp.X = 0; vp.Y = 0; vp.Width = 400; vp.Height = 600; vp.MinZ = 0.0f; vp.MaxZ = 1.0f; g_pd3dDevice->SetViewport( &vp ); // Clear the backbuffer and the zbuffer g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 ); DrawScene(); } if( g_bDrawRight ) { D3DXMatrixPerspectiveOffCenterLH( &matProj, 0, 1, -1, 1, 1.0f, 100.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); vp.X = 400; vp.Y = 0; vp.Width = 400; vp.Height = 600; vp.MinZ = 0.0f; vp.MaxZ = 1.0f; g_pd3dDevice->SetViewport( &vp ); // Clear the backbuffer and the zbuffer g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 ); DrawScene(); } // End the scene g_pd3dDevice->EndScene(); } // Present the backbuffer contents to the display g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); } //----------------------------------------------------------------------------- // Name: MsgProc() // Desc: The window's message handler //----------------------------------------------------------------------------- LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_DESTROY: Cleanup(); PostQuitMessage( 0 ); return 0; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) { Cleanup(); PostQuitMessage( 0 ); break; } else if( wParam == VK_LEFT ) { g_bDrawLeft = !g_bDrawLeft; } else if( wParam == VK_RIGHT ) { g_bDrawRight = !g_bDrawRight; } break; } return DefWindowProc( hWnd, msg, wParam, lParam ); } //----------------------------------------------------------------------------- // Name: WinMain() // Desc: The application's entry point //----------------------------------------------------------------------------- INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT ) { // Register the window class WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, "D3D Tutorial", NULL }; wc.hCursor = LoadCursorA( NULL, IDC_ARROW ); RegisterClassEx( &wc ); // Create the application's window HWND hWnd = CreateWindow( "D3D Tutorial", "窗口分屏渲染:左键-渲染左屏;右键-渲染右屏", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, NULL, NULL, wc.hInstance, NULL ); // Initialize Direct3D if( SUCCEEDED( InitD3D( hWnd ) ) ) { // Create the scene geometry if( SUCCEEDED( InitGeometry() ) ) { // Show the window ShowWindow( hWnd, SW_SHOWDEFAULT ); UpdateWindow( hWnd ); // Enter the message loop MSG msg; ZeroMemory( &msg, sizeof(msg) ); while( msg.message!=WM_QUIT ) { if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } else { Render(); } } } } UnregisterClass( "D3D Tutorial", wc.hInstance ); return 0; }
下面说一下这两个矩阵间的转换,只介绍如何将D3DXMatrixPerspectiveFovLH()参数转换成D3DXMatrixPerspectiveOffCenterLH()函数参数,逆过程类似。由上面两个矩阵可以得到如下式子:
xScale=2*zn/(r-l)
ysclae=2*zn/(t-b)
l+r=0
t+b=0
所以,我们可得到:
r=zn/xScale
l=-zn/xScale
t=zn/yScale
b=-zn/yScale