webentwicklung-frage-antwort-db.com.de

Ist es wirklich so schwierig, in Unity glatte Linien zu zeichnen?

Ich habe seit einiger Zeit versucht, glatte Linien in Unity zu zeichnen, aber mit Line Renderer erhielt ich nur gezackte Linien mit nicht abgerundeten Ecken, insbesondere wenn der Krümmungswinkel wirklich klein ist. Ich habe den Wert von Antialiasing in den Qualitätseinstellungen erhöht und verschiedene Materialien ausprobiert, aber nichts hat sich geändert. Ich habe auch versucht, eine Kugel jedes Mal zu instanziieren, wenn sich die Maus bewegt, es entstehen jedoch einige Lücken zwischen den verschiedenen Kugeln, insbesondere wenn die Maus schnell geht. Ich weiß, dass es dafür ein Plugin namens Vectrosity gibt, aber es gibt einen Weg, dies ohne es zu erreichen?

9
rickyxd

Ich erhielt nur gezackte Linien mit abgerundeten Ecken in insbesondere wenn der Krümmungswinkel wirklich klein ist.

Dies war ein Problem in Unity 5.4 und darunter. Dieses Problem wurde in Unity 5.5 und höher behoben, nachdem LineRenderer vollständig umgestaltet wurde.

Alles, was Sie tun müssen, ist ein Update auf Unity 5.5 oder die obige Version, und dieses Problem sollte verschwinden.

Es gibt eine neue Variable namens LineRenderer.numCornerVertices. Damit können Sie einstellen, wie glatt die Linie sein soll. Der Wert von 5 scheint dafür in Ordnung zu sein.

Es gibt auch eine weitere neue Variable mit dem Namen LineRenderer.numCapVertices, mit der festgelegt werden kann, wie glatt das end der Zeile sein soll.

Dies ist ein Screenshot, der zwischen 5.4 und 5.5 die Änderungen zeigt:

 enter image description here

8
Programmer

Sie können einige gute Ergebnisse erzielen, wenn Sie aus einer Reihe von Punkten ein Netz erzeugen.

Der Algorithmus dafür ist wie folgt:

  1. Sie haben eine Reihe von Punkten, die mit einer Bezier-Kurve erzeugt werden könnten.

 enter image description here

  1. Nehmen Sie für jeden Punkt einen Richtungsvektor zum nächsten Punkt v = (p2 - p1)(blau markiert). Drehen Sie dann diesen Vektor um 90 Grad normal = v.y, -v.x, der rot markiert ist.

 enter image description here

  1. Dies zeigt, dass wir jede Norm von der Punktposition aus verwenden werden. Sie können diesen Vektor nun in beide Richtungen mit der gewünschten Linienbreite multiplizieren.

 enter image description here

  1. Erstellen Sie die Scheitelpunkte an diesen Positionen.

 enter image description here

  1. Fügen Sie Indizes hinzu, um Dreiecke zu bilden. Dies wird in etwa wie [i, w/2 + i, w/2 + i + 1] aussehen, wobei i der aktuelle Index ist und w die Gesamtanzahl der Scheitelpunkte ist.

 enter image description here

  1. Erstellen Sie die anderen Dreiecke. Wieder so etwas wie [i, w/2 * i + 1, i + 1]

 enter image description here

  1. Und das Endergebnis. Sie können weitere Punkte hinzufügen, um die Linie weicher zu machen.

 enter image description here

21
Iggy

Dank der Anregungen und Tutorials von @Iggy auf catlikecoding.com (woher der Spline-Code stammt, den ich verwende), habe ich eine Komponente erstellt, die ein Netz erstellt, das auf einem Spline basiert, dessen Breite und Abtastfrequenz gegeben ist. Höhere Abtastfrequenz = natürlich glattere Kurve.

using UnityEngine;

[ExecuteInEditMode]
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(BezierSpline))]
public class SplineMesh : MonoBehaviour {

  [Range(1, 20)]
  public int sampleFrequency = 5;

  [Range(0, 5f)]
  public float lineWidth = 0.3f;

  BezierSpline spline;
  Mesh mesh;

  private void Awake () {
    spline = GetComponent<BezierSpline>();
    mesh = GetComponent<Mesh>();
  }

