DirectX3D游戏开发四 Z-Buffer的实现

欢迎来到EasyLiu的博客!

参考directxtutorial.com DirectX.9.0.3D游戏开发编程基础

    在上一节课中我们讲了简单的流水线操作,重点讲了坐标变换,实现了一个三角形的自转。但是存在一个问题:虽然它完美的计算了三维顶点在屏幕上的位置,但是它不显示深度。这一节课我们来解决这个问题!

    假设我们要画两个三角形,一个三角形在另外一个三角形的后面,然后从一个(更远处的三角形有一部分在近处三角形后面,就是近处的三角形挡住了远处三角形的一部分)的角度观看。如果我们使用我们掌握的代码来编写程序,会得到如下的结果:


    很显然这个结果不是我们想要的,是违背自然规律的。因为远处的东西不应该出现在近处的东西的前面,特别的当近处的东西挡住远处的东西的时候,我们想要的结果应该是如下所示:


那我们怎样解决这个问题呢?

    当进行模型渲染的时候,Direct3D会做几个事情。首先,Direct3D会绘制流水线。这个流水线都是整齐的放在内存中的。Direct3D每次拾取这个流水线并且把每个模型转变为2D的图形。

    当第一幅图片已经被绘制到屏幕上面以后,下一个模型就被拾取,处理且绘制到后台缓存区。然后,无论这个模型被放在3D空间的哪个位置,第二幅图形总是显示在第一幅图形之上,就得到了之前第一幅图所显示的结果。

    幸运的是,Direct3D提供了一个解决方案。这个解决方案叫做Z-Buffer。

   什么是Z-Buffer

   Z-Buffer也叫作深度缓存,是一个用来存储屏幕上的每一个像素距离摄像头的距离的大的缓存,如下图所示:


    上图讲述了Z-Buffer是如何工作的。当一个像素需要被绘制的时候,Direct3D选择距离摄像头最近的像素并且把其绘制到后台缓存区。同时,把深度值存储到Z-Buffer中相应的位置上,当下一次需要绘制东西的时候,Direct3D就能够知道每个像素距离摄像头多近,从而决定哪个物体应该被绘制而哪个物体不应该被绘制。

程序实现Z-Buffer的步骤

  1、设定适当的显示参数:

   加粗的代码代表在之前的基础上需要添加的代码,设定深度缓存的格式为16bit以及自动运行z-buffer。

// Set up the structure used to create the D3DDevice
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth = SCREEN_WIDTH;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;<strong>
       d3dpp.EnableAutoDepthStencil = TRUE;    // automatically run the z-buffer for us
       d3dpp.AutoDepthStencilFormat = D3DFMT_D16;    // 16-bit pixel format for the z-buffer</strong>
    2、打开Z-Buffer

    g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting
    g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);    // both sides of the triangles<strong>
   g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer</strong>
    3、清除Z-Buffer

   每次绘制之前清除Z-Buffer,如下所示,加粗的代表需要添加的代码。

    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET<strong>|D3DCLEAR_ZBUFFER</strong>, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
    运行结果如下所示:

    

   下面是main.c源代码:

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib,"winmm.lib") 

#include <d3d9.h>
#include <d3dx9.h>
#include "Windows.h"
#include <mmsystem.h>
#pragma warning( disable : 4996 ) // disable deprecated warning 
#include <strsafe.h>
#pragma warning( default : 4996 )

// define the screen resolution
const  int  SCREEN_WIDTH = 800;
const  int  SCREEN_HEIGHT = 600;

LPDIRECT3D9             g_pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // Our rendering device
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold vertices

// A structure for our custom vertex type
struct CUSTOMVERTEX
{
	FLOAT x, y, z; 
	DWORD color;        // The vertex color
};

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

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D(HWND hWnd)
{
	// Create the D3D object.
	if (NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
		return E_FAIL;

	//fill D3DCAPS9 struct with the capabilities of the primary display adapter
	D3DCAPS9 caps;
	g_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
	//can we use hardware vertex processing?
	DWORD vp = 0;
	if (caps.DevCaps&D3DDEVCAPS_HWTRANSFORMANDLIGHT)
	{
		//yes,support hardware vertex processing
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	}
	else
	{
		//no,
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	}

	// Set up the structure used to create the D3DDevice
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth = SCREEN_WIDTH;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;
	d3dpp.EnableAutoDepthStencil = TRUE;    // automatically run the z-buffer for us
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;    // 16-bit pixel format for the z-buffer

	// Create the D3DDevice
	if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
		vp,
		&d3dpp, &g_pd3dDevice)))
	{
		return E_FAIL;
	}

	// Device state would normally be set here

	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting
    g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);    // both sides of the triangles
	g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer
	return S_OK;
}

