Get screen space coordinates of specific verticies of a 3D model when visible
I'd like to render a model, then, if particular verticies (how would i mark them?) are visible, i want to render something 2D where they are.
开发者_如何学运维How would i go about doing this?
First of all, you need your vertex position as a Vector4
(perspective projections require the use of homogeneous coordinates; set W = 1
). I'll assume you know which vertex you want and how to get its position. Its position will be in model space.
Now simply transform that point to projection space. That is - multiply it by your World-View-Projection matrix. That is:
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;
Now your result will be in projection space, which is (-1,-1) in the bottom left corner of the screen, and (1,1) in the top right corner. If you want to get your position in client space (which is what SpriteBatch
uses), then simply transform it using the inverse of a matrix that matches the implicit View-Projection matrix used by 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);
Disclaimer: I haven't tested any of this code.
(Obviously, to check if a particular vertex is visible or not, simply check if it is in the (-1,-1) to (1,1) range in projection space.)
Take a look at the occlusion culling features of your engine. For XNA, you can consult the framework guide (with samples) here.
http://roecode.wordpress.com/2008/02/18/xna-framework-gameengine-development-part-13-occlusion-culling-and-frustum-culling/
Probably the best way to do this is with a BoundingFrustrum
. It's basically like a rectangle that expands out in a certain direction, similar to how a player's camera works. Then, you can check to see if the given point you want is contained in the BoundingFrustrum, and if it is, then render your object.
Basically, the shape it makes looks like this:
Example:
// 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);
}
The example is actually for culling all objects in the game, but it can be pretty easily modified to handle what you want to do.
Example source: http://nfostergames.com/Lessons/SimpleViewCulling.htm
To get your world coordinates into screen space, take a look at Viewport.Project.
精彩评论