DirectX3D游戏开发三 绘制流水线

本文详细介绍了3D游戏开发中的变换技术,包括世界变换、观察变换和投影变换等核心概念及其应用。通过实例演示如何使用DirectX API实现3D物体的旋转、缩放和平移,以及如何设置3D相机视角。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    欢迎来到EasyLiu的博客 !

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

     In the last lesson you built a simple, flat triangle lit with simple diffuse lighting. This triangle was not 3D, it was flat. If you managed to change it, you found it was a 2D triangle drawn in screen coordinates. The triangle you made was pre-transformed, meaning you took no action to convert it from 3D coordinates to screen coordinates, but just gave Direct3D the screen coordinates.

   翻译:在上一节课中我们建立了一个简单的,具有简单漫反射光的平面三角形。这个三角形不是3D的,它是平面的。如果你想改变它,你会发现它是一个平面坐标下的二维三角形。你画的这个三角形是预先经过转换的,意思是你并没有采取任何措施而将它从三维坐标变换成了屏幕坐标,而是直接把三角形的屏幕坐标给了Direct3D进行绘制。

   In this lesson, you will learn to transform vertices, or convert them from 3D coordinates to screen coordinates. You will also learn to position the 3D camera, set the 3D "lens", and put your triangle into a 3D world with other 3D objects in it.

   翻译:在这节课,我们将学习顶点的转换或者三维坐标到屏幕坐标的转换。我们也将学习放置3D摄像头,设置3D“透镜”,并且把你的三角形放入具有其它三维物体的世界中。

绘制流水线

    一个简单的绘制流水线如下图所示:需要经过坐标变换、背面消隐、光照、裁剪、投影、视口坐标系、光栅化等过程,这里主要讲一下局部坐标系到世界坐标系的转换(世界变换)、世界坐标系到观察坐标系的转换(取景变换)、投影(投影变换)


   The Geometry Pipeline(几何管道)

    Before we get into the actual transformation code, let's take a look at everything that happens to translate something from 3D coordinates to a flat image. There is a sequence of actions which must occur before an object appears on the screen properly. This sequence of actions is known as theGeometry Pipeline. It is called a pipeline because vertices are put through each step one at a time, and at each step along the "pipe", the vertex is rendered into its flat image form.

    When a model first starts out, it is generally centered on the origin, meaning the center of the object is at (0, 0, 0). This also means that all the objects in your game will be in the exact center of the world, all piled up on top of each other, which doesn't make a very excellent game. The first step is to sort out the new positions of all the models in relation to each other.This is called World Transformation.

    Once all the objects in the world have been sorted out and the vertices' positions have been altered to represent world coordinates, then we have to change the entire coordinate system to create a virtual camera. What this means is that we have to change the direction of each axis, in order to position everything in the most efficient way for the video card. This process is known as View Transformation.

    After the coordinate system has been rearanged, then it is time to convert all the 3D models into 2D images. In this step, the 3D coordinates are converted into screen coordinates. This process is known as Projection Transformation.

    上面介绍了世界转换、视图转换(取景变换)以及投影变换的概念以及为什么要进行这些变换,下图代表了变换的过程:


World Transformation(世界变换概述)

   World Transformation, in essence, changes coordinates from model space to world space. In other words, it places a model in a world at an exact point defined by coordinates. Following is an overhead demonstration of how this works:

    

    This is known as translation. Translation refers to the movement of a vertex along a coordinate system axis. For example, the above diagram shows movement of a tree along the x and y axes. Of course, we could also translate the tree along the z-axis, but our ground here is flat, and having trees floating ten feet high just outside the local village tends to scare the tourists away (not to mention the players).

Of course, being able to move an object into world space is always useful, but it can get rather limiting if your model is always facing the same direction. A spaceship that can only face East is rather dull and does not elicit much thrill or adrenaline (or so I find).

And so another part of World Transformation is rotation(世界变换的另一部分是旋转). Rotation is the process of spinning 3D objects along an axis. Like translation, it can be done along multiple axes simultaneously, allowing you to position your model as desired.

