24. 幕外渲染

  通常在图形编程中,需要能够将场景渲染到纹理上,并且针对某些目的可以使用这些纹理,例如,也许想在游戏中有一个安全监视器,游戏中间有一个电视屏可以在不同的位置显示游戏的状态。这对游戏玩家而言,非常有用,并且可以在很大程度上提高程序的真实感。

 同样,也可以使用幕外渲染技术创建映射、阴影映射纹理和大量如场地模糊、夜视、热视力、高精度动态范围渲染等许多不同的后处理效果。在Direct3D中实现幕外渲染需要创建幕外表面,并对其渲染而不是渲染到常用的后台缓存中。然后将渲染结果存储在贴在幕外表面上的纹理图像中。并且可以像所有常用的纹理图像一样使用。要增加幕外渲染功能,只要创建一个LPDIRECT3DSURFACE9对象,并在想要渲染该对象时,切换到该对象即可。Direct3D在内部可以处理这一切工作,所以尤其是和OpenGL中的幕外渲染相比,这里的工作量要少很多。

 为了创建幕外表面,就必须创建一个想要保存渲染结果的LPDIRECT3DTEXTURE9对象。这通过调用D3DXCreateTexture()函数就可以实现。该函数的参数有pDevice、纹理的宽度(Width)和高度(Height)、纹理中Mipmap的个数(Miplevels)、指定纹理使用方式的标识符(Usage)、图像格式(Format)、描述放置图像的内存类(Pool),以及正在创建的纹理对象(ppTexture)。

 有了创建好的纹理,就可以调用LPDIRECT3DTEXTURE9的成员函数GetSurfaceLevel()创建幕外表面。GetSurfaceLevel()函数是一个可以从纹理对象中获取表面对象的函数。该表面就是要渲染的表面,而纹理对象将场景存储为一幅图像,而不是将其显示在屏幕上。GetSurfaceLevel()函数原型如程序清单4.23所示。其中,Level是创建表面时的纹理资源级别。ppSurfaceLevel是正在创建的LPDIRECT3DSURFACE9对象。

       程序清单4.23 GetSurfaceLevel()函数原型

HRESULT GetSurfaceLevel(
UINT Level,
// 创建表面时的纹理资源级别
IDirect3DSurface9** ppSurfaceLevel // 正在创建的LPDIRECT3DSURFACE9对象
);

    有了这两个函数,就需要一个渲染用的幕外表面和纹理。要牢记:为了避免内存泄漏,在使用完对象后,一定要释放它们。到目前为止,这对所创建和使用的每个Direct3D对象都是如此。

 当要渲染表面时,有两件事要做。首先,需要一份后台缓存表面的副本,这样在处理完幕外表面时,可以返回正常的渲染。这通过使用Direct3D设备函数GetBackBuffer()就可以实现。GetBackBuffer()函数原型如程序清单4.24所示,其中iSwapChain是一个无符号整数,用于指定正在使用的交换链索引,BackBuffer是后台缓存索引,Type在Direct3D9.0中只能是D3DBACKBUFFER_TYPE_MODO,ppBackBuffer是保存后台缓存的LPDIRECT3DSURFACE9对象。

       程序清单4.24 GetBackBuffer()函数原型

HRESULT GetBackBuffer(
UINT iSwapChain,
// 指定正在使用的交换链索引
UINT BackBuffer, // 后台缓存索引
D3DBACKBUFFER_TYPE Type,
IDirect3DSurface9
** ppBackBuffer // 保存后台缓存的LPDIRECT3DSURFACE9对象
);

     其次,当有了后台缓存后,就可以转换到想要用的所有渲染目标,这只需调用Direct3D设备函数SetRenderTarget()即可。SetRenderTarget()函数原型如程序清单4.25所示,其中RenderTargetIndex是渲染目标的索引,pRenderTarget代表要绘制的LPDIRECT3DSURFACE9对象。

HRESULT SetRenderTarget(

  DWORD RenderTargetIndex,            // 渲染目标的索引

  IDirect3DSurface9 * pRenderTarget   // 要绘制的LPDIRECT3DSURFACE9对象

);

 至此,就得到该演示程序的全部内容。有了这4个函数,就包含了所要渲染的全部内容。既然已经了解到这一点,接下来就可以开发一个程序,实现幕外渲染功能。

