본문 바로가기
개발/Unity

유니티 - 3차원에서 두 직선 사이의 최단 거리를 만드는 직선 (How to Find the Points of Intersection Two Lines in 3D)

by 피로물든딸기 2022. 11. 26.
반응형

Unity 전체 링크

 

참고

- 평면과 직선의 접점 좌표 구하기

- 튜플로 여러 값 반환하기

 

아래와 같이 빨간 직선파란 직선이 3차원 공간에 있을 때,

두 직선의 최단거리최단거리를 만드는 초록색 직선을 구해보자.


3차원에 있는 두 직선의 최단거리

 

두 직선의 최단거리 공식은 다음과 같다.

점 A, B가 만드는 직선과 점 C, D가 만드는 직선이 있을 때, 

직선 AB의 점 하나(=A)와 직선 CD의 점 하나(=C)로 직선을 만들고 아래의 공식을 대입하면 거리를 구할 수 있다.

 

 

위의 공식 AC에서 A나 C가 아니더라도 각각 직선 AB와 직선 CD에 포함된 모든 점이 가능하다.

 

유니티에서는 아래와 같이 구현하면 된다.

    float getDistanceTwoLine(Vector3 A, Vector3 B, Vector3 C, Vector3 D)
    {
        Vector3 AB = A - B;
        Vector3 CD = C - D;
        Vector3 AC = A - C;
        Vector3 line = Vector3.Cross(AB, CD);

        return Mathf.Abs(Vector3.Dot(AC, line)) / line.magnitude;
    }

3차원에 있는 두 직선의 최단거리를 만드는 직선

 

최단거리를 만드는 직선은 두 직선에 대해 모두 수직이어야 한다.

따라서 이 직선의 방향은 직선 AB직선 CD외적이다.

 

그런데 여기서 두 직선의 교차점은 어떻게 구할까?

평면과 직선의 접점 좌표를 이용해서 구하면 된다.

 

외적직선 AB를 다시 외적하고 직선 AB를 포함하는 평면을 구해보자.

이 평면은 반드시 아래와 같게 된다. (초록색 직선빨간색 직선외적이 평면의 노멀 벡터다)

 

평면과 직선의 접점 좌표를 구하는 공식으로 파란색 직선에 있는 최단거리를 만드는 점을 구할 수 있다.

    Vector3 getContactPoint(Vector3 normal, Vector3 planeDot, Vector3 A, Vector3 B)
    {
        Vector3 nAB = (B - A).normalized;

        return A + nAB * Vector3.Dot(normal, planeDot - A) / Vector3.Dot(normal, nAB);
    }

 

위의 공식을 이용하면 마찬가지로 빨간색 직선에 있는 최단거리를 만드는 점도 구할 수 있다.

유니티 C#의 튜플을 이용하여 두 점을 리턴하는 함수를 만들면 아래와 같다.

    (Vector3 point1, Vector3 point2) getShortestPath(Vector3 A, Vector3 B, Vector3 C, Vector3 D)
    {
        Vector3 AB = A - B;
        Vector3 CD = C - D;
        Vector3 line = Vector3.Cross(AB, CD);
        Vector3 crossLineAB = Vector3.Cross(line, AB);
        Vector3 crossLineCD = Vector3.Cross(line, CD);

        return (getContactPoint(crossLineAB, A, C, D), getContactPoint(crossLineCD, C, A, B));
    }

 

실제 최단거리가 같은지 Update 문에서 Vector3.Distance와 getDistanceTwoLine로 구한 값을 비교해보자.

    void Update()
    {
        Vector3 p1, p2, p3, p4;

        p1 = point1.position;
        p2 = point2.position;
        p3 = point3.position;
        p4 = point4.position;

        (Vector3 linePoint1, Vector3 linePoint2)
            = getShortestPath(p1, p2, p3, p4);

        cube1.position = linePoint1;
        cube2.position = linePoint2;

        setLineRenderer(lr1, new List<Vector3>() { p1, p2 }, Color.red);
        setLineRenderer(lr2, new List<Vector3>() { p3, p4 }, Color.blue);
        setLineRenderer(lr3, new List<Vector3>() { cube1.position, cube2.position }, Color.green);

        float distanceCube1 = getDistanceTwoLine(p1, p2, p3, p4);
        float distanceCube2 = Vector3.Distance(cube1.position, cube2.position);

        Debug.Log(distanceCube1 + " / " + distanceCube2);
    }

 

