VTK 碰撞检测 功能实现有几种方式:
1, 求物体的包围盒,然后检测包围盒与线、面、或者另一个包围盒是否相交。包围盒主要包括球体、轴对齐包围盒(AABB)、有向包围盒(OBB)和凸包(Convex Hull),
2,VTK自带了一个vtkCollisionDetectionFilter过滤器,来计算碰撞检测,此过滤器利用的就是OBB包围盒,使用简单,只需提供两个物体的数据集和矩阵即可。
3.判断polydta1中的点是否在polydata2曲面的内部,如果在内部即发生碰撞,如果所有点都不在内部即两个polydata没有发生碰撞。原理类似于点是否在面内的判断方法,以点得到一条线段,判断其与面的交点,依据该点两侧交点个数的奇偶性判断,如果两侧交点均为偶数则在面外,如果为奇数则在面内 ;
1. 直接使用包围盒
好处: 简单快速,可能精度不高
import vtk
import vtk
def do_bounding_boxes_intersect(bounds1, bounds2):
# 检查每个轴是否分离
for i in range(3):
if bounds1[2 * i] > bounds2[2 * i + 1] or bounds1[2 * i + 1] < bounds2[2 * i]:
return False
return True
# 创建两个示例的 vtkPolyData 对象
sphere1 = vtk.vtkSphereSource()
sphere1.SetCenter(0, 0, 0)
sphere1.SetRadius(5)
sphere1.Update()
polydata1 = sphere1.GetOutput()
sphere2 = vtk.vtkSphereSource()
sphere2.SetCenter(8, 0, 0)
sphere2.SetRadius(5)
sphere2.Update()
polydata2 = sphere2.GetOutput()
# 获取包围盒
bounds1 = [0] * 6
bounds2 = [0] * 6
polydata1.GetBounds(bounds1)
polydata2.GetBounds(bounds2)
# 打印包围盒范围
print(f"Bounds 1: {bounds1}")
print(f"Bounds 2: {bounds2}")
# 检查包围盒是否相交
if do_bounding_boxes_intersect(bounds1, bounds2):
print("The bounding boxes intersect.")
else:
print("The bounding boxes do not intersect.")
# 创建一个mapper和actor来显示包围盒
bbox_mapper = vtk.vtkPolyDataMapper()
bbox_mapper.SetInputData(polydata1)
bbox_actor = vtk.vtkActor()
bbox_actor.SetMapper(bbox_mapper)
bbox_actor.GetProperty().SetColor(1, 0, 0) # 设置颜色为红色
# 创建一个mapper和actor来显示包围盒
bbox_mapper2 = vtk.vtkPolyDataMapper()
bbox_mapper2.SetInputData(polydata2)
bbox_actor2 = vtk.vtkActor()
bbox_actor2.SetMapper(bbox_mapper2)
bbox_actor2.GetProperty().SetColor(0, 1, 0) # 设置颜色为红色
# 创建一个renderer和renderWindow来显示结果
renderer = vtk.vtkRenderer()
renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderer.AddActor(bbox_actor)
renderer.AddActor(bbox_actor2)
renderWindow.Render()
renderWindowInteractor.Start()
输出:
The bounding boxes intersect.
2. vtkCollisionDetectionFilter
好处: 计算较多,有耗时长
#!/usr/bin/env python
import time
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingFreeType
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonMath import vtkMatrix4x4
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersModeling import vtkCollisionDetectionFilter
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer,
vtkTextActor
)
def get_program_parameters():
import argparse
description = 'Collision detection.'
epilogue = '''
This examples uses vtkCollisionDetectionFilter to find the intersection between a
fixed (solid white) and moving (red wireframe) sphere.
The animation places the moving sphere some distance from the fixed sphere and
moves the moving sphere until it contacts the fixed sphere.
Three collision modes are available and can be set as the first argument on the command line.
1. All contacts (0) finds all the contacting cell pairs with two points per collision.
2. First contact (1) quickly find the first contact point.
3. Half contacts (2) finds all the contacting cell pairs with one points per collision.
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('contactMode', nargs='?', default=0, type=int, help='Contact mode 0 (default), 1, or 2.')
args = parser.parse_args()
return args.contactMode
def main():
contactMode = get_program_parameters()
# Define colors
colors = vtkNamedColors()
sphere0 = vtkSphereSource()
sphere0.SetRadius(0.29)
sphere0.SetPhiResolution(31)
sphere0.SetThetaResolution(31)
sphere0.SetCenter(0.0, 0, 0)
sphere1 = vtkSphereSource()
sphere1.SetPhiResolution(30)
sphere1.SetThetaResolution(30)
sphere1.SetRadius(0.3)
matrix1 = vtkMatrix4x4()
transform0 = vtkTransform()
collide = vtkCollisionDetectionFilter()
collide.SetInputConnection(0, sphere0.GetOutputPort())
collide.SetTransform(0, transform0)
collide.SetInputConnection(1, sphere1.GetOutputPort())
collide.SetMatrix(1, matrix1)
collide.SetBoxTolerance(0.0)
collide.SetCellTolerance(0.0)
collide.SetNumberOfCellsPerNode(2)
if contactMode == 0:
collide.SetCollisionModeToAllContacts()
elif contactMode == 1:
collide.SetCollisionModeToFirstContact()
else:
collide.SetCollisionModeToHalfContacts()
collide.GenerateScalarsOn()
# Visualize
mapper1 = vtkPolyDataMapper()
mapper1.SetInputConnection(collide.GetOutputPort(0))
mapper1.ScalarVisibilityOff()
actor1 = vtkActor()
actor1.SetMapper(mapper1)
actor1.GetProperty().BackfaceCullingOn()
actor1.SetUserTransform(transform0)
actor1.GetProperty().SetDiffuseColor(colors.GetColor3d("Tomato"))
actor1.GetProperty().SetRepresentationToWireframe()
mapper2 = vtkPolyDataMapper()
mapper2.SetInputConnection(collide.GetOutputPort(1))
actor2 = vtkActor()
actor2.SetMapper(mapper2)
actor2.GetProperty().BackfaceCullingOn()
actor2.SetUserMatrix(matrix1)
mapper3 = vtkPolyDataMapper()
mapper3.SetInputConnection(collide.GetContactsOutputPort())
mapper3.SetResolveCoincidentTopologyToPolygonOffset()
actor3 = vtkActor()
actor3.SetMapper(mapper3)
actor3.GetProperty().SetColor(colors.GetColor3d("Black"))
actor3.GetProperty().SetLineWidth(3.0)
txt = vtkTextActor()
txt.GetTextProperty().SetFontSize(18)
renderer = vtkRenderer()
renderer.UseHiddenLineRemovalOn()
renderer.AddActor(actor1)
renderer.AddActor(actor2)
renderer.AddActor(actor3)
renderer.AddActor(txt)
renderer.SetBackground(colors.GetColor3d("Gray"))
renderer.UseHiddenLineRemovalOn()
renderWindow = vtkRenderWindow()
renderWindow.SetSize(640, 480)
renderWindow.AddRenderer(renderer)
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)
# Move the first object
numSteps = 100
dx = 1.0 / float(numSteps) * 2.0
transform0.Translate(-numSteps * dx - .3, 0.0, 0.0)
renderWindow.Render()
renderer.GetActiveCamera().Azimuth(-60)
renderer.GetActiveCamera().Elevation(45)
renderer.GetActiveCamera().Dolly(1.2)
renderWindow.SetWindowName('CollisionDetection')
renderWindow.Render()
for i in range(0, numSteps):
transform0.Translate(dx, 0.0, 0.0)
renderer.ResetCameraClippingRange()
s = '{:s}: Number of contact cells is {:d}'.format(collide.GetCollisionModeAsString(),
collide.GetNumberOfContacts())
txt.SetInput(s)
renderWindow.Render()
if collide.GetNumberOfContacts() > 0:
break
# The total animation time is 3 seconds
time.sleep(3.0 / numSteps)
renderer.ResetCamera()
renderWindow.Render()
renderWindow.Render()
interactor.Start()
if __name__ == '__main__':
main()
3. 基于vtkSelectEnclosedPoints
vtkSelectEnclosedPoints
可以用来检查一个点集是否被另一个 vtkPolyData
完全包围。虽然它不是专门为碰撞检测设计的,但可以在某些情况下使用它来检查两个对象之间的包围关系。以下是一个示例,展示如何使用 vtkSelectEnclosedPoints
来检测两个 vtkPolyData
对象是否相交(假设一个对象包围另一个对象的某些点)
import vtk
# 创建两个示例的 vtkPolyData 对象
sphere1 = vtk.vtkSphereSource()
sphere1.SetCenter(0, 0, 0)
sphere1.SetRadius(5)
sphere1.Update()
polydata1 = sphere1.GetOutput()
sphere2 = vtk.vtkSphereSource()
sphere2.SetCenter(8, 0, 0)
sphere2.SetRadius(5)
sphere2.Update()
polydata2 = sphere2.GetOutput()
# 获取 polydata2 的点集
points = polydata2.GetPoints()
# 创建 vtkSelectEnclosedPoints 对象
enclosedPoints = vtk.vtkSelectEnclosedPoints()
enclosedPoints.SetInputData(polydata2)
enclosedPoints.SetSurfaceData(polydata1)
enclosedPoints.Update()
# 检查是否有点被包围
numEnclosedPoints = 0
for i in range(points.GetNumberOfPoints()):
if enclosedPoints.IsInside(i):
numEnclosedPoints += 1
if numEnclosedPoints > 0:
print("The objects intersect.")
print(f"Number of enclosed points: {numEnclosedPoints}")
else:
print("The objects do not intersect.")
输出:
The objects intersect.
Number of enclosed points: 2