拾取技术和图形管道逆变换

关于向量和点变换

3D数学中,向量可以表示点也可以表示向量,当表示点时候进行矩阵变换要用coord需要考虑平移w=1,当表示向量时候进行矩阵变换用normal不需要考虑平移w=0,用齐次向量常数是0表示向量不包括平移,非齐次向量常数是1表示的是空间点包括平移。

关于投影变换

矩阵变换:将屏幕空间捕获到的位置,转换为游戏世界坐标系中的位置
世界坐标系->观察坐标系->投影坐标系->屏幕坐标系。
对于投影坐标系有了更深入的理解: (1)投影坐标系因为透视投影面被放置到了正轴,避免了处理负数,同时避免了透视投影的上下左右颠倒。(2) 矩阵没有缩放但是设置了m34=1,使得点变换时候会进行w=z变为w=1的除法,使得经过透视投影,物体点被放置到了x属于[-1,1],y属于[-1,1],z属于[0,1](dx中,opengl中是z为[-1,1])。
(3) 3D透视投影矩阵的Z轴是对1/z进行插值,因为要在1/z后插值到[0,1]中,所以得到的常数是矩阵中的z位置,1/z是矩阵中的平移位置。  2D正交投影矩阵只要对z插值在[0,1]中就可以了,常数矩阵中平移位置,z插值是矩阵中的z位置。

屏幕坐标系变换就是将x的[-1,1]转换为[0,width],将y的[1,-1]转换为[0,height]位置,点的z值用于光栅化深度测试,和模板测试使用
3D数学中,向量可以表示点也可以表示向量,当表示点时候进行矩阵变换要用coord需要考虑平移w=1,当表示向量时候进行矩阵变换用normal不需要考虑平移w=0.

关于世界坐标变换

世界坐标转换SetTransform(D3DTS_WORLD, &M)是将物体坐标系转换到世界坐标系需要进行的转换,基于当前物体坐标系需要进行的变换  就可以了,连续变换世界坐标也不会改变或者重置世界坐标系原点,只是对当前需要绘制的物体产生影响。 
 因为建立的包围盒是变换以后的数据,所以绘制的时候又会根据世界矩阵变换一遍,所以设置世界矩阵变换为单位矩阵不变换绘制包围盒即可。

开发中遇到的各种问题

// 熟悉各种类型的分类讨论。遇到的问题不熟悉的可以通过对比分析找到一些原因。
// 超过左边和右边
// 有的轴在物体内部
// 要根据射线原点开始大于最大缩放后还是大于,或者开始是小于包围盒最大点最大缩放后还是小于。
// 过程中还发现了,根据最迟到达的射线,其实可以求出最长的变换长度的,这样就可以把不符合条件转换为符合条件的已知变换。
// 网络中搜索了下其实大家用射线和包围盒的检测,大多数使用D3DXIntersectTri的方式进行包围盒检测。
1.把物体放置在了原点,导致每次点击都会命中。
2.记得固定管道渲染是4次坐标系变换过程,世界,视图,投影,屏幕,转换到世界空间需要进行屏幕,投影,视图就可以了。
3.调试算法问题:通过不符合的现象进行调试,分析出原因
4.包围盒的颜色没有,那么是因为启动了光照的原因且没有设置材质,所以禁止光照Device->SetRenderState(D3DRS_LIGHTING, FALSE);。
5.AABB的浮点数有问题,FLT_MIN和-FLT_MAX区别,所以建立的包围盒大小是有问题的。
6.世界坐标转换SetTransform(D3DTS_WORLD, &M)是将物体坐标系转换到世界坐标系需要进行的转换,基于当前物体坐标系需要进行的变换
// 就可以了,连续变换世界坐标也不会改变或者重置世界坐标系原点,只是对当前需要绘制的物体产生影响。
// 因为建立的包围盒是变换以后的数据,所以绘制的时候又会根据世界矩阵变换一遍,所以设置世界矩阵变换为单位矩阵不变换绘制包围盒即可。

正交投影和透视投影的深入理解

2D中的正交投影到屏幕坐标系的理解,和2D中的对象拾取技术和3D中的区别研究
 设置屏幕坐标系大小定义,多视图,可以实现屏幕分割
