【发布时间】:2015-11-28 22:36:30
【问题描述】:
我最近开始使用 MonoGame,我喜欢这个库。 但是,我似乎在绘制贝塞尔曲线时遇到了一些问题
我的代码产生的结果看起来像这样
看起来很糟糕,不是吗? 线条一点都不流畅。
让我给你看一些代码:
//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);
}
通过向 interpolatedPoint 方法添加一些手动 Math.Round() 调用,我设法使线条更平滑
//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 进行所有计算并仅在需要绘制时将其转换为点,我设法使曲线看起来更好
它仍然不完美:/
【问题讨论】:
-
第二张图片的链接是什么?我们可以编辑它。也就是说,很难说出你认为“完美”应该是什么。在处理这样的事情时,永远不要直到最后一刻才回过神来。而且,如果您的绘图表面可以进行亚像素绘图,甚至不要在最后进行圆形 - 让画布处理放置。 (通常,您不是绘制单个点,而是跟踪“当前”和“上一个”点,而是在它们之间画线)
-
对我来说看起来不错。您可以在下面发布您的 edit 作为答案。 :)
-
您是否考虑过使用 BasicEffect 代替 SpriteBatch?我认为这将是处理这类事情的更好方法。
-
那不是用来做3D渲染的吗?但是,是的,整个项目都是基本的概念证明。所以我想现在这样很好。
标签: c# xna rounding monogame bezier