2015-11-28 5 views
3

Недавно я освоил MonoGame, и мне нравится библиотека. Однако, я, кажется, возникли некоторые проблемы с нанесением кривых БезьеРисование кривых Безье в MonoGame (XNA) производит царапающие линии

В результате, что мой код производит выглядит примерно так

Bad render

Посмотрите плохо, нет? Линии не являются гладкими.

Позвольте мне показать вам некоторые из кода:

//This is what I call to get all points between which to draw. 
public static List<Point> computeCurvePoints(int steps) 
{ 
    List<Point> curvePoints = new List<Point>(); 
    for (float x = 0; x < 1; x += 1/(float)steps) 
    { 
     curvePoints.Add(getBezierPointRecursive(x, pointsQ)); 
    } 
    return curvePoints; 
} 

//Calculates a point on the bezier curve based on the timeStep. 
private static Point getBezierPointRecursive(float timeStep, Point[] ps) 
{ 
    if (ps.Length > 2) 
    { 
     List<Point> newPoints = new List<Point>(); 
     for (int x = 0; x < ps.Length-1; x++) 
     { 
      newPoints.Add(interpolatedPoint(ps[x], ps[x + 1], timeStep)); 
     } 
     return getBezierPointRecursive(timeStep, newPoints.ToArray()); 
    } 
    else 
    { 
     return interpolatedPoint(ps[0], ps[1], timeStep); 
    } 
} 

//Gets the linearly interpolated point at t between two given points (without manual rounding). 
//Bad results! 
private static Point interpolatedPoint(Point p1, Point p2, float t) 
{ 
    Vector2 roundedVector = (Vector2.Multiply(p2.ToVector2() - p1.ToVector2(), t) + p1.ToVector2());   
    return new Point((int)roundedVector.X, (int)roundedVector.Y); 
} 

//Method used to draw a line between two points. 
public static void DrawLine(this SpriteBatch spriteBatch, Texture2D pixel, Vector2 begin, Vector2 end, Color color, int width = 1) 
{ 
    Rectangle r = new Rectangle((int)begin.X, (int)begin.Y, (int)(end - begin).Length() + width, width); 
    Vector2 v = Vector2.Normalize(begin - end); 
    float angle = (float)Math.Acos(Vector2.Dot(v, -Vector2.UnitX)); 
    if (begin.Y > end.Y) angle = MathHelper.TwoPi - angle; 
    spriteBatch.Draw(pixel, r, null, color, angle, Vector2.Zero, SpriteEffects.None, 0); 
} 

//DrawLine() is called as following. "pixel" is just a Texture2D with a single black pixel. 
protected override void Draw(GameTime gameTime) 
{ 
      GraphicsDevice.Clear(Color.CornflowerBlue); 

      spriteBatch.Begin();   
      for(int x = 0; x < curvePoints.Count-1; x++) 
      { 
       DrawExtenstion.DrawLine(spriteBatch, pixel, curvePoints[x].ToVector2(), curvePoints[x + 1].ToVector2(), Color.Black, 2); 
      } 
      spriteBatch.End(); 

      base.Draw(gameTime); 
} 

мне удалось сделать линию немного более гладкой, добавив некоторые ручной Math.Round() вызывает к моему методу interpolatedPoint

//Gets the linearly interpolated point at t between two given points (with manual rounding). 
//Better results (but still not good). 
private static Point interpolatedPoint(Point p1, Point p2, float t) 
{ 
    Vector2 roundedVector = (Vector2.Multiply(p2.ToVector2() - p1.ToVector2(), t) + p1.ToVector2());   
    return new Point((int)Math.Round(roundedVector.X), (int)Math.Round(roundedVector.Y)); 
} 

Это дает следующий результат:

Мне пришлось удалить одно изображение, так как Stackoverflow не позволяет мне использовать более двух ссылок

Есть ли способы, чтобы эта кривая была абсолютно гладкой? Возможно, проблема с методом DrawLine?

Заранее спасибо.

EDIT:

Хорошо, я сумел сделать кривой выглядеть намного лучше, делая все расчеты с Vector2Ds и только преобразовав его в точку, в тот момент, когда он должен быть нарисован

A lot smoother

он по-прежнему не является совершенным, хотя:/

+0

что ссылка на втором изображении? мы можем его отредактировать. Тем не менее, трудно сказать, что вы считаете «идеальным». Когда вы занимаетесь такими вещами, никогда не обходитесь до самого последнего момента. И если ваша поверхность рисования может сделать рисунок подпикселя, даже не округлите ее в конце - пусть холст обрабатывает место размещения. (И обычно вместо рисования отдельных точек вы отслеживаете «текущую» и «предыдущую» точку, а вместо этого рисуете линии между ними) –

+0

Выглядит хорошо. Вы, возможно, можете опубликовать свой ** текст ** в качестве ответа ниже. :) – MickyD

+1

Вы изучали использование BasicEffect вместо SpriteBatch? Я думаю, что это лучший способ пойти на этот вопрос. – craftworkgames

ответ

5

Как сказал Майк «Pomax» Kamermans, , кажется, были проблемы с 2D-поверхности, не позволяя субпикселей рисунок и тем самым вызывая проблемы округления

Следуя советам ремесленных мастеров, я адаптировал алгоритм для рисования кривой в 3D с помощью BasicEffect. Это также позволяет сглаживать, что сильно разглаживает кривую.

В результате получается следующее:

link

Намного лучше!

Благодарим вас за полезный совет!

EDIT:

Вот код, который я использовал для этого.

