一个入门四叉树例子

概述: 

      一个四叉树读书笔记

     源代码: http://www.pudn.com/downloads343/sourcecode/windows/opengl/detail1500968.html

 

正文:

      首先四叉树结构

typedef struct _QNode_{

	unsigned int	*m_tri_indexes;				//地形的索引序列
	
	unsigned int	m_num_triangles;			//该节点含有的三角面片数,可能为0

	Vector3	        m_min, m_max;		                 //X Z 平面的包围盒

	struct _QNode_	*m_children[4];				//所包含的子节点


}QNode;

基础地形数据的建立:

    创建一个Row*Col的网格,毋庸置疑,他包含了(Row-1)* (Col-1)个有效的三维顶点,包含(Row-1) * (Col-1)  * 2个三角形一个网格由两个三角形来表示,要表示这些三角形,则需要 (Row-1) * (Col-1)  * 2 * 3 个索引来表示。

   值得一提的是本例子采用的三角形索引顶点存储顺序如图所示:

一个入门四叉树例子_第1张图片

             三角形索引序列为: 以一个网格为例 右上角三角形(i,j),(i+1,j+1) , (i+1,j+0),
左下角三角形 (i,j+1),(i+1,j+1),(i,j) 索引序列存储的是连续的三角形(以第一个顶点为标记),

依次类推…..


创建节点树

 

1 四叉树由上到下建立,从根节点开始,通过遍历顶点的X,Z,将整张地形的大小赋值给根节点。

 

2,根节点包围盒宽高,根据包围盒的起始位置,将其分割为四个子节点。一次判断对应子节点中包围盒中对应的位置包含的三角面片数来确定是否继续分割,本例的分割三角面片数为500个

 

主:应为每次为子节点添加实际的三角面片时,都需遍历整个网格,为了提高效率,了一个标记为存储已经归档的是三角顶点,在顶点遍历的过程中提前进行筛选已经遍历过的顶点

顶点索引很好判断:原始的顶点索引是以第一个顶点确定三角面片,索引只要知道某一个顶点在包围盒内,后边连续两个索引即为组成三角面片的三个索引。

 

这样顶点编译进四叉树中了


进入渲染

首先进入根节点:

1,判断当前视点是否在节点范围内,不在,则推出,不渲染

2,判断当前视角到节点的距离是否超过最小视距,如果是,退出渲染

这里提一下如何判断视点到节点(平面包围盒的距离)

:计算平面包围盒的四条线,依次判视点到线的距离,取其最短一点,

最短距离不是视点到包围盒中心平面的投影?

3  如果包围四个顶点到到视点的方向与视点方向的夹角超过82度,则退出渲染


渲染技术点:

点到线的最短距离判断

void QuadTree::ClosestPointOnLine(Vector3 *start, Vector3 *end, Vector3 *point, Vector3 *result)
{
    Vector3  c,                          // vector from a->p
            ev,                         // vector from a->b
            pv;                         // projection of a->p onto a->b

    float   d,                          // length of vector from a->b (distance from start to end of line)
            t;                          // amount of c that projects onto ev

    // Get vector from point to start of line
	c.x = point->x - start->x;
	//c.y = point->y - start->y;			//quick speedup. Quadtrees ignore Y!
	c.z = point->z - start->z;

	
    // Get edge vector
	ev.x = end->x - start->x;
	//ev.y = end->y - start->y;				//quick speedup. Quadtrees ignore Y!
	ev.z = end->z - start->z;

    

    // find length of ev
	d = sqrtf(ev.x*ev.x + ev.z*ev.z);		//quick speedup. Quadtrees ignore Y!
    
    // Normalise ev
    ev.x /= d;
    //ev.y /= d;							//quick speedup. Quadtrees ignore Y!
    ev.z /= d;

    // Calculate projection of vector from start to point (c) onto line (ev)
	t = (ev.x*c.x) + (ev.z*c.z);			//quick speedup. Quadtrees ignore Y!
    

    // Check that projection amount (t) is within range 0 < t < d
    // n.b. 0 = start of line, d = length of line
    if (t < 0)
    {
        // closest point is start of line
        *result = *start;
        return;
    }

    if (t > d)
    {
        // closest point is end of line
        *result = *end;
        return;
    }

    // Closest point is somewhere on the line
    // Calculate projected vector using amount & normalised edge vector
	pv.x = ev.x * t;
	//pv.y = ev.y * t;						//quick speedup. Quadtrees ignore Y!
	pv.z = ev.z * t;

	

    // Closest point on line is start + projected vector
	result->x = start->x + pv.x;
	//result->y = start->y + pv.y;			//quick speedup. Quadtrees ignore Y!
	result->y = 0.0f;
	result->z = start->z + pv.z;
}