D3DVIEWPORT9 viewport = {0, 0, width, height, 0.0f, 1.0f} ;
(*device)->SetViewport(&viewport);
 设备规范坐标系到屏幕坐标系D3D会自动进行变换,3D透视投影中通过:
  W/2, 0, 0, 0
  0, -H/2, 0,0
  0, 0, MaxZ-MinZ, 0
  X0+W/2, Y0+H/2, MinZ,1
  上述的参数含义就是D3DVIEWPORT9中定义的参数含义。
  3D透视投影矩阵的Z轴是对1/z进行插值,因为要在1/z后插值到[0,1]中,所以得到的常数是矩阵中的z位置,1/z是矩阵中的平移位置。
  2D正交投影矩阵只要对z插值在[0,1]中就可以了,常数矩阵中平移位置,z插值是矩阵中的z位置。
 
  3D透视投影的设备规范坐标系由透视投影矩阵和顶点除法变换得到,x属于[-1,1],y属于[-1,1],z属于[0,1](但是OGL中z属于[-1,-1]
  可见深度缓存中存储的深度也就是z在设备规范坐标系中的值, 但程序员也可以在世界坐标系和视图坐标系中对物体按z值排序渲染。
 
  2D正交投影的设备规范坐标系直接由正交投影变换得到,不会再进行除法,且前面的世界坐标系视图坐标系都是一样的,只不过正交
  投影去掉了进大远小的效果(z值不能影响了),直接经过正交投影变换如果x在[-1,1],y在[-1,1]规范坐标内的可以渲染,在外的不会
  渲染,z值也被正交投影放置到了[0,1]中放入深度缓存中。
  一般设置的正交投影设置下,也就是不缩放,那么物体在世界坐标系中的点和屏幕坐标系中的像素是对应的。
 
  2D和3D的区别仅仅是投影那一刻,他们在世界坐标系中都是一样的世界坐标空间的,摄像机空间也是一样的空间,
  设备规范化坐标系x属于[-1,1]y属于[-1,1]z属于[0,1]OGLz属于[-1,1],屏幕坐标系也是一样的。
  一般2D中位置用像素和格子来衡量就可以了,3D中位置需要虚拟空间尺寸和格子来衡量。

  其实D3D,OGL中的透视投影和正交投影都是没有丢掉z值的,知道z值情况下都是可逆的
  但是在不知道z值情况下,比如设置设备规范坐标系中的z值是1或者0.5或者0.0:

  那么在透视投影情况下计算得到的摄像机坐标空间下的点会不一样,但是单位射线向量都是一样的,
  因为透视投影下摄像机空间下相同视锥夹角的点,是会被投影到相同的设备规范坐标系的(因为除以w=z)。

  而在正交投影情况下得到的摄像机空间下的点会不一样,且单位化法向量也不会一样, 
 只有知道真正的z值计算得到的法向量才是正确的(因为知道z值情况下D3DXMatrixOrthoLH正交投影也是可逆的)
 因为正交投影下摄像机空间下相同的视锥夹角的点,会被投影到不同的设备规范坐标系,因为没有除以w=z,正交投影只和x,y本身
 相关,和z不相关。在不同设备坐标系z下,所以得到摄像机坐标系中的变换射线向量是不成比例的,导致拾取算法的射线方向计算错误。

射线方向计算错误导致z轴上是最远的相交,但此时计算x,y轴并没有超出包围盒区域;而透视投影钟z轴也是最远的相交, 
 而此时x,y轴是超出了包围盒的。所以3D拾取算法不能用于2D中,2D需要一个忽略掉z值的鼠标点击点是否在包围盒矩形中就可以了。
 

AABB.h

#ifndef _AABB_H_
#define _AABB_H_
#include "d3dUtility.h"
// AABB的顶点用于绘制
struct AABBVertex
{
	AABBVertex(){}
	AABBVertex(float x, float y, float z, D3DCOLOR color)
	{
		_x = x;  _y = y;  _z = z;
		_color = color;
	}
	float _x, _y, _z;
	D3DCOLOR _color;
	static const DWORD FVF;
};

class AABB
{
public:
	AABB();
	~AABB();
	// 1.自己开发的射线和包围盒检测的方法RayIntersetBoxWithoutDirLength
	// 射线与AABB的检测,该射线不用考虑射线方向向量的大小,假设方向向量是无限长的,这个判断在用鼠标在3D世界中拾取常用到
	// 避免参数方程,代入球面方程判断解,只能用球形包围盒的模式
	inline bool CheckNotArrive(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta,int  nWhich,float fMaxScale);
	bool CheckOneAxis(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta, int nWhich);
	bool RayIntersetBoxWithoutDirLength(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta);

	// 2D中的选择检测
	bool SelectObject2D(const D3DXVECTOR3 worldClickPos);

	// 2.变换后使用RayIntersect算法
	D3DXVECTOR3 GetRayDir(const D3DXVECTOR3 &rayOrg,const D3DXVECTOR3 &rayDelta);
	float	RayIntersect(
		const D3DXVECTOR3 &rayOrg,		// orgin of the ray
		const D3DXVECTOR3 &rayDelta,	// length and direction of the ray
		D3DXVECTOR3 *returnNormal		// optionally, the normal is returned
		) const;
	// 3.标准的使用D3DXIntersectTri,用射线和AABB的六个面进行检测,射线是不用求出长度的
	bool IsIntersect(const D3DXVECTOR3 &rayOrg,const D3DXVECTOR3 &rayDelta);

	// 一些公共的函数
	void CalculateBoudingBox(D3DXVECTOR3 *pVec, int n);
	void setToTransformedBox(const AABB &box, const D3DXMATRIX &m);
	void Reset();
	bool IsEmpty();
	
	// 建立包围盒模型并渲染的
	void Setup(IDirect3DDevice9 *pDevice);
	void Draw( IDirect3DDevice9 *pDevice );
private:
	D3DXVECTOR3 min;
	D3DXVECTOR3 max;
	bool m_bEmpty;
	bool m_bSetup;
	IDirect3DVertexBuffer9* VB;
	IDirect3DIndexBuffer9*  IB;
};

#endif

AABB.cpp

#include "AABB.h"

const DWORD AABBVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

AABB::AABB()
{
	VB = NULL;
	IB = NULL;
	Reset();
}

AABB::~AABB()
{

}

void AABB::Reset()
{
	min.x = min.y = min.z = FLT_MAX;
	// FLT_MIN 是最小正负数,但是我们需要的是最小的负数,所以这里如果用FLT_MIN会导致计算不正确
	max.x = max.y = max.z = -FLT_MAX;
	m_bEmpty = true;
	m_bSetup = false;
	d3d::Release<IDirect3DVertexBuffer9*>(VB);
	d3d::Release<IDirect3DIndexBuffer9*>(IB);
}

bool AABB::IsEmpty()
{
	return m_bEmpty;
}

void AABB::Setup(IDirect3DDevice9 *pDevice)
{
	if( pDevice == NULL || m_bEmpty )
	{
		m_bSetup = false;
		return;
	}

	pDevice->CreateVertexBuffer(
		8 * sizeof(AABBVertex), 
		D3DUSAGE_WRITEONLY,
		AABBVertex::FVF,
		D3DPOOL_MANAGED,
		&VB,
		0);

	pDevice->CreateIndexBuffer(
		36 * sizeof(WORD), // 索引缓存的字节数,个数是三角形的个数*3,索引缓存个数还是没有减少的。
		D3DUSAGE_WRITEONLY, // 使用类型为只读类型,获取指针然后写入数据,数据将会批量提交
		D3DFMT_INDEX16, // D3DFMT_INDEX16是16bit,D3DFMT_INDEX32是32bit
		D3DPOOL_MANAGED, // D3DPOOL类型,创建在SM中,需要时候拷贝到AGP内存/显存中
		&IB,
		0);//保留

	AABBVertex *vertices;
	D3DCOLOR blueColor = D3DCOLOR_XRGB(0,0,255);
	VB->Lock(0,0,(void**)&vertices, 0/*D3DLOCK_DISCARD*/);
	vertices[0] = AABBVertex(min.x, min.y, min.z, blueColor);
	vertices[1] = AABBVertex(min.x,  max.y,min.z, blueColor);
	vertices[2] = AABBVertex( max.x, max.y, min.z, blueColor);
	vertices[3] = AABBVertex( max.x, min.y, min.z, blueColor);
	vertices[4] = AABBVertex(min.x, min.y,  max.z, blueColor);
	vertices[5] = AABBVertex(min.x, max.y,  max.z, blueColor);
	vertices[6] = AABBVertex(max.x,  max.y,  max.z, blueColor);
	vertices[7] = AABBVertex( max.x, min.y,  max.z, blueColor);
	VB->Unlock();

	WORD *indices = NULL;
	IB->Lock(0,0,(void**)&indices, 0);
	indices[0]  = 0; indices[1]  = 1; indices[2]  = 2;
	indices[3]  = 0; indices[4]  = 2; indices[5]  = 3;

	// back side
	indices[6]  = 4; indices[7]  = 6; indices[8]  = 5;
	indices[9]  = 4; indices[10] = 7; indices[11] = 6;

	// left side
	indices[12] = 4; indices[13] = 5; indices[14] = 1;
	indices[15] = 4; indices[16] = 1; indices[17] = 0;

	// right side
	indices[18] = 3; indices[19] = 2; indices[20] = 6;
	indices[21] = 3; indices[22] = 6; indices[23] = 7;

	// top
	indices[24] = 1; indices[25] = 5; indices[26] = 6;
	indices[27] = 1; indices[28] = 6; indices[29] = 2;

	// bottom
	indices[30] = 4; indices[31] = 0; indices[32] = 3;
	indices[33] = 4; indices[34] = 3; indices[35] = 7;
	IB->Unlock();
	m_bSetup = true;
}

void AABB::Draw( IDirect3DDevice9 *pDevice )
{   
	if( pDevice == NULL || !m_bSetup )
	{
		return;
	}

	pDevice->BeginScene();
	pDevice->SetStreamSource(0, VB, 0, sizeof(AABBVertex));
	pDevice->SetIndices(IB);
	pDevice->SetFVF(AABBVertex::FVF);
	// Draw cube.
	pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
	pDevice->EndScene();
}

bool AABB::SelectObject2D(const D3DXVECTOR3 worldClickPos)
{
	return worldClickPos.x >= min.x && worldClickPos.x<= max.x && worldClickPos.y >= min.y && worldClickPos.y <= max.y;
}

bool AABB::CheckNotArrive(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta,int  nWhich,float fMaxScale )
{
	float fCurWhich;
	float fRayOrgWhich;
	float fRayDeltaWhich;
	float fMinWhich;
	float fMaxWhich;
	if ( nWhich == 1)
	{
		fRayOrgWhich = rayOrg.x;
		fRayDeltaWhich = rayDelta.x;
		fMinWhich = min.x;
		fMaxWhich = max.x;
	}
	else if( nWhich == 2)
	{
		fRayOrgWhich = rayOrg.y;
		fRayDeltaWhich = rayDelta.y;
		fMinWhich = min.y;
		fMaxWhich = max.y;
	}
	else if( nWhich == 3)
	{
		fRayOrgWhich = rayOrg.z;
		fRayDeltaWhich = rayDelta.z;
		fMinWhich = min.z;
		fMaxWhich = max.z;
	}
	else
	{
		MessageBox(NULL, "CheckNotArrive参数nWhich不对","调试",0);
		return false;
	}
	fCurWhich = fRayOrgWhich + fMaxScale * fRayDeltaWhich;
	// 转换为int是为了放宽点精度,避免选中了但是没有判断选中
	/*float fCurWhichAdd = 0.7f;
	fCurWhich += fCurWhichAdd;*/
	if( fRayOrgWhich < fMinWhich && int(fCurWhich) < int(fMinWhich) )
	{
		return false;
	}
	else if ( fRayOrgWhich > fMaxWhich && int(fCurWhich) > int(fMaxWhich) )
	{
		return false;
	}

	return true;
}

bool AABB::CheckOneAxis(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta, int nWhich )
{
	bool bIncrease = true;
	float fRayOrgWhich;
	float fRayDeltaWhich;
	float fMinWhich;
	float fMaxWhich;
	int nOtherAxis1;
	int nOtherAxis2;
	if ( nWhich == 1)
	{
		fRayOrgWhich = rayOrg.x;
		fRayDeltaWhich = rayDelta.x;
		fMinWhich = min.x;
		fMaxWhich = max.x;
		nOtherAxis1 = 2;
		nOtherAxis2 = 3;
	}
	else if( nWhich == 2)
	{
		fRayOrgWhich = rayOrg.y;
		fRayDeltaWhich = rayDelta.y;
		fMinWhich = min.y;
		fMaxWhich = max.y;
		nOtherAxis1 = 1;
		nOtherAxis2 = 3;
	}
	else if( nWhich == 3)
	{
		fRayOrgWhich = rayOrg.z;
		fRayDeltaWhich = rayDelta.z;
		fMinWhich = min.z;
		fMaxWhich = max.z;
		nOtherAxis1 = 1;
		nOtherAxis2 = 2;
	}
	else
	{
		MessageBox(NULL, "CheckNotArrive参数nWhich不对","调试",0);
		return false;
	}

	if( d3d::FloatEquip(fRayDeltaWhich, 0.0f) )
	{
		return false;
	}

	if( fRayOrgWhich > fMaxWhich )
	{
		bIncrease = false;
	}

	if( bIncrease )
	{
		// rayDelta.x x正负都是不能判断一定不相交的,因为x可能在包围盒内,只能通过是向右增加还是向右增长来判断
		if( fRayDeltaWhich < 0.0f )
		{
			if( fRayOrgWhich < fMinWhich )
			{
				return false;
			}
			float fScaleMaxLimit = ( fMinWhich - fRayOrgWhich ) / fRayDeltaWhich;
			if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis1,fScaleMaxLimit) )
			{
				return false;
			}

			if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis2 ,fScaleMaxLimit) )
			{
				return false;
			}
		}
		else
		{
			if( fRayOrgWhich > fMaxWhich )
			{
				return false;
			}

			float fScaleMaxLimit = ( fMaxWhich - fRayOrgWhich ) / fRayDeltaWhich;
			if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis1,fScaleMaxLimit) )
			{
				return false;
			}

			if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis2, fScaleMaxLimit) )
			{
				return false;
			}
		}
	}
	else
	{
		// 射线原点一定在最大值右边了,不会在物体中间情况
		if( fRayDeltaWhich >= 0.0f )
		{
			return false;
		}
		
		float fScaleMax = (  fMinWhich - fRayOrgWhich ) / fRayDeltaWhich;
		if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis1,fScaleMax) )
		{
			return false;
		}

		if( !CheckNotArrive(rayOrg, rayDelta, nOtherAxis2, fScaleMax) )
		{
			return false;
		}
	}
}
// 还有一个最长的射线方式,转换为固定长度射线和AABB相交的判断
bool AABB::RayIntersetBoxWithoutDirLength(const D3DXVECTOR3 &rayOrg, D3DXVECTOR3 rayDelta)
{
	// 如果原点在包围盒内,那么就直接返回了
	if ( rayOrg.x >= min.x && rayOrg.x <= max.x &&
		rayOrg.y >= min.y && rayOrg.y <= max.y &&
		rayOrg.z >= min.z && rayOrg.z <= max.z)
	{
		return true;
	}

	// 1.先考虑x方向上,判断 方向
	if ( !CheckOneAxis(rayOrg, rayDelta, 1) )
	{
		return false;
	}

	// 2.先考虑x方向上,判断 方向
	if ( !CheckOneAxis(rayOrg, rayDelta, 2) )
	{
		return false;
	}

	// 3.先考虑x方向上,判断 方向
	if ( !CheckOneAxis(rayOrg, rayDelta, 3) )
	{
		return false;
	}

	// 哪里出现了问题
		
	return true;
}