  void Update()
  {
    /*
    for(int i = 0; i <= sampleFrequency; i++){
      float interval = i / (float)sampleFrequency;

      var point = spline.GetPoint(interval);
      var direction = spline.GetDirection(interval);

      var perpendicularLeftVec = PerpendicularLeft(direction) * lineWidth;
      var perpendicularRightVec = PerpendicularRight(direction) * lineWidth;

      Debug.DrawLine(point, point + (Vector3)perpendicularLeftVec, Color.Magenta, 0.5f, false);
      Debug.DrawLine(point, point + (Vector3)perpendicularRightVec, Color.cyan, 0.5f, false);
    }
    */
  }

    Vector2 PerpendicularRight(Vector2 orig){
        var vec = new Vector2(orig.y, -orig.x);
        vec.Normalize();
        return vec;
    }
    Vector2 PerpendicularLeft(Vector2 orig){
        var vec = new Vector2(orig.y, -orig.x);
        vec.Normalize();
        return vec * -1;
    }

  private Vector3[] vertices;

  public void GenerateMesh(){
    vertices = new Vector3[(sampleFrequency + 1) * 2];

    //iterate over our samples adding two vertices for each one
    for(int s = 0, i = 0; s <= sampleFrequency; s++, i += 2){
      float interval = s / (float)sampleFrequency;

      //get point along spline, and translate to local coords from world
      var point = transform.InverseTransformPoint(spline.GetPoint(interval));
      var direction = spline.GetDirection(interval);

      var perpendicularLeftVec = PerpendicularLeft(direction) * lineWidth;
      var perpendicularRightVec = PerpendicularRight(direction) * lineWidth;
      // var perpendicularVec = turnLeft ? PerpendicularLeft(diffVector) : PerpendicularRight(diffVector);

      vertices[i] = point + (Vector3)perpendicularLeftVec;
      vertices[i + 1] = point + (Vector3)perpendicularRightVec;
    }

    GetComponent<MeshFilter>().mesh = mesh = new Mesh();
    mesh.name = "Spline Mesh";

    mesh.vertices = vertices;

    //now figure out our triangles
    int [] triangles = new int[sampleFrequency * 6];
    for(int s = 0, ti = 0, vi = 0; s < sampleFrequency; s++, ti += 6, vi += 2){
      //first tri
      triangles[ti] = vi;
      triangles[ti + 1] = vi + 3;
      triangles[ti + 2] = vi + 1;
      //second matching tri
      triangles[ti + 3] = vi;
      triangles[ti + 4] = vi + 2;
      triangles[ti + 5] = vi + 3;
    }

    mesh.triangles = triangles;
    mesh.RecalculateNormals();

    Debug.Log("Generated Spline Mesh");
  }


}
1
DShook

Dank der hervorragenden Antwort von @ Iggy habe ich es endlich geschafft.

Erstellen Sie ein neues Bild auf Ihrer Leinwand, löschen Sie das Bildskript und ersetzen Sie es durch UICubicBezier.

enter image description here

Geben:

enter image description here

using UnityEngine;
using UnityEngine.UI;

[ExecuteInEditMode]
public class UiCubicBezier : MaskableGraphic
{
    public float thickness = 2;

    public int anchors = 20;

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        // draws a cubic bezier curve from the lower left hand corner (start)
        // to the upper right hand corner (end).
        vh.Clear();

        var rt = this.rectTransform;
        var rect = rt.rect;

        var start = new Vector2(-rect.width / 2, -rect.height / 2);
        var cp1 = new Vector2(-rect.width / 6, -rect.height / 2);
        var cp2 = new Vector2(rect.width / 6, rect.height / 2);
        var end = new Vector2(rect.width / 2, rect.height / 2);
        var data = new BezierData(start, cp1, cp2, end);

        // all you need to know is that data.GetPoint generates a sequence of points
        // between the start and end points.
        var points = new Vector2[this.anchors];
        for (var anchor = 0; anchor < points.Length; anchor++)
        {
            var t = (float)anchor / this.anchors;
            points[anchor] = data.GetPoint(t);
        }

        // because the normals are at the mid-points between vertexes the start and end
        // points don't touch the bounding box. to fix this some vertexes are added to
        // the start and end that touch the bounding box.
        this.DrawStartVertexes(vh, start);

