Я хотів би намалювати лінію в 3D-просторі, який завжди рівно один піксель на екрані, незалежно від того, наскільки це далеко від камери. (І те саме для одиничних балів теж).
Будь-які підказки, як я міг це зробити?
Я хотів би намалювати лінію в 3D-просторі, який завжди рівно один піксель на екрані, незалежно від того, наскільки це далеко від камери. (І те саме для одиничних балів теж).
Будь-які підказки, як я міг це зробити?
Відповіді:
Лінії візуалізації - метод 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. Використовуваний код - це майже все, що я написав вище. Я також збільшив масштаб, щоб ви могли підтвердити, що вони мають рівно один піксель:
Це позначено як 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
.