当3D模型可见时,获取特定顶点的屏幕空间坐标。

7
我想渲染一个模型,如果特定的顶点(我该如何标记它们?)可见,我想在它们所在的位置上渲染一些2D内容。
我该怎么做呢?
3个回答

7
首先,您需要将顶点位置作为一个Vector4(透视投影需要使用齐次坐标;设置W = 1)。我假设您知道要获取哪个顶点以及如何获取其位置。其位置将在模型空间中。
现在只需将该点转换为投影空间。也就是说-将其乘以您的世界-视图-投影矩阵。那就是:
Vector4 position = new Vector4(/* your position as a Vector3 */, 1);
IEffectMatrices e = /* a BasicEffect or whatever you are using to render with */
Matrix worldViewProjection = e.World * e.View * e.Projection;
Vector4 result = Vector4.Transform(position, worldViewProjection);
result /= result.W;

现在您的结果将位于投影空间中,该空间在屏幕左下角为(-1,-1),右上角为(1,1)。如果您想要获取客户端空间中的位置(这是SpriteBatch使用的),那么只需使用与SpriteBatch所使用的隐式视图-投影矩阵相匹配的矩阵的逆变换即可。
Viewport vp = GraphicsDevice.Viewport;
Matrix invClient = Matrix.Invert(Matrix.CreateOrthographicOffCenter(0, vp.Width, vp.Height, 0, -1, 1));
Vector2 clientResult = Vector2.Transform(new Vector2(result.X, result.Y), invClient);

免责声明:我没有测试过这些代码。
(显然,要检查特定顶点是否可见,只需在投影空间中检查它是否在(-1,-1)到(1,1)的范围内。)

为了获得[1,-1]范围内的坐标,需要通过齐次分量进行除法运算。代码应该是:Vector4 aux = Vector4.Transform(new Vector4(position, 1), worldViewProjection); result.X = aux.X /aux.W; result.Y = aux.Y /aux.W; result.Z = aux.Z /aux.W;我尝试编辑安德鲁的回复,但被拒绝了... :( - Blau
你说得很对。我已经修改了我的答案以考虑到这一点。 - Andrew Russell

1

可能最好的方法是使用一个BoundingFrustrum。它基本上像一个矩形,向某个方向扩展,类似于玩家的相机工作方式。然后,您可以检查所需点是否包含在BoundingFrustrum中,如果是,则渲染您的对象。

基本上,它形成的形状看起来像这样: Shape of a BoundingFrustrum

示例:

// A view frustum almost always is initialized with the ViewMatrix * ProjectionMatrix
BoundingFrustum viewFrustum = new BoundingFrustum(ActivePlayer.ViewMatrix * ProjectionMatrix);

// Check every entity in the game to see if it collides with the frustum.
foreach (Entity sourceEntity in entityList)
{
    // Create a collision sphere at the entities location. Collision spheres have a
    // relative location to their entity and this translates them to a world location.
    BoundingSphere sourceSphere = new BoundingSphere(sourceEntity.Position,
                                  sourceEntity.Model.Meshes[0].BoundingSphere.Radius);

    // Checks if entity is in viewing range, if not it is not drawn
    if (viewFrustum.Intersects(sourceSphere))
        sourceEntity.Draw(gameTime);
}

这个例子实际上是用于剔除游戏中的所有对象,但可以很容易地修改以处理您想要执行的操作。

示例源代码:http://nfostergames.com/Lessons/SimpleViewCulling.htm

要将您的世界坐标转换为屏幕空间,请查看Viewport.Project


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接