gpt4 book ai didi

c# - 在 MonoGame (XNA) 中绘制贝塞尔曲线会产生划痕线

转载 作者:太空狗 更新时间:2023-10-30 00:39:39 25 4
gpt4 key购买 nike

我最近开始使用 MonoGame,我喜欢这个库。但是,我似乎在绘制贝塞尔曲线时遇到了一些问题

我的代码产生的结果看起来像这样

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);
}

通过向我的 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 方法有问题?

提前致谢。

编辑:

好吧,通过使用 Vector2D 进行所有计算并仅在需要绘制时将其转换为点,我设法使曲线看起来好多了

A lot smoother

虽然还不够完美:/

最佳答案

正如 Mike '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

关于c# - 在 MonoGame (XNA) 中绘制贝塞尔曲线会产生划痕线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33977226/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com