점을 움직여보면 최단거리가 정상적으로 변경되는 것을 알 수 있다.

 

그리고 콘솔의 로그를 보면 두 직선의 최단거리가 잘 구해지는 것을 알 수 있다.

 

만약 선이 유한한 경우에만 직선을 구해야 한다면,

2차원 평면에서 유한한 선의 교점 구하기를 참고하여 위에서 구한 두 점이 각각의 직선에 포함되는지 체크하면 된다.

    bool checkDotInLine(Vector3 a, Vector3 b, Vector3 dot)
    {
        float epsilon = 0.00001f;
        float dAB = Vector3.Distance(a, b);
        float dADot = Vector3.Distance(a, dot);
        float dBDot = Vector3.Distance(b, dot);

        return ((dAB + epsilon) >= (dADot + dBDot));
    }

 

테스트는 다음과 같이 설정하였다. 

자세한 사항은 글 아래에 있는 unitypackage를 참고하자.

 

전체 코드는 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LineToLine : MonoBehaviour
{
    public Transform point1, point2, point3, point4;
    public Transform cube1, cube2;
    public LineRenderer lr1, lr2, lr3;

    float getDistanceTwoLine(Vector3 A, Vector3 B, Vector3 C, Vector3 D)
    {
        Vector3 AB = A - B;
        Vector3 CD = C - D;
        Vector3 AC = A - C;
        Vector3 line = Vector3.Cross(AB, CD);

        return Mathf.Abs(Vector3.Dot(AC, line)) / line.magnitude;
    }

    Vector3 getContactPoint(Vector3 normal, Vector3 planeDot, Vector3 A, Vector3 B)
    {
        Vector3 nAB = (B - A).normalized;

        return A + nAB * Vector3.Dot(normal, planeDot - A) / Vector3.Dot(normal, nAB);
    }

    (Vector3 point1, Vector3 point2) getShortestPath(Vector3 A, Vector3 B, Vector3 C, Vector3 D)
    {
        Vector3 AB = A - B;
        Vector3 CD = C - D;
        Vector3 line = Vector3.Cross(AB, CD);
        Vector3 crossLineAB = Vector3.Cross(line, AB);
        Vector3 crossLineCD = Vector3.Cross(line, CD);

        return (getContactPoint(crossLineAB, A, C, D), getContactPoint(crossLineCD, C, A, B));
    }

    void setLineRenderer(LineRenderer lr, List<Vector3> list, Color color)
    {
        lr.startWidth = lr.endWidth = .2f;
        lr.material.color = color;
        lr.positionCount = list.Count;

        for (int i = 0; i < list.Count; i++)
            lr.SetPosition(i, list[i]);
    }

    void Update()
    {
        Vector3 p1, p2, p3, p4;

        p1 = point1.position;
        p2 = point2.position;
        p3 = point3.position;
        p4 = point4.position;

        (Vector3 linePoint1, Vector3 linePoint2)
            = getShortestPath(p1, p2, p3, p4);

        cube1.position = linePoint1;
        cube2.position = linePoint2;

        setLineRenderer(lr1, new List<Vector3>() { p1, p2 }, Color.red);
        setLineRenderer(lr2, new List<Vector3>() { p3, p4 }, Color.blue);
        setLineRenderer(lr3, new List<Vector3>() { cube1.position, cube2.position }, Color.green);

        float distanceCube1 = getDistanceTwoLine(p1, p2, p3, p4);
        float distanceCube2 = Vector3.Distance(cube1.position, cube2.position);

        Debug.Log(distanceCube1 + " / " + distanceCube2);
    }
}

 

위의 실행결과는 아래의 unitypackage에서 확인 가능하다.

PointsOfIntersectionTwoLines.unitypackage
0.04MB

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글