#include<d3d9.h>
#include
<d3dx9.h>

#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Off-Screen Rendering"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480

// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();


// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice
= NULL;

// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_ViewMatrix;

// stD3DVertex buffer to hold the square's geometry.
LPDIRECT3DVERTEXBUFFER9 g_VertexBuffer = NULL;

// g_Teapot object and its material.
LPD3DXMESH g_Teapot = NULL;
D3DMATERIAL9 g_Material;
float g_RotationAngle = 0.0f;

// Light object.
D3DLIGHT9 g_Light;

// Back buffer, offscreen texture, offscreen surface.
LPDIRECT3DSURFACE9 g_BackSurface = NULL;
LPDIRECT3DTEXTURE9 g_SurfaceTexture
= NULL;
LPDIRECT3DSURFACE9 g_OffScreenSurface
= NULL;


// A structure for our custom vertex type
struct stD3DVertex
{
float x, y, z;
float tu, tv;
};

// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)


LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(
0);
return 0;
break;

case WM_KEYUP:
if(wParam == VK_ESCAPE) PostQuitMessage(0);
break;
}

return DefWindowProc(hWnd, msg, wParam, lParam);
}


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(
&wc);

// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);

// Initialize Direct3D
if(InitializeD3D(hWnd, false))
{
// 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
RenderScene();
}
}

// Release any and all resources.
Shutdown();

// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}


bool InitializeD3D(HWND hWnd, bool fullscreen)
{
D3DDISPLAYMODE displayMode;

// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;

// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;

// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(
&d3dpp, sizeof(d3dpp));

if(fullscreen)
{
d3dpp.Windowed
= FALSE;
d3dpp.BackBufferWidth
= WINDOW_WIDTH;
d3dpp.BackBufferHeight
= WINDOW_HEIGHT;
}
else
d3dpp.Windowed
= TRUE;
d3dpp.SwapEffect
= D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat
= displayMode.Format;
d3dpp.BackBufferCount
= 1;
d3dpp.EnableAutoDepthStencil
= TRUE;
d3dpp.AutoDepthStencilFormat
= D3DFMT_D16;

// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_D3DDevice)))
{
return false;
}

// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;

return true;
}


bool InitializeObjects()
{
// Setup the g_Light source.
g_Light.Type = D3DLIGHT_DIRECTIONAL;
g_Light.Direction
= D3DXVECTOR3(0.0f, 0.0f, 1.0f);
g_Light.Diffuse.r
= 1;
g_Light.Diffuse.g
= 1;
g_Light.Diffuse.b
= 1;
g_Light.Diffuse.a
= 1;
g_Light.Specular.r
= 1;
g_Light.Specular.g
= 1;
g_Light.Specular.b
= 1;
g_Light.Specular.a
= 1;

g_D3DDevice
->SetLight(0, &g_Light);
g_D3DDevice
->LightEnable(0, TRUE);

// Setup the material properties for the teapot.
ZeroMemory(&g_Material, sizeof(D3DMATERIAL9));
g_Material.Diffuse.r
= g_Material.Ambient.r = 0.6f;
g_Material.Diffuse.g
= g_Material.Ambient.g = 0.6f;
g_Material.Diffuse.b
= g_Material.Ambient.b = 0.7f;
g_Material.Specular.r
= 0.4f;
g_Material.Specular.g
= 0.4f;
g_Material.Specular.b
= 0.4f;
g_Material.Power
= 8.0f;

// Create the teapot.
if(FAILED(D3DXCreateTeapot(g_D3DDevice, &g_Teapot, NULL)))
return false;


// Create the texture that will be rendered to.
if(FAILED(D3DXCreateTexture(g_D3DDevice, WINDOW_WIDTH,
WINDOW_HEIGHT,
1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT,
&g_SurfaceTexture))) return false;

// Set texture to offscreen surface. 将纹理与幕外表面关联起来
g_SurfaceTexture->GetSurfaceLevel(0, &g_OffScreenSurface);


// Square that will be textured with offscreen rendering data.
stD3DVertex square[] =
{
{
-1, 1, -1, 0, 0}, {1, 1, -1, 1, 0},
{
-1, -1, -1, 0, 1}, {1, -1, -1, 1, 1}
};

g_D3DDevice
->CreateVertexBuffer(4 * sizeof(stD3DVertex), 0,
D3DFVF_VERTEX, D3DPOOL_DEFAULT,
&g_VertexBuffer, NULL);

void *pData = NULL;
g_VertexBuffer
->Lock(0, sizeof(square), (void**)&pData, 0);
memcpy(pData, square,
sizeof(square));
g_VertexBuffer
->Unlock();


// Set the image states to get a good quality image.
g_D3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_D3DDevice
->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

// Set default rendering states.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
//让逆时针的三角形显示出来
g_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
//启用深度缓存
g_D3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);


