2015-11-28 70 views
3

我最近開始使用MonoGame,我喜歡圖書館。 不過,我似乎有與繪製Bezier曲線在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方法有問題?

在此先感謝。

編輯:

好吧,我設法讓曲線看起來很多做所有的計算與Vector2Ds並且只是在一瞬間將其轉換爲一個點,它需要繪製

A lot smoother更好

它仍然是不完美的,但:/

+0

有什麼聯繫的第二形象嗎?我們可以編輯它。也就是說,很難說出你認爲「完美」應該是什麼。在處理這樣的事情時,永遠不要輪到最後一刻。如果您的繪圖表面可以進行子像素繪製,那麼最後不要進行圓形處理 - 讓畫布處理位置。 (通常,不是繪製各個點,而是跟蹤「當前」和「前一個」點,而是在這些點之間畫線) –

+0

對我來說看起來很好。您可以將您的**編輯**發佈爲下面的答案。 :) – MickyD

+1

你有沒有研究過使用BasicEffect而不是SpriteBatch?我認爲這將是更好的方式去做這種事情。 – craftworkgames

回答

5

正如邁克Pomax「Kamermans說, 它似乎已經與2D表面的問題,不允許子像素繪圖和從而導致舍入問題

遵循craftworkgames的建議,我調整了算法,使用BasicEffect在3D中繪製曲線。這也允許抗鋸齒,可以平滑曲線。

結果如下:

link

好多了!

非常感謝您的有益建議!

編輯:

這裏是我用來做這個的代碼。

我還想補充一點,這個網頁(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