        for (var anchor = 0; anchor < points.Length - 1; anchor++)
        {
            this.DrawVertexes(vh, points[anchor], points[anchor + 1]);
        }

        this.DrawEndVertexes(vh, end);

        for (var v = 0; v + 2 < vh.currentVertCount; v += 2)
        {
            vh.AddTriangle(v, v + 1, v + 2);
        }

        for (var v = 0; v + 3 < vh.currentVertCount; v += 2)
        {
            vh.AddTriangle(v + 1, v + 2, v + 3);
        }
    }

    private void DrawStartVertexes(VertexHelper vh, Vector2 start)
    {
        // d = thickness * \sqrt{2}, so the distance between the vertexes
        // is equal to the thickness (https://en.wikipedia.org/wiki/Triangle#Right_triangles)
        var d = this.thickness * 0.70710678118f;

        var vertex = UIVertex.simpleVert;
        vertex.color = this.color;

        vertex.position = new Vector2(start.x, start.y + d);
        vh.AddVert(vertex);

        vertex.position = new Vector2(start.x + d, start.y);
        vh.AddVert(vertex);
    }

    private void DrawEndVertexes(VertexHelper vh, Vector2 end)
    {
        // d = thickness * \sqrt{2}, so the distance between the vertexes
        // is equal to the thickness (https://en.wikipedia.org/wiki/Triangle#Right_triangles)
        var d = this.thickness * 0.70710678118f;

        var vertex = UIVertex.simpleVert;
        vertex.color = this.color;

        vertex.position = new Vector2(end.x - d, end.y);
        vh.AddVert(vertex);

        vertex.position = new Vector2(end.x, end.y - d);
        vh.AddVert(vertex);
    }

    private void DrawVertexes(VertexHelper vh, Vector2 start, Vector2 end)
    {
        var v = end - start;
        var mid = start + v / 2; // the mid-point between start and end.
        var perp = Vector2.Perpendicular(v.normalized); // vector of length 1 perpendicular to v.

        var vertex = UIVertex.simpleVert;
        vertex.color = this.color;

        // move half the thickness away from the mid-point.
        vertex.position = mid + (perp * this.thickness / 2); 
        vh.AddVert(vertex);

        // move half the thickness away from the mid-point in the opposite direction.
        vertex.position = mid - (perp * this.thickness / 2);
        vh.AddVert(vertex);
    }

    private struct BezierData
    {
        private readonly Vector2 start;
        private readonly float cx;
        private readonly float bx;
        private readonly float ax;
        private readonly float cy;
        private readonly float by;
        private readonly float ay;

        public BezierData(Vector2 start, Vector2 cp1, Vector2 cp2, Vector2 end)
        {
            // cribbed from here: https://www.codeproject.com/articles/25237/bezier-curves-made-simple

            this.start = start;
            this.cx = 3 * (cp1.x - start.x);
            this.bx = 3 * (cp2.x - cp1.x) - this.cx;
            this.ax = end.x - start.x - this.cx - this.bx;

            this.cy = 3 * (cp1.y - start.y);
            this.by = 3 * (cp2.y - cp1.y) - this.cy;
            this.ay = end.y - start.y - this.cy - this.by;
        }

        public Vector2 GetPoint(float t)
        {
            var tSquared = t * t;
            var tCubed = tSquared * t;

            return new Vector2(
                (this.ax * tCubed) + (this.bx * tSquared) + (this.cx * t) + this.start.x,
                (this.ay * tCubed) + (this.by * tSquared) + (this.cy * t) + this.start.y);
        }
    }
}
0
briantyler

Ich hatte dieses Problem mit Unity 2017.2. Ich habe versucht, meine AA-Einstellungen auf Maximum zu ändern, um die Render-Jaggies von Linien zu beseitigen. Hat nicht funktioniert und es war frustrierend. 

Meine Lösung löste das Problem, dass MSAA auf der Kamera deaktiviert war, weil das Rendern verzögert wurde. Die Kamera verfügt über eine Einstellung für "Grafikeinstellungen verwenden", die eigentlich nie durcheinander gekommen sein sollte, aber ich bin Anfänger - ich weiß nicht viel. Ich änderte die Einstellung auf "vorwärts" und meine Jaggies verschwanden im Nebel. 

Wenn ich fleißiger wäre, würde ich vor und nach Bilder posten.

0
jpgm