HRESULT InitVB()
{
	// Initialize three vertices for rendering a triangle
	CUSTOMVERTEX vertices[] =
	{
		{ 3.0f, -3.0f, 0.0f, D3DCOLOR_XRGB(0, 255, 0), }, // x, y, z, color
		{ 0.0f, 3.0f, 0.0f, D3DCOLOR_XRGB(0, 255, 255), },
		{ -3.0f, -3.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
	};

	// Create the vertex buffer. Here we are allocating enough memory
	// (from the default pool) to hold all our 3 custom vertices. We also
	// specify the FVF, so the vertex buffer knows what data it contains.
	if (FAILED(g_pd3dDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX),
		0, D3DFVF_CUSTOMVERTEX,
		D3DPOOL_DEFAULT, &g_pVB, NULL)))
	{
		return E_FAIL;
	}

	// Now we fill the vertex buffer. To do this, we need to Lock() the VB to
	// gain access to the vertices. This mechanism is required becuase vertex
	// buffers may be in device memory.
	VOID* pVertices;
	if (FAILED(g_pVB->Lock(0, sizeof(vertices), (void**)&pVertices, 0)))
		return E_FAIL;
	memcpy(pVertices, vertices, sizeof(vertices));
	g_pVB->Unlock();


	return S_OK;
}
VOID SetupMatrices()
{
	
	D3DXVECTOR3 vEyePt(0.0f, 0.0f, -20.0f);//摄像头位置,决定了场景中物体的大小
	D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);//摄像头朝向
	D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);//垂直方向为y轴,这个一般是y轴
	D3DXMATRIXA16 matView;
	D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
	g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);


	D3DXMATRIXA16 matProj;
	D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, 1.0f, 100.0f);
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}

VOID Render()
{
	// Clear the backbuffer to a blue color
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);

	// Begin the scene
	if (SUCCEEDED(g_pd3dDevice->BeginScene()))
	{

		SetupMatrices();//设置转换矩阵
		g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
		g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);	//设置顶点格式
	
		D3DXMATRIX matTranslateA;    // a matrix to store the translation for triangle A
		D3DXMATRIX matTranslateB;   // a matrix to store the translation for triangle B
		D3DXMATRIX matRotateY;      // a matrix to store the rotation for each triangle
		UINT iTime = timeGetTime() % 2000;//获取当前时间
		FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 2000.0f;//1000ms旋转360度

		D3DXMatrixRotationY(&matRotateY, fAngle);    // the front side
		// build MULTIPLE matrices to translate the model and one to rotate
		D3DXMatrixTranslation(&matTranslateA, 0.0f, 0.0f, 2.0f);//往里面平移
		D3DXMatrixTranslation(&matTranslateB, 0.0f, 0.0f, -2.0f);//往外面平移
		

		// tell Direct3D about each world transform, and then draw another triangle
		g_pd3dDevice->SetTransform(D3DTS_WORLD, &(matTranslateA * matRotateY));
		g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

		g_pd3dDevice->SetTransform(D3DTS_WORLD, &(matTranslateB * matRotateY));
		g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

		// End the scene
		g_pd3dDevice->EndScene();
	}

	// Present the backbuffer contents to the display
	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}
//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
VOID Cleanup()
{
	if (g_pVB != NULL)
		g_pVB->Release();

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

	if (g_pD3D != NULL)
		g_pD3D->Release();
}
//-----------------------------------------------------------------------------
// 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;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------
// Name: wWinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT)
{
	UNREFERENCED_PARAMETER(hInst);

	// Register the window class
	WNDCLASSEX wc =
	{
		sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
		GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
		L"D3D Tutorial", NULL
	};
	RegisterClassEx(&wc);

	// Create the application's window
	HWND hWnd = CreateWindow(L"D3D Tutorial", L"D3D Tutorial 07: ZBuffer",
		WS_OVERLAPPEDWINDOW, 100, 100, SCREEN_WIDTH, SCREEN_HEIGHT,
		NULL, NULL, wc.hInstance, NULL);

	// Initialize Direct3D
	if (SUCCEEDED(InitD3D(hWnd)))
	{
		// Create the vertex buffer
		if (SUCCEEDED(InitVB()))
		{
			// 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(L"D3D Tutorial", wc.hInstance);
	return 0;
}




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值