// Set the projection matrix.
D3DXMatrixPerspectiveFovLH(&g_projection, 45.0f,
WINDOW_WIDTH
/WINDOW_HEIGHT, 0.1f, 1000.0f);
g_D3DDevice
->SetTransform(D3DTS_PROJECTION, &g_projection);


// Define camera information.
D3DXVECTOR3 cameraPos(0.0f, 0.0f, -4.0f);
D3DXVECTOR3 lookAtPos(
0.0f, 0.0f, 0.0f);
D3DXVECTOR3 upDir(
0.0f, 1.0f, 0.0f);

// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos,
&lookAtPos, &upDir);

return true;
}


void RenderScene()
{
// Get a copy of the back buffer.
g_D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &g_BackSurface);

// Prepare to draw to the offscreen surface.
g_D3DDevice->SetRenderTarget(0, g_OffScreenSurface);

// Clear the offscreen surface.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET/* | D3DCLEAR_ZBUFFER*/, D3DCOLOR_XRGB(200,200,200), 1.0f, 0);

// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();

// Turn on lighting.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

// Set projection.
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);

// Create rotation matrix to rotate teapot.
D3DXMATRIXA16 w;
D3DXMatrixRotationY(
&w, g_RotationAngle);
g_D3DDevice
->SetTransform(D3DTS_WORLD, &w);

// Add to the rotation.
g_RotationAngle += 0.02f;
if(g_RotationAngle >= 360) g_RotationAngle = 0.0f;

// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);

// Set the material and draw the Teapot.
g_D3DDevice->SetMaterial(&g_Material);
g_D3DDevice
->SetTexture(0, NULL);
g_Teapot
->DrawSubset(0);

// End the scene. Stop rendering.
g_D3DDevice->EndScene();


// Switch back to our back buffer.
g_D3DDevice->SetRenderTarget(0, g_BackSurface);

// Clear the back buffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(
0,0,0), 1.0f, 0);

// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();

// Turn off lighting. Don't need it.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

// Set projection.
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);

// Rotate just a little to see this is a flat surface.
D3DXMatrixRotationY(&w, 120);
g_D3DDevice
->SetTransform(D3DTS_WORLD, &w);

// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);

g_D3DDevice
->SetTexture(0, g_SurfaceTexture);
g_D3DDevice
->SetStreamSource(0, g_VertexBuffer, 0, sizeof(stD3DVertex));
g_D3DDevice
->SetFVF(D3DFVF_VERTEX);
g_D3DDevice
->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

// End the scene. Stop rendering.
g_D3DDevice->EndScene();

// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}


void Shutdown()
{
if(g_D3DDevice != NULL) g_D3DDevice->Release();
g_D3DDevice
= NULL;

if(g_D3D != NULL) g_D3D->Release();
g_D3D
= NULL;

if(g_VertexBuffer != NULL) g_VertexBuffer->Release();
g_VertexBuffer
= NULL;

if(g_Teapot != NULL) g_Teapot->Release();
g_Teapot
= NULL;

if(g_BackSurface != NULL) g_BackSurface->Release();
g_BackSurface
= NULL;

if(g_SurfaceTexture != NULL) g_SurfaceTexture->Release();
g_SurfaceTexture
= NULL;

if(g_OffScreenSurface != NULL) g_OffScreenSurface->Release();
g_OffScreenSurface
= NULL;
}

  

  

    




原文地址:https://www.cnblogs.com/kex1n/p/2156495.html