一个四叉树读书笔记
源代码: 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 个索引来表示。
值得一提的是本例子采用的三角形索引顶点存储顺序如图所示:
三角形索引序列为: 以一个网格为例 右上角三角形(i,j),(i+1,j+1) , (i+1,j+0),依次类推…..
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,数据是一次性载入内存的,对于大数据不吭能叫所有顶点都放入内存中