在我想用来演示我的问题的例子中,我有一个涡轮机和一个小网格模型,如下图所示。模型的边界框显示为红色框。我试着检查绿色网格中的哪些节点位于涡轮机的边界框上方。如果一个节点位于边界框之上,我将其向下移动一点,这样我就可以清楚地看到哪些节点在边界框上方,哪些节点不在边界框内。
正如您在第一张图片中看到的那样,它对于我的非旋转模型非常适用。但是,如果我将模型旋转-90°,则所有命中测试都会失败。我不明白为什么会这样!在进行“拣货”的点击测试时,我遇到了同样的问题。
这是我用来检查节点是否在任何边界框之上的小函数。对于此任务,我为每个指向底部的节点创建一条光线。然后,我检查这条光线与我所有模型的所有位置相交。 (如果我有多个涡轮机,我只有涡轮机模型,但它有多个位置。这使得在GPU上渲染速度更快。)
foreach (var node in level.VisualGraph.Nodes)
{
SharpDX.Vector3 upperVector = new SharpDX.Vector3(node.Position.X, 1.0f, node.Position.Z);
SharpDX.Ray ray = new SharpDX.Ray(upperVector, new SharpDX.Vector3(0.0f, -1.0f, 0.0f));
foreach (var model in level.Models)
{
foreach (var position in model.Positions)
{
if (position.BoundingBox.GeometricBoundingBox.Intersects(ref ray))
{
node.Position = new SharpDX.Vector3(node.Position.X, 0.6f, node.Position.Z);
break;
}
}
}
}
如前所述,每个模型可以在场景中多次出现,因此我只存储每个模型一次并计算每个模型的位置变换。因此,模型的每个位置都有一个边界框。
我用于命中测试的'GeometricBoundingBox'是 SharpDX.BoundingBox 类型。当我向模型添加新位置时,我通过检查模型的所有顶点的最小值和最大值来创建一个新的边界框。
bool isFirst = true;
foreach (var vertex in vertices)
{
if (isFirst)
{
_min = new SharpDX.Vector3(vertex.GetPosition().X, vertex.GetPosition().Y, vertex.GetPosition().Z);
_max = new SharpDX.Vector3(vertex.GetPosition().X, vertex.GetPosition().Y, vertex.GetPosition().Z);
isFirst = false;
}
else
{
_min.X = Math.Min(_min.X, vertex.GetPosition().X);
_min.Y = Math.Min(_min.Y, vertex.GetPosition().Y);
_min.Z = Math.Min(_min.Z, vertex.GetPosition().Z);
_max.X = Math.Max(_max.X, vertex.GetPosition().X);
_max.Y = Math.Max(_max.Y, vertex.GetPosition().Y);
_max.Z = Math.Max(_max.Z, vertex.GetPosition().Z);
}
}
第一次创建边界框时或者当父位置的世界矩阵发生变化时,我按如下方式计算我的'GeometricBoundingBox':
SharpDX.Vector4 testMin = Helper.Math.Multiply(new SharpDX.Vector4(_min, 1.0f), worldMatrix);
SharpDX.Vector4 testMax = Helper.Math.Multiply(new SharpDX.Vector4(_max, 1.0f), worldMatrix);
GeometricBoundingBox = new SharpDX.BoundingBox(new SharpDX.Vector3(testMin.X, testMin.Y, testMin.Z), new SharpDX.Vector3(testMax.X, testMax.Y, testMax.Z));
用于转换位置模型的世界矩阵也用于计算边界框,如上所示。我这样计算:
public void UpdateWorldMatrix()
{
SharpDX.Matrix rotationMatrix = SharpDX.Matrix.RotationYawPitchRoll(RotationY, RotationX, RotationZ);
// We need to calculate the transformation matrix once and apply it for the initial offset before we can calculate the final matrix.
SharpDX.Matrix initialMatrix = SharpDX.Matrix.Scaling(Scaling * _parentModel.InitialScaling) * rotationMatrix;
SharpDX.Vector3 initialTranslation = Helper.Converters.ToVector(Helper.Math.Multiply2(_parentModel.InitialTranslation, initialMatrix));
// Calculate world matrix and update bounding box.
WorldMatrix = SharpDX.Matrix.Scaling(Scaling * _parentModel.InitialScaling) * rotationMatrix * SharpDX.Matrix.Translation(Position + Translation + initialTranslation);
if (ParentStaticPosition != null)
WorldMatrix = SharpDX.Matrix.Multiply(WorldMatrix, ParentStaticPosition.WorldMatrix);
BoundingBox.ReBuild(WorldMatrix);
}
两个模型中的边界框完全适合我的涡轮机模型。当模型在GPU上针对每个位置进行转换时,边界框将通过上面显示的代码进行转换。由于模型和边界拟合,我相信计算它们是有效的。
但是我不明白为什么我的命中测试适用于非旋转对象,但我不适用于旋转的对象。
你有什么想法吗?
答案 0 :(得分:1)
我可以为光线投射做的一件事是将光线转换为模型空间,而不是将边界框转换为方向。这是通过获取模型方向矩阵的逆矩阵并将矢量的起点和终点乘以逆来完成的。然后在模型空间中创建光线并将其投射到模型空间aabb。
如果您通过矩阵定位您的aabb,这将无法正常工作。您需要通过将模型中的每个顶点与方向矩阵相乘来重新计算新的aabb,然后重新计算aabb。我相信你正在做的事情,但我相信你正在改造的边界框可能没有正确的最大最小范围。