void AABB::CalculateBoudingBox(D3DXVECTOR3 *pVec, int n)
{
	for( int i = 0; i < n; i++ )
	{
		if(pVec[i].x < min.x )
		{
			min.x = pVec[i].x;
		}

		if(pVec[i].y < min.y )
		{
			min.y = pVec[i].y;
		}

		if(pVec[i].z < min.z )
		{
			min.z = pVec[i].z;
		}

		if(pVec[i].x > max.x )
		{
			max.x = pVec[i].x;
		}
		if(pVec[i].y > max.y )
		{
			max.y = pVec[i].y;
		}
		if(pVec[i].z > max.z )
		{
			max.z = pVec[i].z;
		}
	}

	m_bEmpty = false;
}

D3DXVECTOR3 AABB::GetRayDir(const D3DXVECTOR3 &rayOrg, const D3DXVECTOR3 &rayDelta)
{
	float fMaxScale = 0.0f;
	float fMaxScaleNext = 0.0f;
	if( rayOrg.x < max.x && rayDelta.x > 0.0f )
	{
		fMaxScale = (max.x - rayOrg.x) / rayDelta.x;
	}
	else if( rayOrg.x > min.x && rayDelta.x < 0.0f )
	{
		fMaxScale = (min.x - rayOrg.x) / rayDelta.x;
	}

	if( rayOrg.y < max.y && rayDelta.y > 0.0f )
	{
		fMaxScaleNext = (max.y - rayOrg.y) / rayDelta.y;
	}
	else if( rayOrg.y > min.y && rayDelta.y < 0.0f )
	{
		fMaxScaleNext = (min.y - rayOrg.y) / rayDelta.y;
	}

	if( fMaxScaleNext > fMaxScale )
	{
		fMaxScale = fMaxScaleNext;
	}

	if( rayOrg.z < max.z && rayDelta.z > 0.0f )
	{
		fMaxScaleNext = (max.z - rayOrg.z) / rayDelta.z;
	}
	else if( rayOrg.z > min.z && rayDelta.z < 0.0f )
	{
		fMaxScaleNext = (min.z - rayOrg.z) / rayDelta.z;
	}

	if( fMaxScaleNext > fMaxScale )
	{
		fMaxScale = fMaxScaleNext;
	}

	return (fMaxScale * rayDelta);
}

