zoukankan      html  css  js  c++  java
  • 3D空间中的AABB(轴向平行包围盒, Aixe align bounding box)的求法

    引言

                在前面的一篇文章中讲述了怎样通过模型的顶点来求的模型的包围球,而且还讲述了基本包围体除了包围球之外,还有AABB包围盒。在这一章,将讲述怎样依据模型的坐标求得它的AABB盒。




    表示方法

                 AABB盒的表示方法有非常多,总结起来有例如以下的三种情况:

                 Max-min表示法:使用一个右上角和左下角的点来唯一的定义一个包围体

                 Center-radious表示法:我们用center点来表示中点,radious是一个数组,保存了包围盒在x方向,y方向,z方向上的半径。

                  Min-Width表示方法:我们用min来定义左下角的点,使用width来保存在x,y,z方向上的长度。

                  不同的方法,他们的碰撞检測算法也会有所不同,而且不同的表示方法也会适用在不同的情形下。所以,大家自己设计的时候,须要谨慎考虑。

                  在本文中,将会使用的是Max-min表示方法,例如以下所看到的:

    class AABB
    {
       ....
    public:
        VECTOR3 max ;
        VECTOR3 min ;
    };
    
                在这样的表示方法之下,进行碰撞检測的代码例如以下所看到的:

    bool AABB::isCollided(AABB* a)
    {
    	if(max.x < a->min.x || min.x > a->max.x) return false ;
    	if(max.y < a->min.y || min.y > a->max.y) return false ;
    	if(max.z < a->min.z || min.z > a->min.z) return false ;
    
    	return true ;
    }// end for isCollided



    AABB盒构造

                构造AABB盒的方法有非常多种,有的非常easy,有的非常复杂,这里将介绍两种主要的构造方法,他们也非常的简单,easy掌握。

                 第一种是固定大小的AABB盒,这样的AABB盒在构造完成之后,无论被包围的物体怎么样的旋转,都不须要在进行又一次构造了。

                另外一种是比較紧凑的一种,利用X,Y和Z轴向上最长和最远的点来构造一个AABB盒。



    固定大小AABB盒

                正如上面说的那样,固定大小的AABB盒,它须要被包围的物体,无论怎么旋转,都还在这个包围体里面。所以,我们先为这个物体构造一个包围球体,然后在这个包围球体的基础上构建一个AABB盒。这样就行达到无论怎么旋转,都还在包围体里面。

                只是,为这个物体构建一个包围球相同也可以满足这种要求,所以,就有点鸡肋了。可是,在某些限制条件下,你无法使用包围球,那么就行使用这个方案来构建一个固定大小的AABB盒。

                这个算法的核心是怎样构建一个包围球体,而这个算法我在前面一章中已经讲述了,就不再反复,感兴趣的读者能够去看博客中3D空间包围球(Bounding Sphere)的求法 的文章。

               在有了包围球之后,我们通过例如以下的方法就行计算出固定大小的AABB盒了:

    void AABB::computeFixedAABB(Sphere *s)
    {
    	max.x = s->center.x + s->radious ;
    	max.y = s->center.y + s->radious ;
    	max.z = s->center.z + s->radious ;
    	min.x = s->center.x - s->radious ;
    	min.y = s->center.y - s->radious ;
    	min.z = s->center.z - s->radious ;
    }// end for computeFixedAABB
    



    紧凑点的AABB盒

               这个AABB盒的构造方法,是从顶点集中获取X,Y和Z方向上最远的和近期的点,然后利用他们来构建一个AABB盒。这样的方法也非常easy。我直接上代码来给大家解说:

    void AABB::computeAABBFromOriginalPointSet(VECTOR3* vertices, unsigned int vertex_num)
    {
    	unsigned int minX_i = 0 , maxX_i = 0 ;
    	extrameDistanceAlongDir(MAKE_VECTOR3(1,0,0), vertices, vertex_num, &minX_i, &maxX_i);
    	min.x = vertices[minX_i].x ;
    	max.x = vertices[maxX_i].x ;
    
    	unsigned int minY_i = 0 , maxY_i = 0;
    	extrameDistanceAlongDir(MAKE_VECTOR3(0,1,0),vertices, vertex_num, &minY_i, &maxY_i);
    	min.y = vertices[minY_i].y ;
    	max.y = vertices[maxY_i].y ;
    
    	unsigned int minZ_i = 0 , maxZ_i = 0;
    	extrameDistanceAlongDir(MAKE_VECTOR3(0,0,1),vertices, vertex_num, &minZ_i, &maxZ_i);
    	min.z = vertices[minZ_i].z ;
    	max.z = vertices[maxZ_i].z ;
    }// end for computeAABBFromOriginalPointSet
    
    void AABB::extrameDistanceAlongDir(VECTOR3 dir, VECTOR3* vertices, unsigned int vertex_num, unsigned int* min, unsigned int*max)
    {
    	float maxProj = FLT_MIN , minProj = FLT_MAX ;
    	for(unsigned int i = 0 ; i < vertex_num ; i ++)
    	{
    		float proj = 0 ;
    		Vec3Dot(proj, vertices[i], dir);
    
    		if(proj > maxProj)
    		{
    			maxProj = proj ;
    			*max = i ;
    		}
    
    		if(proj < minProj)
    		{
    			minProj = proj ;
    			*min = i ;
    		}
    	}// end for
    }// end for extrameDistanceAlongDir

               上面一共同拥有两个函数,第一个函数就是给用户调用的计算AABB盒的方法。用户仅仅须要将模型的顶点集传递进来就可以。

               第二个函数,是获取在指定的轴向上,哪个点在这个轴向上的投影是最长的和最短的。这个函数非常easy,仅仅须要调用一个点积Dot运算就行求出。

              当求出了在X,Y和Z轴向上投影最长和最短的6个点之后,我们就分别取他们相应的轴向上的坐标值来构成Max和min,这样一个AABB盒就构造完成了。是不是非常easy??




    AABB类

              以下是AABB的完整类:

    //--------------------------------------------------------------------------------------------------
    // declaration	: Copyright (c), by XJ , 2014 . All right reserved .
    // brief		: This file will define the Axie aligned bounding box.
    // author		: XJ
    // date			: 2014 / 6 / 22
    // file			: AABB.h
    // version		: 1.0
    //---------------------------------------------------------------------------------------------------
    #pragma once
    #include"XJMath.h"
    #include"Sphere.h"
    
    namespace XJCollision
    {
    	/**
    	* brief	: We use the Max-min representation
    	*/
    	class AABB
    	{
    	public:
    		AABB();
    		AABB(VECTOR3 min, VECTOR3 max);
    
    	public:
    		/**
    		* Compute the fixed AABB
    		*/
    		void computeFixedAABB(Sphere* s);
    
    		/**
    		* Compute the AABB from the original point set
    		*/
    		void computeAABBFromOriginalPointSet(VECTOR3 *vertices, unsigned int vertex_num);
    
    		/**
    		* Collision Detection between two AABB
    		*/
    		bool isCollided(AABB* a);
    
    	private:
    		/**
    		* Compute the least and most distance along the specific direction
    		*/
    		void extrameDistanceAlongDir(VECTOR3 dir, VECTOR3 * vertices, unsigned int vertex_num, unsigned int * min, unsigned int * max);
    
    	public:
    		VECTOR3 max ;
    		VECTOR3 min ;
    	};
    };

    #include"AABB.h"
    #include<cmath>
    #include<float.h>
    using namespace XJCollision ;
    
    AABB::AABB()
    	:max(),
    	min()
    {
    
    }
    
    AABB::AABB(VECTOR3 _max, VECTOR3 _min)
    {
    	max.x = _max.x ; max.y = _max.y ; max.z = _max.z ;
    	min.x = _min.x ; min.y = _min.y ; min.z = _min.z ;
    }
    
    void AABB::computeFixedAABB(Sphere *s)
    {
    	max.x = s->center.x + s->radious ;
    	max.y = s->center.y + s->radious ;
    	max.z = s->center.z + s->radious ;
    	min.x = s->center.x - s->radious ;
    	min.y = s->center.y - s->radious ;
    	min.z = s->center.z - s->radious ;
    }// end for computeFixedAABB
    
    void AABB::computeAABBFromOriginalPointSet(VECTOR3* vertices, unsigned int vertex_num)
    {
    	unsigned int minX_i = 0 , maxX_i = 0 ;
    	extrameDistanceAlongDir(MAKE_VECTOR3(1,0,0), vertices, vertex_num, &minX_i, &maxX_i);
    	min.x = vertices[minX_i].x ;
    	max.x = vertices[maxX_i].x ;
    
    	unsigned int minY_i = 0 , maxY_i = 0;
    	extrameDistanceAlongDir(MAKE_VECTOR3(0,1,0),vertices, vertex_num, &minY_i, &maxY_i);
    	min.y = vertices[minY_i].y ;
    	max.y = vertices[maxY_i].y ;
    
    	unsigned int minZ_i = 0 , maxZ_i = 0;
    	extrameDistanceAlongDir(MAKE_VECTOR3(0,0,1),vertices, vertex_num, &minZ_i, &maxZ_i);
    	min.z = vertices[minZ_i].z ;
    	max.z = vertices[maxZ_i].z ;
    }// end for computeAABBFromOriginalPointSet
    
    void AABB::extrameDistanceAlongDir(VECTOR3 dir, VECTOR3* vertices, unsigned int vertex_num, unsigned int* min, unsigned int*max)
    {
    	float maxProj = FLT_MIN , minProj = FLT_MAX ;
    	for(unsigned int i = 0 ; i < vertex_num ; i ++)
    	{
    		float proj = 0 ;
    		Vec3Dot(proj, vertices[i], dir);
    
    		if(proj > maxProj)
    		{
    			maxProj = proj ;
    			*max = i ;
    		}
    
    		if(proj < minProj)
    		{
    			minProj = proj ;
    			*min = i ;
    		}
    	}// end for
    }// end for extrameDistanceAlongDir
    
    bool AABB::isCollided(AABB* a)
    {
    	if(max.x < a->min.x || min.x > a->max.x) return false ;
    	if(max.y < a->min.y || min.y > a->max.y) return false ;
    	if(max.z < a->min.z || min.z > a->min.z) return false ;
    
    	return true ;
    }// end for isCollided

    程序实例

                以下的两种图,各自是使用了第一种和另外一种计算方法计算出来的包围盒:

     

  • 相关阅读:
    Fedora/CentOS/RHEL删除旧的内核
    Linux下使Shell命令脱离终端运行
    保持tmux窗口名更改后不变
    Centos7 修改ssh 默认端口号
    验证码生成代码
    Json帮助类代码
    Http请求代码
    cookies读写代码
    缓存读写代码
    数据读写 CommonCurd
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4489854.html
Copyright © 2011-2022 走看看