Я также хотел бы добавить, что эта страница (http://gamedevelopment.tutsplus.com/tutorials/create-a-glowing-flowing-lava-river-using-bezier-curves-and-shaders--gamedev-919) мне очень помогла при написании этого кода.

Также обратите внимание, что некоторые из имен, которые я использовал для определения методов, могут не иметь смысла или могут вводить в заблуждение.Это было то, что я быстро собрал вечером.

//Used for generating the mesh for the curve 
//First object is vertex data, second is indices (both as arrays) 
public static object[] computeCurve3D(int steps) 
{ 
    List<VertexPositionTexture> path = new List<VertexPositionTexture>(); 
    List<int> indices = new List<int>(); 

    List<Vector2> curvePoints = new List<Vector2>(); 
    for (float x = 0; x < 1; x += 1/(float)steps) 
    { 
     curvePoints.Add(getBezierPointRecursive(x, points3D)); 
    } 

    float curveWidth = 0.003f; 

    for(int x = 0; x < curvePoints.Count; x++) 
    { 
     Vector2 normal; 

     if(x == 0) 
     { 
      //First point, Take normal from first line segment 
      normal = getNormalizedVector(getLineNormal(curvePoints[x+1] - curvePoints[x])); 
     } 
     else if (x + 1 == curvePoints.Count) 
     { 
      //Last point, take normal from last line segment 
      normal = getNormalizedVector(getLineNormal(curvePoints[x] - curvePoints[x-1])); 
     } else 
     { 
      //Middle point, interpolate normals from adjacent line segments 
      normal = getNormalizedVertexNormal(getLineNormal(curvePoints[x] - curvePoints[x - 1]), getLineNormal(curvePoints[x + 1] - curvePoints[x])); 
     } 

     path.Add(new VertexPositionTexture(new Vector3(curvePoints[x] + normal * curveWidth, 0), new Vector2())); 
     path.Add(new VertexPositionTexture(new Vector3(curvePoints[x] + normal * -curveWidth, 0), new Vector2())); 
    } 

    for(int x = 0; x < curvePoints.Count-1; x++) 
    { 
     indices.Add(2 * x + 0); 
     indices.Add(2 * x + 1); 
     indices.Add(2 * x + 2); 

     indices.Add(2 * x + 1); 
     indices.Add(2 * x + 3); 
     indices.Add(2 * x + 2); 
    } 

    return new object[] { 
     path.ToArray(), 
     indices.ToArray() 
    }; 
} 

//Recursive algorithm for getting the bezier curve points 
private static Vector2 getBezierPointRecursive(float timeStep, Vector2[] ps) 
{ 

    if (ps.Length > 2) 
    { 
     List<Vector2> newPoints = new List<Vector2>(); 
     for (int x = 0; x < ps.Length - 1; x++) 
     { 
      newPoints.Add(interpolatedPoint(ps[x], ps[x + 1], timeStep)); 
     } 
     return getBezierPointRecursive(timeStep, newPoints.ToArray()); 
    } 
    else 
    { 
     return interpolatedPoint(ps[0], ps[1], timeStep); 
    } 
} 

//Gets the interpolated Vector2 based on t 
private static Vector2 interpolatedPoint(Vector2 p1, Vector2 p2, float t) 
{ 
    return Vector2.Multiply(p2 - p1, t) + p1; 
} 

//Gets the normalized normal of a vertex, given two adjacent normals (2D) 
private static Vector2 getNormalizedVertexNormal(Vector2 v1, Vector2 v2) //v1 and v2 are normals 
{ 
    return getNormalizedVector(v1 + v2); 
} 

//Normalizes the given Vector2 
private static Vector2 getNormalizedVector(Vector2 v) 
{ 
    Vector2 temp = new Vector2(v.X, v.Y); 
    v.Normalize(); 
    return v; 
} 

//Gets the normal of a given Vector2 
private static Vector2 getLineNormal(Vector2 v) 
{ 
    Vector2 normal = new Vector2(v.Y, -v.X);    
    return normal; 
} 

//Drawing method in main Game class 
//curveData is a private object[] that is initialized in the constructor (by calling computeCurve3D) 
protected override void Draw(GameTime gameTime) 
{ 
    GraphicsDevice.Clear(Color.CornflowerBlue); 

    var camPos = new Vector3(0, 0, 0.1f); 
    var camLookAtVector = Vector3.Forward; 
    var camUpVector = Vector3.Up; 

    effect.View = Matrix.CreateLookAt(camPos, camLookAtVector, camUpVector); 
    float aspectRatio = graphics.PreferredBackBufferWidth/(float)graphics.PreferredBackBufferHeight; 
    float fieldOfView = MathHelper.PiOver4; 
    float nearClip = 0.1f; 
    float farClip = 200f; 

    //Orthogonal 
    effect.Projection = Matrix.CreateOrthographic(480 * aspectRatio, 480, nearClip, farClip); 

    foreach (var pass in effect.CurrentTechnique.Passes) 
    { 
     pass.Apply(); 
     effect.World = Matrix.CreateScale(200); 

     graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, 
      (VertexPositionTexture[])curveData[0], 
      0, 
      ((VertexPositionTexture[])curveData[0]).Length, 
      (int[])curveData[1], 
      0, 
      ((int[])curveData[1]).Length/3);    
    } 

    base.Draw(gameTime); 
} 

Кроме того, этот образ может быть в состоянии показать, что код делает немного лучше

Wireframe of curve

+0

Молодцы. Интересно отметить BasicEffect и сглаживание. +1 – MickyD

+0

Хорошая работа. Это было всего лишь предположение от моего имени, но я рад, что вы его работали. Если бы вы могли разместить соответствующий код здесь для других, это было бы здорово. – craftworkgames

+0

О, извините, я учился в школе. Я отредактирую его. – Razacx

 Смежные вопросы

  • Нет связанных вопросов^_^