碰撞檢測問題在虛擬現實、計算機輔助設計與制造、游戲及機器人等領域有着廣泛的應用,甚至成為關鍵技術。而包圍盒算法是進行碰撞干涉初步檢測的重要方法之一。包圍盒算法是一種求解離散點集最優包圍空間的方法。基本思想是用體積稍大且特性簡單的幾何體(稱為包圍盒)來近似地代替復雜的幾何對象。為物體添加包圍體的目的是快速的進行碰撞檢測或者進行精確的碰撞檢測之前進行過濾(即當包圍體碰撞,才進行精確碰撞檢測和處理)。包圍體類型包括球體、軸對齊包圍盒(AABB/Axis-aligned bounding box)、有向包圍盒(OBB/Oriented bounding box)以及凸殼/凸包(Convex Hull)等。
AABB是應用最早的包圍盒。它被定義為包含該對象,且邊平行於坐標軸的最小六面體。故描述一個AABB,僅需六個標量。AABB構造比較簡單,存儲空間小,但緊密性差,尤其對不規則幾何形體,冗余空間很大,當對象旋轉時,無法對其進行相應的旋轉。
包圍球被定義為包含該對象的最小的球體。確定包圍球,首先需分別計算組成對象的基本幾何元素集合中所有元素的頂點的x,y,z坐標的均值以確定包圍球的球心,再由球心與三個最大值坐標所確定的點間的距離確定半徑r。包圍球的碰撞檢測主要是比較兩球間半徑和與球心距離的大小。
OBB是較為常用的包圍盒類型。它是包含該對象且相對於坐標軸方向任意的最小的長方體。OBB最大特點是它的方向的任意性,這使得它可以根據被包圍對象的形狀特點盡可能緊密的包圍對象,但同時也使得它的相交測試變得復雜。OBB包圍盒比AABB包圍盒和包圍球更加緊密地逼近物體,能比較顯著地減少包圍體的個數,從而避免了大量包圍體之間的相交檢測。但OBB之間的相交檢測比AABB或包圍球體之間的相交檢測更費時。
在多維空間中有一群散布各處的點,凸包是包覆這群點的所有外殼當中,表表面積容積最小的一個外殼,而最小的外殼一定是凸的。
二維平面上的凸包是一個凸多邊形,在所有點的外圍繞一圈即得凸包。另外,最頂端、最底端、最左端、最右端的點,一定是凸包上的點。
在許多物理引擎或仿真軟件中進行碰撞檢測和接觸力計算時,會需要物體具有一個能代表其碰撞屬性的碰撞體(Each object must have a Collision Shape)。碰撞體的形狀可以是其真實的三維網格,但這樣會影響計算的實時性和效果。這時可以用更簡單的幾何形狀來代表要發生碰撞或接觸的物體,比如立方體、球體等。下面是幾種碰撞體形狀,從左到右為:球體、立方體、凸包、原始網格。前三種包圍體和原始網格相比顯得不那么精確,但是計算效率更高。
VTK提取凸包使用類vtkPointsProjectedHull,該類可以獲取任意點集的最小凸包。輸入為點集,輸出為包圍該點集的最小凸包輪廓點集。下面代碼以原點為球心隨機生成40個三維點,然后提取凸包並投影到Y-Z平面上(projection along the x axis)
import vtk pointSource = vtk.vtkPointSource() # By default location of the points is random within the sphere pointSource.SetNumberOfPoints(40) pointSource.Update() #Create a mapper and actor mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(pointSource.GetOutputPort()) pointActor = vtk.vtkActor() pointActor.SetMapper(mapper) # change the size of actor's points pointActor.GetProperty().SetPointSize(5) points = vtk.vtkPointsProjectedHull() points.DeepCopy(pointSource.GetOutput().GetPoints()) # Returns the number of points in the convex hull of the projection of the points down the positive x-axis xSize = points.GetSizeCCWHullX() print "xSize: " , xSize pts = 2 * xSize * [0] # Returns the coordinates (y,z) of the points in the convex hull of the projection of the points down the positive x-axis points.GetCCWHullX(pts, xSize) xHullPoints = vtk.vtkPoints() for i in range(xSize): yval = pts[2*i] zval = pts[2*i + 1] print "(y,z) value of point " , i , " : (" , yval, " , " , zval , ")" xHullPoints.InsertNextPoint(0.0, yval, zval) # Insert the first point again to close the loop xHullPoints.InsertNextPoint(0.0, pts[0], pts[1]) # Display the x hull xPolyLine = vtk.vtkPolyLine() xPolyLine.GetPointIds().SetNumberOfIds(xHullPoints.GetNumberOfPoints()) for i in range(xHullPoints.GetNumberOfPoints()): xPolyLine.GetPointIds().SetId(i, i) # Create a cell array to store the lines in and add the lines to it cells = vtk.vtkCellArray() cells.InsertNextCell(xPolyLine) # Create a polydata to store everything in polyData = vtk.vtkPolyData() # Add the points to the dataset polyData.SetPoints(xHullPoints) # Add the lines to the dataset polyData.SetLines(cells) # Setup actor and mapper xHullMapper = vtk.vtkPolyDataMapper() xHullMapper.SetInputData(polyData) xHullActor = vtk.vtkActor() xHullActor.SetMapper(xHullMapper) #Create a renderer, render window, and interactor renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) renderWindowInteractor = vtk.vtkRenderWindowInteractor() renderWindowInteractor.SetRenderWindow(renderWindow) # Add the actor to the scene renderer.AddActor(xHullActor) renderer.AddActor(pointActor) axesActor = vtk.vtkAxesActor() axesActor.SetConeRadius(0) renderer.AddActor(axesActor) # Rotate camera renderer.GetActiveCamera().ParallelProjectionOn() renderer.GetActiveCamera().Azimuth(90) renderer.ResetCamera() # Render and interact renderWindow.Render() style = vtk.vtkInteractorStyleTrackballCamera() renderWindowInteractor.SetInteractorStyle(style) renderWindowInteractor.Start()
輸出結果如下:
xSize: 12
(y,z) value of point 0 : ( -0.0582492426038 , -0.422939538956 )
(y,z) value of point 1 : ( 0.105404652655 , -0.416797012091 )
(y,z) value of point 2 : ( 0.384681999683 , -0.139498367906 )
(y,z) value of point 3 : ( 0.403645426035 , -0.0483181998134 )
(y,z) value of point 4 : ( 0.312736421824 , 0.160078570247 )
(y,z) value of point 5 : ( 0.0731690451503 , 0.45936870575 )
(y,z) value of point 6 : ( 0.0130856623873 , 0.45393627882 )
(y,z) value of point 7 : ( -0.090303093195 , 0.434131532907 )
(y,z) value of point 8 : ( -0.292154729366 , 0.340970009565 )
(y,z) value of point 9 : ( -0.374829471111 , -0.0632947012782 )
(y,z) value of point 10 : ( -0.348517596722 , -0.259341210127 )
(y,z) value of point 11 : ( -0.256385087967 , -0.352375864983 )
參考:
VTK/Examples/Cxx/PolyData/PointsProjectedHull
Picking with a physics library
Video Game Physics Tutorial - Part I: An Introduction to Rigid Body Dynamics
Video Game Physics Tutorial - Part II: Collision Detection for Solid Objects
Video Game Physics Tutorial - Part III: Constrained Rigid Body Simulation