Намалювання широкої лінії в один піксель у тривимірному просторі


10

Я хотів би намалювати лінію в 3D-просторі, який завжди рівно один піксель на екрані, незалежно від того, наскільки це далеко від камери. (І те саме для одиничних балів теж).

Будь-які підказки, як я міг це зробити?

Відповіді:


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методу, який повинен бути представлений як a 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використовуючи метод , описаний тут . У цьому випадку вам просто потрібно буде знайти координату простору екрану для обох кінців тривимірної лінії (ще раз використовуючи 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);
}

Далі виводить його як звичайний примітивний рядок.


Демонстрація

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

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


А як щодо лінії з однаковою початковою і кінцевою точкою, чи це не призведе до точки? Або просто було б непомітним?
aaaaaaaaaaaa

@eBusiness Добре запитання! Я просто спробував це, і справді це нічого не робить.
Девід Гувейя

@eBusiness Я щойно знайшов спосіб, але думаю, що це трохи нерозумно. Я додамо його для повноти все одно.
Девід Гувейя

2

Це позначено як XNA, тому я припускаю, що саме про це ви питаєте?

Якщо так, ця стаття повинна бути корисною для рядків:

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

Звичайно, ви можете використовувати власні матриці перегляду / проекції замість їхніх. В основному, PrimitiveType.LineListабо LineStripяк малювати лінії на рівні GPU.

Що стосується очок, ви більше не можете використовувати PrimitiveType.PointList для малювання очок у XNA 4.0. Натомість вам доведеться скласти дуже маленькі трикутники. Цей зразок забезпечує хорошу базову лінію:

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

Для попередніх версій XNA, якщо ви використовуєте одну з цих, ви можете продовжити і прочитати точку частини версії XNA 3.0 статті, розміщеної вище:

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

Зверніть увагу, що посилання має:

graphics.GraphicsDevice.RenderState.PointSize = 10;

Очевидно змінити це на 1.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.