旋转模型后,SharpDX 中的命中测试不起作用
Hit tests in SharpDX do not work after rotating a model
在我想用来演示我的问题的示例中,我有一个涡轮机模型和一个小网格,如下图所示。模型的边界框显示为红色框。我尝试检查绿色网格中的哪些节点位于涡轮机边界框上方。如果一个节点位于边界框上方,我将它向下移动一点,这样我可以清楚地看到哪些节点在边界框上方,哪些不在边界框上方。
正如您在第一张图片中看到的,它非常适合我的非旋转模型。但是,如果我将模型旋转 -90°,则所有命中测试都会失败。我不明白为什么会这样!我在对 'Picking'.
进行命中测试时遇到了同样的问题
这是我用来检查节点是否在任何边界框上方的小函数。对于此任务,我为每个指向底部的节点创建了一条射线。然后,我检查这条射线是否与我所有模型的所有位置相交。 (如果我有多个涡轮机,我只会有一个涡轮机模型,但是它的多个位置。这使得在 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 上针对每个位置进行转换时,边界框由上面显示的代码进行转换。由于模型和边界拟合,我相信计算它们是有效的。
但我不明白为什么我的命中测试对非旋转对象有效,但对旋转对象无效。
你有什么想法吗?
我可以为光线投射做的一件事是将光线转换为模型 space 而不是将边界框转换为您的方向。这是通过采用模型方向矩阵的逆矩阵并将向量的起点和终点乘以逆矩阵来完成的。然后在模型 space 中创建光线并将其投射到模型 space aabb.
如果你用矩阵来定位你的 aabb,这是行不通的。您需要通过将模型中的每个顶点与方向矩阵相乘来重新计算新的 aabb,然后重新计算 aabb。我相信这就是您正在做的,但我相信您正在转换的边界框可能没有正确的最大最小范围。
在我想用来演示我的问题的示例中,我有一个涡轮机模型和一个小网格,如下图所示。模型的边界框显示为红色框。我尝试检查绿色网格中的哪些节点位于涡轮机边界框上方。如果一个节点位于边界框上方,我将它向下移动一点,这样我可以清楚地看到哪些节点在边界框上方,哪些不在边界框上方。
正如您在第一张图片中看到的,它非常适合我的非旋转模型。但是,如果我将模型旋转 -90°,则所有命中测试都会失败。我不明白为什么会这样!我在对 'Picking'.
进行命中测试时遇到了同样的问题这是我用来检查节点是否在任何边界框上方的小函数。对于此任务,我为每个指向底部的节点创建了一条射线。然后,我检查这条射线是否与我所有模型的所有位置相交。 (如果我有多个涡轮机,我只会有一个涡轮机模型,但是它的多个位置。这使得在 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 上针对每个位置进行转换时,边界框由上面显示的代码进行转换。由于模型和边界拟合,我相信计算它们是有效的。
但我不明白为什么我的命中测试对非旋转对象有效,但对旋转对象无效。
你有什么想法吗?
我可以为光线投射做的一件事是将光线转换为模型 space 而不是将边界框转换为您的方向。这是通过采用模型方向矩阵的逆矩阵并将向量的起点和终点乘以逆矩阵来完成的。然后在模型 space 中创建光线并将其投射到模型 space aabb.
如果你用矩阵来定位你的 aabb,这是行不通的。您需要通过将模型中的每个顶点与方向矩阵相乘来重新计算新的 aabb,然后重新计算 aabb。我相信这就是您正在做的,但我相信您正在转换的边界框可能没有正确的最大最小范围。