在3D空间中绘制一条像素宽的线


10

我想在3D空间中画一条线,无论距相机有多远,该线始终在屏幕上始终只有一个像素宽。(单点也一样)。

关于我该怎么做的任何提示?

Answers:


24

渲染线-方法1(原语)

对于3D空间中的简单线条,您可以使用LineListLineStrip图元进行绘制。这是添加到空XNA项目中才能使它从(0,0,0)到(0,0,-50)的线的最少代码量。无论相机位于何处,该线的宽度应看起来大致相同。

// Inside your Game class
private BasicEffect basicEffect;
private Vector3 startPoint = new Vector3(0, 0, 0);
private Vector3 endPoint = new Vector3(0, 0, -50);

// Inside your Game.LoadContent method
basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.View = Matrix.CreateLookAt(new Vector3(50, 50, 50), new Vector3(0, 0, 0), Vector3.Up);
basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f), GraphicsDevice.Viewport.AspectRatio, 1f, 1000f);

// Inside your Game.Draw method
basicEffect.CurrentTechnique.Passes[0].Apply();
var vertices = new[] { new VertexPositionColor(startPoint, Color.White),  new VertexPositionColor(endPoint, Color.White) };
GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, vertices, 0, 1);

我基本上创建了一个简单的BasicEffect视图和投影转换,并将两个顶点(存储位置和颜色)传递给该GraphicsDevice.DrawUserPrimitives方法以将其渲染为LineList

当然,有很多方法可以对其进行优化,其中大多数方法涉及创建一个VertexBuffer存储所有顶点的方法,以及将尽可能多的线批处理到一个Draw调用中,但这与问题无关。


渲染点-方法1(SpriteBatch)

至于绘制点,以前使用点精灵很容易,但是它们已从XNA 4.0中删除。虽然有一些替代方法。最简单的方法是创建一个1x1白色Texture2D对象,并SpriteBatch在正确的屏幕位置使用渲染,您可以使用Viewport.Project方法轻松找到它。

您可以这样创建所需的Texture2D对象:

Texture2D pixel = new Texture2D(GraphicsDevice, 1, 1);
pixel.SetData(new [] { Color.White });

并在位置(x,y,z)处进行渲染,如下所示:

// Find screen equivalent of 3D location in world
Vector3 worldLocation = new Vector3(0, 0, 50);
Vector3 screenLocation = GraphicsDevice.Viewport.Project(worldLocation, projectionMatrix, viewMatrix, Matrix.Identity);

// Draw our pixel texture there
spriteBatch.Begin();
spriteBatch.Draw(pixel, new Vector2(screenLocation.X, screenLocation.Y), Color.White);
spriteBatch.End();

渲染线-方法2(SpriteBatch)

另外,您也可以SpriteBatch使用此处描述技术使用画线。在这种情况下,您只需要找到3D线两端的屏幕空间坐标(再次使用Viewport.Project),然后在它们之间绘制一条规则线即可。


渲染点-方法2(带有基元的小线)

在评论中,电子商务提出了以下问题:

起点和终点相同的线会不会产生一个点呢?还是仅仅是看不见?

我尝试了一下,并LineList使用相同的起点和终点进行渲染,结果什么也没画。不过,我找到了解决方法,因此在这里我将对其进行完整描述。

诀窍不是使用相同的起点和终点,而是绘制一条很小的线,以使其在绘制时仅显示为一个像素。因此,为了选择正确的端点,我首先将世界空间点投影到屏幕空间中,将其在屏幕空间中向右移动一个像素,最后将其投影回到世界空间中。那是您的线条的终点,以使其看起来像点。像这样:

Vector3 GetEndPointForDot(Vector3 start)
{
    // Convert start point to screen space
    Vector3 screenPoint = GraphicsDevice.Viewport.Project(start, projection, view, Matrix.Identity);

    // Screen space is defined in pixels so adding (1,0,0) moves it right one pixel
    screenPoint += Vector3.Right;

    // Finally unproject it back into world space
    return GraphicsDevice.Viewport.Unproject(screenPoint, projection, view, Matrix.Identity);
}

随后将其渲染为普通线图元。


示范

这就是我使用线列表图元在3D空间中绘制白线,并使用1x1纹理和SpriteBatch在线的两端绘制红点的方法。所使用的代码几乎是我上面编写的代码。我也放大了,因此您可以确认它们恰好是一个像素宽:

在此处输入图片说明


起点和终点相同的线会不会产生一个点呢?还是仅仅是看不见?
aaaaaaaaaaaa 2012年

@eBusiness好问题!我只是尝试了一下,实际上它没有任何效果。
David Gouveia 2012年

@eBusiness我只是找到了一种方法,但是我认为这有点愚蠢。无论如何,我都会添加它以保持完整性。
David Gouveia 2012年

2

这被标记为XNA,所以我假设这就是您要问的?

如果是这样,那么本文对以下几行应该有所帮助:

http://msdn.microsoft.com/zh-CN/library/bb196414(v=xnagamestudio.40).aspx

当然,您可以使用自己的视图/投影矩阵来代替它们。基本上PrimitiveType.LineList还是LineStrip在GPU级别上画线。

至于点,您不再可以在XNA 4.0中使用PrimitiveType.PointList绘制点。相反,您必须制作非常小的三角形。该示例提供了一个良好的基准:

http://create.msdn.com/education/catalog/sample/primitives_3d

对于早期版本的XNA,如果您使用的是其中一种,则可以继续阅读上述文章的XNA 3.0版本的Point部分:

http://msdn.microsoft.com/zh-CN/library/bb196414(v=xnagamestudio.30).aspx

请注意,该链接具有:

graphics.GraphicsDevice.RenderState.PointSize = 10;

显然将其更改为1

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.