float	AABB::RayIntersect(
	const D3DXVECTOR3 &rayOrg,		// orgin of the ray
	const D3DXVECTOR3 &rayDelta,	// length and direction of the ray
	D3DXVECTOR3 *returnNormal		// optionally, the normal is returned
	) const
{ // We'll return this huge number if no intersection

		const float kNoIntersection = 1e30f;

		// Check for point inside box, trivial reject, and determine parametric
		// distance to each front face

		bool inside = true;

		float xt, xn;
		if (rayOrg.x < min.x) {
			xt = min.x - rayOrg.x;
			if (xt > rayDelta.x) return kNoIntersection;
			xt /= rayDelta.x;
			inside = false;
			xn = -1.0f;
		} else if (rayOrg.x > max.x) {
			xt = max.x - rayOrg.x;
			if (xt < rayDelta.x) return kNoIntersection;
			xt /= rayDelta.x;
			inside = false;
			xn = 1.0f;
		} else {
			xt = -1.0f;
		}

		float yt, yn;
		if (rayOrg.y < min.y) {
			yt = min.y - rayOrg.y;
			if (yt > rayDelta.y) return kNoIntersection;
			yt /= rayDelta.y;
			inside = false;
			yn = -1.0f;
		} else if (rayOrg.y > max.y) {
			yt = max.y - rayOrg.y;
			if (yt < rayDelta.y) return kNoIntersection;
			yt /= rayDelta.y;
			inside = false;
			yn = 1.0f;
		} else {
			yt = -1.0f;
		}

		float zt, zn;
		if (rayOrg.z < min.z) {
			zt = min.z - rayOrg.z;
			if (zt > rayDelta.z) return kNoIntersection;
			zt /= rayDelta.z;
			inside = false;
			zn = -1.0f;
		} else if (rayOrg.z > max.z) {
			zt = max.z - rayOrg.z;
			if (zt < rayDelta.z) return kNoIntersection;
			zt /= rayDelta.z;
			inside = false;
			zn = 1.0f;
		} else {
			zt = -1.0f;
		}

		// Inside box?

		if (inside) {
			if (returnNormal != NULL) {
				*returnNormal = -rayDelta;
				//returnNormal->normalize();
				D3DXVec3Normalize(returnNormal, returnNormal);
			}
			return 0.0f;
		}

		// Select farthest plane - this is
		// the plane of intersection.

		int which = 0;
		float t = xt;
		if (yt > t) {
			which = 1;
			t = yt;
		}
		if (zt > t) {
			which = 2;
			t = zt;
		}

		switch (which) {

		case 0: // intersect with yz plane
			{
				float y = rayOrg.y + rayDelta.y*t;
				if (y < min.y || y > max.y) return kNoIntersection;
				float z = rayOrg.z + rayDelta.z*t;
				if (z < min.z || z > max.z) return kNoIntersection;

				if (returnNormal != NULL) {
					returnNormal->x = xn;
					returnNormal->y = 0.0f;
					returnNormal->z = 0.0f;
				}

			} break;

		case 1: // intersect with xz plane
			{
				float x = rayOrg.x + rayDelta.x*t;
				if (x < min.x || x > max.x) return kNoIntersection;
				float z = rayOrg.z + rayDelta.z*t;
				if (z < min.z || z > max.z) return kNoIntersection;

				if (returnNormal != NULL) {
					returnNormal->x = 0.0f;
					returnNormal->y = yn;
					returnNormal->z = 0.0f;
				}

			} break;

		case 2: // intersect with xy plane
			{
				float x = rayOrg.x + rayDelta.x*t;
				if (x < min.x || x > max.x) return kNoIntersection;
				float y = rayOrg.y + rayDelta.y*t;
				if (y < min.y || y > max.y) return kNoIntersection;

				if (returnNormal != NULL) {
					returnNormal->x = 0.0f;
					returnNormal->y = 0.0f;
					returnNormal->z = zn;
				}

			} break;
		}

		// Return parametric point of intersection

		return t;
}