Another important part of World Transformation is scaling(世界变换中另外一个重要的变换是缩放). Scaling is the action of making a 3D object larger or smaller. When an object is scaled, each vertex in the object is multiplied by a given number. These numbers can be different for each axis, resulting in various stretching effects.


     位于局部坐标系中的物体通过世界变换的运算变换到世界坐标系中,该变换通常包括平移、旋转以及比例运算。世界变换通过一个矩阵来表示,并由Direct3D通过SetTransform(D3DTS_WORLD, &matWorld)方法来加以应用,第二个参数表示所采用的世界变换矩阵。例如,我们想要一个立方体的中心位于世界坐标系中的的点(-3,2,6),让一个球体的中心位于点(5,0,-2),可以这样实现:

        D3DXMATRIXA16 cubeWorldMatrix;
	D3DXMatrixTranslation(&cubeWorldMatrix, -3.0f, 2.0f, 6.0f);

	D3DXMATRIXA16 sphereWorldMatrix;
	D3DXMatrixTranslation(&sphereWorldMatrix, 5.0f, 0.0f, -2.0f);

	g_pd3dDevice->SetTransform(D3DTS_WORLD, &cubeWorldMatrix);
	//绘制立方体
	g_pd3dDevice->SetTransform(D3DTS_WORLD, &sphereWorldMatrix);
	//绘制球体


View Transformation(取景变换)

    在世界坐标中,几何体和摄像机是相对世界坐标系定义的。但是当摄像机的位置和朝向任意时,投影变换以及其他类型的变换就略显困难或者效率不高。为了简化运算,我们将摄像机变换至世界坐标系的原点,并将其旋转,使得摄像头的光轴与世界坐标系的Z轴正方向一致。同时,世界坐标系中的所有几何体随着摄像头一同进行变换,保证摄像头的视场恒定,变换称为取景变换,我们称变换后的几何体位于观察坐标系中,如下图所示:




Project Transformation(投影变换)
  
    投影变换定义了视域体,并负责将视域体中的几何体投影到投影窗口中。投影矩阵比较复杂,可以使用如下函数:
// Build a perspective projection matrix. (right-handed)
D3DXMATRIX* WINAPI D3DXMatrixPerspectiveFovRH( D3DXMATRIX *pOut, FLOAT fovy, FLOAT Aspect, FLOAT zn, FLOAT zf );

   其中pOut代表投影之后的矩阵,Aspect代表横纵比(投影窗口中的几何体最终会变换到屏幕显示区,从方形(投影窗口)到矩形的显示屏的的变换会导致拉伸畸变,所谓纵横比就是显示屏纵横两维尺寸的比率,长用于校正由正方形到矩形的映射而引发的畸变,纵横比=屏幕宽度/屏幕高度),其余的三个参数的含义如下图所示:fovy代表观察范围夹角,zn代表投影中心距离近平面的距离,zf代表投影中心距离远平面的距离。



下面实现一个效果:使一个三角形绕Y轴一直旋转。主要函数是自定义的一个函数SetupMatrices,用于设定世界、观察、投影变换矩阵,代码如下所示:
VOID SetupMatrices()
{
	// For our world matrix, we will just rotate the object about the y-axis.
	D3DXMATRIXA16 matWorld;

	// Set up the rotation matrix to generate 1 full rotation (2*PI radians) 
	// every 1000 ms. To avoid the loss of precision inherent in very high 
	// floating point numbers, the system time is modulated by the rotation 
	// period before conversion to a radian angle.
	UINT iTime = timeGetTime() % 1000;//获取当前时间
	FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;//1000ms旋转360度
	D3DXMatrixRotationY(&matWorld, fAngle);//旋转矩阵
	g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);//设置世界变换

	// Set up our view matrix. A view matrix can be defined given an eye point,
	// a point to lookat, and a direction for which way is up. Here, we set the
	// eye five units back along the z-axis and up three units, look at the
	// origin, and define "up" to be in the y-direction.
	D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f);//观察点,摄像头的位置
	D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);//朝原点看,代表摄像头朝向
	D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);//垂直方向为y轴
	D3DXMATRIXA16 matView;
	D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
	g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);

	// For the projection matrix, we set up a perspective transform (which
	// transforms geometry from 3D view space to 2D viewport space, with
	// a perspective divide making objects smaller in the distance). To build
	// a perpsective transform, we need the field of view (1/4 pi is common),
	// the aspect ratio, and the near and far clipping planes (which define at
	// what distances geometry should be no longer be rendered).
	D3DXMATRIXA16 matProj;
	D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f);
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}

同时在初始化里面关闭背面消隐以及关闭光照:

// Turn off culling, so we see the front and back of the triangle
	g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

	// Turn off D3D lighting, since we are providing our own vertex colors
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);</span>

最后,Render()函数:

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

	// Begin the scene
	if (SUCCEEDED(g_pd3dDevice->BeginScene()))
	{
		// Setup the world, view, and projection matrices
		SetupMatrices();

		// 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, 1);

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

	// Present the backbuffer contents to the display
	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}
效果如下:由于是动态的,从图片看不出什么效果。

下面是main.c源代码:

