Рисование линии шириной в один пиксель в трехмерном пространстве

10

Я хотел бы нарисовать линию в трехмерном пространстве, которая всегда имеет ширину в один пиксель на экране, независимо от того, насколько далеко она находится от камеры. (И то же самое для отдельных точек тоже).

Любые намеки на то, как я мог это сделать?

danbystrom
источник

Ответы:

24

Линии рендеринга - Метод 1 (Примитивы)

Для простых линий в трехмерном пространстве вы можете нарисовать их, используя LineListили LineStripпримитив. Вот минимальный объем кода, который вы должны добавить в пустой проект 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.

Конечно, есть много способов его оптимизации, большинство из которых включает создание a VertexBufferдля хранения всех вершин и пакетирование как можно большего количества строк в один вызов Draw, но это не имеет отношения к вопросу.


Точки рендеринга - метод 1 (SpriteBatch)

Что касается рисования точек, раньше было легко использовать точечные спрайты, но они были удалены из XNA 4.0 . Хотя есть несколько альтернатив. Самый простой способ - создать белый Texture2Dобъект 1x1 и отобразить его, используя 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 (Малая линия с примитивами)

В комментариях eBusiness поднял следующий вопрос:

Как насчет линии с одинаковыми начальной и конечной точкой, разве это не даст точку? Или это будет просто невидимым?

Я попробовал, и рендеринг с 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);
}

Затем следует преобразовать его в обычный примитив строки.


демонстрация

Вот что я получил, нарисовав белую линию в трехмерном пространстве, используя примитив списка линий, и красные точки на обоих концах линии, используя текстуру 1x1 и SpriteBatch. Используемый код в значительной степени соответствует тому, что я написал выше. Я также увеличил масштаб, чтобы вы могли убедиться, что они имеют ширину ровно в один пиксель:

введите описание изображения здесь

Дэвид Гувея
источник
Как насчет линии с одинаковыми начальной и конечной точкой, разве это не даст точку? Или это будет просто невидимым?
аааааааааааа
@eBusiness Хороший вопрос! Я только что попробовал, и на самом деле это ничего не делает.
Дэвид Гувейя
@eBusiness Я только что нашел способ, но я думаю, что это немного глупо. Я добавлю это для полноты в любом случае.
Дэвид Гувея
2

Это помечено как XNA, так что я полагаю, это то, о чем вы спрашиваете?

Если это так, эта статья должна быть полезна для строк:

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

Конечно, вы можете использовать свои собственные матрицы вида / проекции вместо их. В основном, PrimitiveType.LineListили LineStripкак рисовать линии на уровне графического процессора.

Что касается точек, вы больше не можете использовать PrimitiveType.PointList для рисования точек в XNA 4.0. Вместо этого вам придется сделать очень маленькие треугольники. Этот образец обеспечивает хорошую основу:

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

В предыдущих версиях XNA, если вы используете одну из них, вы можете прочитать часть Point статьи XNA 3.0, опубликованной выше:

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

Обратите внимание, что ссылка имеет:

graphics.GraphicsDevice.RenderState.PointSize = 10;

Очевидно, изменить это на 1.


источник