bool AABB::IsIntersect(const D3DXVECTOR3 &rayOrg,const D3DXVECTOR3 &rayDelta)
{
	if( m_bEmpty )
	{
		return false;
	}
	if( !m_bSetup )
	{
		return false;
	}

	D3DXVECTOR3 v0(min.x, min.y, min.z);
	D3DXVECTOR3 v1(min.x, max.y, min.z);

	D3DXVECTOR3 v2(max.x, max.y, min.z);
	D3DXVECTOR3 v3(max.x, min.y, min.z);

	D3DXVECTOR3 v4(min.x, min.y, max.z);
	D3DXVECTOR3 v5(max.x, min.y, max.z);

	D3DXVECTOR3 v6(max.x, max.y, max.z);
	D3DXVECTOR3 v7(min.x, max.y, max.z);

	// front
	if( D3DXIntersectTri(&v0, &v1, &v2, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}
	if( D3DXIntersectTri(&v0, &v2, &v3, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}

	// back
	if( D3DXIntersectTri(&v4, &v5, &v6, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}

	if( D3DXIntersectTri(&v4, &v6, &v7, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}

	// left
	if( D3DXIntersectTri(&v4, &v7, &v1, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}
	if( D3DXIntersectTri(&v4, &v1, &v0, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}

	// right
	if( D3DXIntersectTri(&v5, &v3, &v2, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}
	if( D3DXIntersectTri(&v5, &v2, &v6, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}

	// top
	if( D3DXIntersectTri(&v7, &v6, &v2, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}
	if( D3DXIntersectTri(&v7, &v2, &v1, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}

	// bottom
	if( D3DXIntersectTri(&v0, &v3, &v5, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}
	if( D3DXIntersectTri(&v0, &v5, &v4, &rayOrg,&rayDelta, NULL, NULL, NULL) )
	{
		return true;
	}
}

void	AABB::setToTransformedBox(const AABB &box, const D3DXMATRIX &m) 
{

	// If we're empty, then bail
	if ( box.m_bEmpty ) {
		return;
	}

	// Start with the translation portion

	min = max = D3DXVECTOR3(m._41, m._42, m._43);

	// Examine each of the 9 matrix elements
	// and compute the new AABB

	if (m._11 > 0.0f) {
		min.x += m._11 * box.min.x; max.x += m._11 * box.max.x;
	} else {
		min.x += m._11 * box.max.x; max.x += m._11 * box.min.x;
	}

	if (m._12 > 0.0f) {
		min.y += m._12  * box.min.x; max.y += m._12  * box.max.x;
	} else {
		min.y += m._12  * box.max.x; max.y += m._12  * box.min.x;
	}

	if (m._13 > 0.0f) {
		min.z += m._13 * box.min.x; max.z += m._13 * box.max.x;
	} else {
		min.z += m._13 * box.max.x; max.z += m._13 * box.min.x;
	}

	if (m._21 > 0.0f) {
		min.x += m._21 * box.min.y; max.x += m._21 * box.max.y;
	} else {
		min.x += m._21 * box.max.y; max.x += m._21 * box.min.y;
	}

	if (m._22 > 0.0f) {
		min.y += m._22 * box.min.y; max.y += m._22 * box.max.y;
	} else {
		min.y += m._22 * box.max.y; max.y += m._22 * box.min.y;
	}

	if (m._23 > 0.0f) {
		min.z += m._23 * box.min.y; max.z += m._23 * box.max.y;
	} else {
		min.z += m._23 * box.max.y; max.z += m._23 * box.min.y;
	}

	if (m._31 > 0.0f) {
		min.x += m._31 * box.min.z; max.x += m._31 * box.max.z;
	} else {
		min.x += m._31 * box.max.z; max.x += m._31 * box.min.z;
	}

	if (m._32 > 0.0f) {
		min.y += m._32 * box.min.z; max.y += m._32 * box.max.z;
	} else {
		min.y += m._32* box.max.z; max.y += m._32 * box.min.z;
	}

	if (m._33 > 0.0f) {
		min.z += m._33 * box.min.z; max.z += m._33 * box.max.z;
	} else {
		min.z += m._33 * box.max.z; max.z += m._33 * box.min.z;
	}

	m_bEmpty = false;
}

PickingTechnology.h

struct Ray
{
	D3DXVECTOR3 _origin;
	D3DXVECTOR3 _direction;
};

struct BoudingSphere
{
	D3DXVECTOR3 _center;
	float _radius;
};

class CPickingTechnology
{
public:
	CPickingTechnology(int nWidth, int nHeight, IDirect3DDevice9 *pDevice);
	~CPickingTechnology();
	// 构建包围盒
	void BuildObjectBox(D3DXVECTOR3 *pVec, int n, const string strObjectMarkNmae);
	bool IsPickingOK(const POINT &cursorPoint);
	void SetupAABB();
	void DrawAABB();
	void setToTransformedBox(const AABB &box, const D3DXMATRIX &m);

	// 和包围球的相交检测
	Ray CalcPickingRay(int x, int y);
	void TransFormRay(Ray *ray, D3DXMATRIX *T);
	bool PickingTest(int x, int y);
	bool RaySphereIntTest( Ray *ray, BoudingSphere *sphere);

private:
	// 自己写的转换函数验证过是正确的,而且非常清晰
	void CalculateRayDirFromScreenToWorld(const POINT &cursorPoint);

private:
	// 包围盒物体的名字,用于调试
	string m_strObjectName;

	// 当前的包围盒对象
	 AABB m_AABB;
	// 鼠标点击世界坐标系中原点
	D3DXVECTOR3 m_cursorWorldOrigin;
	// 鼠标点击的世界坐标系中射线方向向量
	D3DXVECTOR3 m_cursorWorldDir;
	// 设备对象
	IDirect3DDevice9 *m_pDevice;
	// 宽高
	int m_nWidth;
	int m_nHeight;
	// 包围球
	BoudingSphere m_Shere;
};
#endif

PickingTechnology.cpp

#include "PickingTechnology.h"

CPickingTechnology::CPickingTechnology(int nWidth, int nHeight, IDirect3DDevice9 *pDevice)
{
	m_nWidth = nWidth;
	m_nHeight = nHeight;
	m_pDevice = pDevice;
}

CPickingTechnology::~CPickingTechnology()
{
}

void CPickingTechnology::BuildObjectBox(D3DXVECTOR3 *pVec, int n,  const string strObjectMarkNmae)
{
	m_AABB.CalculateBoudingBox(pVec, n);
	m_strObjectName = strObjectMarkNmae;
}

void CPickingTechnology::CalculateRayDirFromScreenToWorld(const POINT &cursorPoint)
{
	if(m_pDevice == NULL)
	{
		return;
	}
	// 由屏幕变换到x,y属于[-1,1], z属于1的裁剪除以w = z后的空间为
	float x = 2.0f * cursorPoint.x / m_nWidth - 1;
	float y = 1 - 2.0f * cursorPoint.y / m_nHeight;
	D3DXVECTOR3 vecPointInCube(x, y, 0.0f);

	// 由cube空间通过透视投影求逆,得到鼠标点击点在透视投影空间中的位置
	D3DXMATRIX perspectiveMatrix;
	
	m_pDevice->GetTransform(D3DTS_PROJECTION,&perspectiveMatrix);
	D3DXMATRIX perspectiveMatrixInverse;
	D3DXMatrixInverse(&perspectiveMatrixInverse, NULL, &perspectiveMatrix);
	D3DXVECTOR3 vecPointInPerspective;
	// 视图空间的透视投影变换内部仅仅是放缩,尽管没有进行4D 到3D的除法运算,就是除以w = z的运算, 但是因为需要的是单位向量.
	// 其逆运算cube空间内乘以w = z,后计算得到的方向向量和没有乘以w = z的运算得到的单位向量都是一样的。
	// 所以直接透视逆矩阵乘法,就得到视图空间中所求的方向向量
	// Transforms a 3D vector by a given matrix, projecting the result back into w = 1.
	// 当投影变换时候虽然没有4D 到3D的除以了w = z 等于1,但是向量点进行变换时候,却会进行除以w = z的变换,使得透视投影变换
	// 向量点,会使得向量点被投影到xy属于[-1,1],z属于[0,1]的小立方体中;
	// 同时投影矩阵求逆时也是没有进行乘以w=z的乘法,但是 D3DXVec3TransformCoord变换时候,会使得w=z,变为w = 1,从而进行乘法。
	D3DXVec3TransformCoord(&vecPointInPerspective, &vecPointInCube, &perspectiveMatrixInverse);
	// 测试
	//D3DXMATRIX projMatrix(1.33f, 0.0f, 0.0f, 0.0f,
	//	0.0f, 1.0f, 0.0f, 0.0f,
	//	0.0f, 0.0f, 0.0f, -0.999f,// 一定要-0.999因为是D3DTS_PROJECTION
	//	0.0f, 0.0f, 1.0f, 1.0f
	//	);
	//D3DXVECTOR3 vecTest(-0.0094f, 0.054, 1);
	////D3DXVec3Normalize(&vecTest, &vecTest);
	//D3DXVECTOR3 vecOut;
	//// 归一化处理如果w=0.001,那么乘以1000就得到投影坐标系中的坐标;这里其实逆向运算是需要真正的z的值,才能进行更准确的变换
	//// 对于拾取技术,是不需要z的值的,因为用一个点击的原点和射线单位向量就可以了,检测单位向量穿过的最前面的物体
	//// 置于屏幕中斜线前面和后面的物体并不是同一个射线的,正对z轴里面的物体才是同一个射线选中的
	//D3DXVec3TransformCoord(&vecOut, &vecTest, &projMatrix);
	//// 测试结束

	// 鼠标点击的原点是(0,0,0)和方向向量进行规范化
	D3DXVECTOR3 cursorPersOrigin(0.0f, 0.0f, 0.0f);
	/*D3DXVECTOR3 cursorPersDir;
	D3DXVec3Normalize(&cursorPersDir, &vecPointInPerspective);*/

	// 视图空间到世界坐标系的变换
	D3DXMATRIX viewMatrix;
	D3DXMATRIX viewInverseMatrix;
	m_pDevice->GetTransform(D3DTS_VIEW, &viewMatrix);
	D3DXMatrixInverse(&viewInverseMatrix, NULL, &viewMatrix);

	// 原点进行点变换,需要考虑平移,w = 1
	D3DXVec3TransformCoord(&m_cursorWorldOrigin, &cursorPersOrigin, &viewInverseMatrix);
	// 鼠标射线进行点变换,需要考虑平移,w = 0
	D3DXVec3TransformNormal(&m_cursorWorldDir, &vecPointInPerspective, &viewInverseMatrix);
	// 单位化,因为拾取技术,只需要知道射线方向选择第一个相交的物体旧可以了
	// 2D中可以不用单位化,通过世界坐标系中的x,y进行判断即可
	D3DXVec3Normalize(&m_cursorWorldDir, &m_cursorWorldDir);
}

bool CPickingTechnology::IsPickingOK(const POINT &cursorPoint)
{
	if( m_AABB.IsEmpty() )
	{
		MessageBox(NULL, "没有设置需要判断的物体", "鼠标拾取判断", 0);
		return false;
	}
	/*POINT point;
	point.x = 576;
	point.y = 425;*/
	CalculateRayDirFromScreenToWorld(cursorPoint);
	//  m_cursorWorldDir是世界坐标系中的点
	// 2D的自己写的
	//bool bSelect = m_AABB.SelectObject2D(m_cursorWorldDir);

	// 3D通用的标准做法
	//bool bSelect = m_AABB.IsIntersect(m_cursorWorldOrigin, m_cursorWorldDir);

	// 3D的改用的标准算法
	D3DXVECTOR3 rayDir = m_AABB.GetRayDir(m_cursorWorldOrigin, m_cursorWorldDir);
	float ft = m_AABB.RayIntersect(m_cursorWorldOrigin, rayDir/*m_cursorWorldDir*/, NULL);
	// 3D的自己写的算法
	//bool bSelect = m_AABB.RayIntersetBoxWithoutDirLength(m_cursorWorldOrigin, m_cursorWorldDir);
	string strMsg = "拾取对象:"; 
	if ( ft >= 0.0f && ft <=1.0f/*bSelect*/ )
	 {
		 strMsg += m_strObjectName;
	 }
	 else
	 {
		 strMsg += "没有选择对象";
	 }

	 MessageBox(NULL, strMsg.c_str(), "鼠标拾取判断", 0);
}

void CPickingTechnology::SetupAABB()
{
	m_AABB.Setup(m_pDevice);
}
void CPickingTechnology::DrawAABB()
{
	m_AABB.Draw(m_pDevice);
}

void CPickingTechnology::setToTransformedBox(const AABB &box, const D3DXMATRIX &m)
{
	m_AABB.setToTransformedBox(box, m);
}

Ray CPickingTechnology::CalcPickingRay(int x, int y)
{
	float px = 0.0f;
	float py = 0.0f;
	D3DVIEWPORT9 vp;
	m_pDevice->GetViewport(&vp);
	D3DXMATRIX proj;
	m_pDevice->GetTransform(D3DTS_PROJECTION, &proj);
	px = ( ((2.0f * x) / vp.Width) - 1.0f) / proj(0,0);
	py = ( ((-2.0f * y) / vp.Height) + 1.0f) / proj(1,1);

	Ray ray;
	ray._origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	ray._direction = D3DXVECTOR3(px, py, 1.0f);
	return ray;
}

void CPickingTechnology::TransFormRay(Ray *ray, D3DXMATRIX *T)
{
	D3DXVec3TransformCoord(&(ray->_origin), &(ray->_origin),T);
	// 鼠标射线进行点变换,需要考虑平移,w = 1
	D3DXVec3TransformNormal(&(ray->_direction), &(ray->_direction),T);

	D3DXVec3Normalize(&(ray->_direction), &(ray->_direction));
}

bool CPickingTechnology::RaySphereIntTest( Ray *ray, BoudingSphere *sphere)
{
	D3DXVECTOR3 v = ray->_origin - sphere->_center;
	float b = 2.0f * D3DXVec3Dot(&ray->_direction, &v);
	float c = D3DXVec3Dot(&v, &v) - sphere->_radius * sphere->_radius;
	float discriminant = b * b - 4.0f * c;
	if( discriminant < 0.0f)
	{
		return false;
	}
	discriminant = sqrt(discriminant);
	float s0 = (-b + discriminant) / 2.0f;
	float s1 = (-b -discriminant) / 2.0f;
	if(s0 >= 0.0f || s1 >= 0.0f)
	{
		return true;
	}
	return false;
}

bool CPickingTechnology::PickingTest(int x, int y)
{
	Ray ray = CalcPickingRay(x, y);
	D3DXMATRIX viewMatrix;
	D3DXMATRIX viewInverseMatrix;
	m_pDevice->GetTransform(D3DTS_VIEW, &viewMatrix);
	D3DXMatrixInverse(&viewInverseMatrix, NULL, &viewMatrix);

	//float ft = m_AABB.RayIntersect(ray._origin, ray._direction, NULL);
	m_Shere._center = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	m_Shere._radius = 1.732f;
	//bool bSelect = m_AABB.RayIntersectLimitless(m_cursorWorldOrigin, m_cursorWorldDir);
	string strMsg = "拾取对象:"; 
	bool bOK = RaySphereIntTest(&ray, &m_Shere);
	if ( bOK/*ft >= 0.0f && ft <=1.0f*/ )
	{
		strMsg += m_strObjectName;
		bOK = true;
	}
	else
	{
		strMsg += "没有选择对象";
	}

	MessageBox(NULL, strMsg.c_str(), "鼠标拾取判断", 0);
	return bOK;
}

cube.cpp

//////////////////////////////////////////////////////////////////////////////////////////////////
// 
// File: cube.cpp
// 
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 
//
// Desc: Renders a spinning cube in wireframe mode.  Demonstrates vertex and 
//       index buffers, world and view transformations, render states and
//       drawing commands.
//          
//////////////////////////////////////////////////////////////////////////////////////////////////

#include "d3dUtility.h"
#include "PickingTechnology.h"

// 拾取对象
CPickingTechnology *g_pPickingObj =  NULL;
//
// Globals
//

IDirect3DDevice9* Device = 0; 

const int Width  = 640;
const int Height = 480;

IDirect3DVertexBuffer9* VB = 0;
IDirect3DIndexBuffer9*  IB = 0;

//
// Classes and Structures
//

struct Vertex
{
	Vertex(){}
	Vertex(float x, float y, float z, D3DCOLOR color)
	{
		_x = x;  _y = y;  _z = z;
		_color = color;
	}
	float _x, _y, _z;
	D3DCOLOR _color;
	static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

//
// Framework Functions
//
bool Setup()
{
	//
	// Create vertex and index buffers.
	//

	Device->CreateVertexBuffer(
		8 * sizeof(Vertex), 
		D3DUSAGE_WRITEONLY,
		Vertex::FVF,
		D3DPOOL_MANAGED,
		&VB,
		0);

	Device->CreateIndexBuffer(
		36 * sizeof(WORD), // 索引缓存的字节数,个数是三角形的个数*3,索引缓存个数还是没有减少的。
		D3DUSAGE_WRITEONLY, // 使用类型为只读类型,获取指针然后写入数据,数据将会批量提交
		D3DFMT_INDEX16, // D3DFMT_INDEX16是16bit,D3DFMT_INDEX32是32bit
		D3DPOOL_MANAGED, // D3DPOOL类型,创建在SM中,需要时候拷贝到AGP内存/显存中
		&IB,
		0);//保留

	//
	// Fill the buffers with the cube data.
	//

	// define unique vertices:
	Vertex* vertices;
	// Locks a range of vertex data and obtains a pointer to the vertex buffer memory.
	/* nOffset: 0 whole
	   nSize: 0 Whole
	   void **ppbData
	nFlags: Combination of zero or more locking flags that describe the type of lock to perform. For this method, the valid flags are:
	D3DLOCK_DISCARD */
	//D3DLOCK_DISCARD
	//char szDebugInfo[256];
	VB->Lock(0, 0, (void**)&vertices, 0/*D3DLOCK_DISCARD*/);

	// vertices of a unit cube
	/*vertices[0] = Vertex(-1.0f, -1.0f, -1.0f, D3DCOLOR_XRGB(255,0,0));
	vertices[1] = Vertex(-1.0f,  1.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[2] = Vertex( 1.0f,  1.0f, -1.0f, D3DCOLOR_XRGB(0,255,0));
	vertices[3] = Vertex( 1.0f, -1.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[4] = Vertex(-1.0f, -1.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[5] = Vertex(-1.0f,  1.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[6] = Vertex( 1.0f,  1.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[7] = Vertex( 1.0f, -1.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));*/

	vertices[0] = Vertex(-100.0f, -100.0f, -1.0f, D3DCOLOR_XRGB(255,0,0));
	vertices[1] = Vertex(-100.0f,  100.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[2] = Vertex( 100.0f,  100.0f, -1.0f, D3DCOLOR_XRGB(0,255,0));
	vertices[3] = Vertex( 100.0f, -100.0f, -1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[4] = Vertex(-100.0f, -100.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[5] = Vertex(-100.0f,  100.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[6] = Vertex( 100.0f,  100.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));
	vertices[7] = Vertex( 100.0f, -100.0f,  1.0f, D3DCOLOR_XRGB(255,255,0));

	VB->Unlock();
	// define the triangles of the cube:
	WORD* indices = 0;
	IB->Lock(0, 0, (void**)&indices, 0);

	// DX中三角形顺时针(左手定则)为正面, 逆时针为背面会被消隐,OGL则相反。
	// 索引缓存点的个数是三角形片面数 * 3得到的个数。
	// 所有索引缓存表面从外往物体里看都是顺时针给出的(例如六面体背面的索引缓存顺序),索引缓存每连续三个为一个三角形即可。

	// 索引缓存的好处:
	// 1.减少了顶点缓存的顶点个数(不需重复顶点)
	// 2.灵活修改图形(通过修改索引而不是顶点)
	// 3.提高渲染runtime将多次用到的数据放置在相近的地方Cache中(local reference)
	// front side
	indices[0]  = 0; indices[1]  = 1; indices[2]  = 2;
	indices[3]  = 0; indices[4]  = 2; indices[5]  = 3;

	// back side
	indices[6]  = 4; indices[7]  = 6; indices[8]  = 5;
	indices[9]  = 4; indices[10] = 7; indices[11] = 6;

	// left side
	indices[12] = 4; indices[13] = 5; indices[14] = 1;
	indices[15] = 4; indices[16] = 1; indices[17] = 0;

	// right side
	indices[18] = 3; indices[19] = 2; indices[20] = 6;
	indices[21] = 3; indices[22] = 6; indices[23] = 7;

	// top
	indices[24] = 1; indices[25] = 5; indices[26] = 6;
	indices[27] = 1; indices[28] = 6; indices[29] = 2;

	// bottom
	indices[30] = 4; indices[31] = 0; indices[32] = 3;
	indices[33] = 4; indices[34] = 3; indices[35] = 7;

	IB->Unlock();

	

	//
	// Position and aim the camera.
	//

// 视图坐标系和局部坐标系是一样的,都是世界坐标系转换为指定的局部坐标系,从局部点到世界点需要先旋转后平移Mx*My*Mz*P的变换矩阵,
//而从世界点到局部点要进行先平移后旋转的逆过程P-1Mz-1My-1Mx-1的变换矩矩阵(PMzMyMx是重用局部点到世界的变换).
//矩阵是重新分解和组合空间位置和方位的空间变换过程;
//矩阵行是新坐标系的基向量或表示平移,用变换后的向量在原坐标系各轴分解得的向量表示;
//矩阵列是对应原坐标系的各轴,是各新基向量在列对应原坐标系的轴上的累积(组合)向量。

	D3DXVECTOR3 position(0.0f, 0.0f, -100.0f);//camera在世界坐标系中的位置向量
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);//target是camera需要平移到的且旋转所在的点,可以是原点,也 可以是其它观察点
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);//定义向上的方向,一般是[0,1,0]
    D3DXMATRIX V;
	// V是world-to-view空间的变换矩阵,内部会计算世界坐标系到观察坐标系的变换(物体假设不动),
	// 其实新的坐标系下所有物体的位置时重新计算了的。
	D3DXMatrixLookAtLH(&V, &position, &target, &up);
    Device->SetTransform(D3DTS_VIEW, &V);////设置变换的状态,待最后综合各种变换得到最终实际变换时才变换(可暂时理解为世界坐标系中的物体被变换到观察坐标系中)

	//
	// Set the projection matrix.
	//

	D3DXMATRIX proj;
	D3DXMatrixPerspectiveFovLH(
		// 假设物体不动,观察坐标系到投影坐标系的转换矩阵
		// 待最后综合各种变换得到最终实际变换时才变换(可暂时理解为观察坐标系中的物体被变换到投影坐标系内)
			&proj, 
			D3DX_PI * 0.5f, // 90 - degree 观察坐标系中的上下夹角
			(float)Width / (float)Height, // 宽高比例
			1.0f, // 观察坐标系视锥体的近截面
			1000.0f);// 观察坐标系视锥体的远截面

	D3DXMATRIX proj2D;
	D3DXMatrixOrthoLH(&proj2D, float(Width), float(Height), 1.0f, 1000.0f);
	Device->SetTransform(D3DTS_PROJECTION, &proj);
	//
	// Switch to wireframe mode.
	//
	Device->SetRenderState(D3DRS_LIGHTING, FALSE);
	Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID/*D3DFILL_WIREFRAME*/);
	Device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
	//Device->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, true);
	//D3DXMATRIX Rx, Ry;
	static float y = 2.0f;
	D3DXMATRIX T;
	D3DXMatrixTranslation(&T, -3.0f, 2.0f, 6.0f); 
	//D3DXMatrixRotationY(&Ry, y);
	//D3DXMatrixIdentity(&Ry);
	// 缩放或旋转会导致包围盒拉伸变形,而不正确
	D3DXMATRIX M =T /*T * Ry*/;
	AABB originBox;
	D3DXVECTOR3 pVertex[8];
	for ( int i = 0; i < 8; i++)
	{ 
		pVertex[i] = D3DXVECTOR3(vertices[i]._x, vertices[i]._y, vertices[i]._z);
		D3DXVec3TransformCoord(&pVertex[i], &pVertex[i], &M);
	}
	//originBox.CalculateBoudingBox(pVertex, 8);
	//g_pPickingObj->setToTransformedBox(originBox, M);
	g_pPickingObj->BuildObjectBox(pVertex, 8, "茶壶");

	

	// 设置包围盒
	/*VB->Lock(0, 0, (void**)&vertices, 0);
	

	if( g_pPickingObj != NULL )
	{
	g_pPickingObj->BuildObjectBox( pVertex, 8, "茶壶");
	}

	VB->Unlock();*/

	if( g_pPickingObj != NULL )
	{
		g_pPickingObj->SetupAABB();
	}
	

	return true;
}

void Cleanup()
{
	d3d::Release<IDirect3DVertexBuffer9*>(VB);
	d3d::Release<IDirect3DIndexBuffer9*>(IB);
}

bool Display(float timeDelta)
{
	if( Device )
	{
		//
		// spin the cube:
		//
		D3DXMATRIX Rx, Ry;

		// rotate 45 degrees on x-axis
		D3DXMatrixRotationX(&Rx, 3.14f / 4.0f);

		// incremement y-rotation angle each frame
		static float y = 2.0f;
		D3DXMatrixRotationY(&Ry, y);
		y += timeDelta;

		// reset angle to zero when angle reaches 2*PI
		if( y >= 6.28f )
			y = 0.0f;

		// combine x- and y-axis rotation transformations.
		//D3DXMATRIX p = Rx * Ry;
		// 世界坐标系的转换命令会被runtime commmand buffer调度,因为前面的D3DTS_VIEW,D3DTS_PROJECTION会被真正渲染管道中的时候排序到后面去了
	
	/*	D3DXMATRIX T = D3DXMATRIX( 1.0f, 0.0f, 0.0f, 0.0f,
			0.0f, 1.0f, 0.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
			0.0f, 1.0f,5.0f,1.0f);
		D3DXMATRIX M = T * Ry;

		Device->SetTransform(D3DTS_WORLD, &M);*/
		
		D3DXMATRIX T;
		D3DXMatrixTranslation(&T, -3.0f, 2.0f, 6.0f); 
		Device->SetTransform(D3DTS_WORLD, &T); 
		// draw the scene:
		//
		Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
		Device->BeginScene();
		Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
		Device->SetIndices(IB);
		Device->SetFVF(Vertex::FVF);
		// Draw cube.
		Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
		Device->EndScene();

		D3DXMATRIX T2;
		D3DXMatrixIdentity(&T2);
		Device->SetTransform(D3DTS_WORLD, &T2); 
		if( g_pPickingObj != NULL )
		{
			g_pPickingObj->DrawAABB();
		}

		Device->Present(0, 0, 0, 0);
	}
	return true;
}


//
// WndProc
//
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch( msg )
	{
	case WM_DESTROY:
		::PostQuitMessage(0);
		break;
		
	case WM_KEYDOWN:
		if( wParam == VK_ESCAPE )
			::DestroyWindow(hwnd);
		break;
	case WM_LBUTTONDOWN:
		{
			POINT p;
			GetCursorPos(&p);
			if( g_pPickingObj != NULL )
			{
				g_pPickingObj->IsPickingOK(p);
				//g_pPickingObj->PickingTest/*IsPickingOK*/(p.x, p.y);
			}

		}
		break;
	}
	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

//
// WinMain
//
int WINAPI WinMain(HINSTANCE hinstance,
				   HINSTANCE prevInstance, 
				   PSTR cmdLine,
				   int showCmd)
{
	if(!d3d::InitD3D(hinstance,
		Width, Height, true, D3DDEVTYPE_HAL, &Device))
	{
		::MessageBox(0, "InitD3D() - FAILED", 0, 0);
		return 0;
	}
	g_pPickingObj = new CPickingTechnology(Width, Height, Device);
		
	if(!Setup())
	{
		::MessageBox(0, "Setup() - FAILED", 0, 0);
		return 0;
	}

	d3d::EnterMsgLoop( Display );

	Cleanup();

	Device->Release();

	return 0;
}


你可能感兴趣的:(拾取技术和图形管道逆变换)