//-----------------------------------------------------------------------------
// File: Matrices.cpp
//
// Desc: Now that we know how to create a device and render some 2D vertices,
//       this tutorial goes the next step and renders 3D geometry. To deal with
//       3D geometry we need to introduce the use of 4x4 matrices to transform
//       the geometry with translations, rotations, scaling, and setting up our
//       camera.
//
//       Geometry is defined in model space. We can move it (translation),
//       rotate it (rotation), or stretch it (scaling) using a world transform.
//       The geometry is then said to be in world space. Next, we need to
//       position the camera, or eye point, somewhere to look at the geometry.
//       Another transform, via the view matrix, is used, to position and
//       rotate our view. With the geometry then in view space, our last
//       transform is the projection transform, which "projects" the 3D scene
//       into our 2D viewport.
//
//       Note that in this tutorial, we are introducing the use of D3DX, which
//       is a set of helper utilities for D3D. In this case, we are using some
//       of D3DX's useful matrix initialization functions. To use D3DX, simply
//       include <d3dx9.h> and link with d3dx9.lib.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib,"winmm.lib") 


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




//-----------------------------------------------------------------------------
// 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

// A structure for our custom vertex type
struct CUSTOMVERTEX
{
	FLOAT x, y, z;      // The untransformed, 3D position for the vertex
	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;

	// Set up the structure used to create the D3DDevice
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

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

	// Turn off culling, so we see the front and back of the triangle
	g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

	// Turn off D3D lighting, since we are providing our own vertex colors
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: InitGeometry()
// Desc: Creates the scene geometry
//-----------------------------------------------------------------------------
HRESULT InitGeometry()
{
	// Initialize three vertices for rendering a triangle
	CUSTOMVERTEX g_Vertices[] =
	{
		{ -1.0f, -1.0f, 0.0f, 0xffff0000, },
		{ 1.0f, -1.0f, 0.0f, 0xff0000ff, },
		{ 0.0f, 1.0f, 0.0f, 0xffffffff, },
	};

	// Create the vertex buffer.
	if (FAILED(g_pd3dDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX),
		0, D3DFVF_CUSTOMVERTEX,
		D3DPOOL_DEFAULT, &g_pVB, NULL)))
	{
		return E_FAIL;
	}

	// Fill the vertex buffer.
	VOID* pVertices;
	if (FAILED(g_pVB->Lock(0, sizeof(g_Vertices), (void**)&pVertices, 0)))
		return E_FAIL;
	memcpy(pVertices, g_Vertices, sizeof(g_Vertices));
	g_pVB->Unlock();

	return S_OK;
}




//-----------------------------------------------------------------------------
// 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: SetupMatrices()
// Desc: Sets up the world, view, and projection transform matrices.
//-----------------------------------------------------------------------------
VOID SetupMatrices()
{
	// For our world matrix, we will just rotate the object about the y-axis.
	D3DXMATRIXA16 matWorld;

	// Set up the rotation matrix to generate 1 full rotation (2*PI radians) 
	// every 1000 ms. To avoid the loss of precision inherent in very high 
	// floating point numbers, the system time is modulated by the rotation 
	// period before conversion to a radian angle.
	UINT iTime = timeGetTime() % 1000;//获取当前时间
	FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;//1000ms旋转360度
	D3DXMatrixRotationY(&matWorld, fAngle);//旋转矩阵
	g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);//设置世界变换

	// Set up our view matrix. A view matrix can be defined given an eye point,
	// a point to lookat, and a direction for which way is up. Here, we set the
	// eye five units back along the z-axis and up three units, look at the
	// origin, and define "up" to be in the y-direction.
	D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f);//摄像头位置
	D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);//摄像头朝向
	D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);//垂直方向为Y轴
	D3DXMATRIXA16 matView;
	D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
	g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);

	// For the projection matrix, we set up a perspective transform (which
	// transforms geometry from 3D view space to 2D viewport space, with
	// a perspective divide making objects smaller in the distance). To build
	// a perpsective transform, we need the field of view (1/4 pi is common),
	// the aspect ratio, and the near and far clipping planes (which define at
	// what distances geometry should be no longer be rendered).
	D3DXMATRIXA16 matProj;
	D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f);
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}



//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()
{
	// Clear the backbuffer to a black color
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

	// Begin the scene
	if (SUCCEEDED(g_pd3dDevice->BeginScene()))
	{
		// Setup the world, view, and projection matrices
		SetupMatrices();

		// 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, 1);

		// 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;
	}

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




//-----------------------------------------------------------------------------
// Name: WinMain()
// 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 03: Matrices",
		WS_OVERLAPPEDWINDOW, 100, 100, 256, 256,
		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(L"D3D Tutorial", wc.hInstance);
	return 0;
}





    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值