节点视距的角度判断

bool QuadTree::NodeIsBehind(QNode *node, Vector3 *pos, Vector3 *at)
{
	//Calculate the angle from at, to node corners.

	Vector3 points[4], v;


	points[0].x = node->m_min.x;
	points[0].z = node->m_min.z;

	points[1].x = node->m_max.x;
	points[1].z = node->m_min.z;

	points[2].x = node->m_min.x;
	points[2].z = node->m_max.z;

	points[3].x = node->m_max.x;
	points[3].z = node->m_max.z;



	for(int i=0; i<4; i++)
	{
		v.x = points[i].x - pos->x;
		v.z = points[i].z - pos->z;

		//normalize.
		float mag = (v.x * v.x) + (v.z * v.z);
		mag = 1.0f / sqrtf(mag);

		v.x *= mag;
		v.z *= mag;

		//do dot product.
		float dot = (v.x * at->x) + (v.z * at->z);

		float angle = (float) acos(dot);

		angle *= 180.0f / 3.14592f;

		if(angle < 82.0f)
		{
			return false;
		}
	}

	return true;

}

视点到节点包围盒的距离

float QuadTree::DistFromPoint(QNode *node, Vector3 *pos)
{//This function calcs the distance between pos, and the node, at the closest point, checks all 4 lines aroun
 //bounding box of node.

	Vector3 v, points[4], p;
	float distance[4], ret_dist;
	int i;


	//Get the 4 points of the bounding box.
	points[0].x = node->m_min.x;			points[0].z = node->m_min.z;
	points[1].x = node->m_max.x;			points[1].z = node->m_max.z;

	points[2].x = node->m_min.x;			points[2].z = node->m_max.z;
	points[3].x = node->m_max.x;			points[3].z = node->m_min.z;


	// speedup hacks here, by ignoring Y.
	ClosestPointOnLine(&points[0], &points[3], pos, &p);
	v.x = p.x - pos->x;
	v.z = p.z - pos->z;
	distance[0] = (v.x * v.x) + (v.z * v.z);

	ClosestPointOnLine(&points[0], &points[2], pos, &p);
	v.x = p.x - pos->x;
	v.z = p.z - pos->z;
	distance[1] = (v.x * v.x) + (v.z * v.z);

	ClosestPointOnLine(&points[1], &points[3], pos, &p);
	v.x = p.x - pos->x;
	v.z = p.z - pos->z;
	distance[2] = (v.x * v.x) + (v.z * v.z);

	ClosestPointOnLine(&points[2], &points[1], pos, &p);
	v.x = p.x - pos->x;
	v.z = p.z - pos->z;
	distance[3] = (v.x * v.x) + (v.z * v.z);


	ret_dist = distance[0];

	for(i=1; i<4; i++)
	{
		if(distance[i] < ret_dist)
		{
			ret_dist = distance[i];
		}
		
	}

	ret_dist = sqrtf(ret_dist);			//Square root it.
	/*printf("\n distance to camera = %f ", ret_dist);*/
	return ret_dist;
}

主要做了什么:

1 以四叉树结构来组织顶点数据,三角数据

2 通过视点与四叉树的包围盒进行数据筛选,优化加载

 

缺点:

1,没有LOD

2,筛选判断过于简单主要体现在节点包围盒与视距的距离判断,尤其是节点是否在视距范围内的判断

3,数据是一次性载入内存的,对于大数据不吭能叫所有顶点都放入内存中



你可能感兴趣的:(原创)