게임 개발의 베 지어 곡선, 곡선 및 경로


베 지어 곡선은 자연스러운 기하학적 모양의 수학적 근사치입니다. 가능한 한 적은 정보가 있고 매우 유연한 곡선을 나타내는 데 사용합니다.

더 추상적 인 수학적 개념과 달리 베 지어 곡선은 산업 디자인을 위해 만들어졌습니다. 그래픽 소프트웨어 산업에서 널리 사용되는 도구입니다.

그들은 부드러운 곡선을 만들기 위해 여러 단계를 결합하는 보간법 (이전 기사에서 언급했습니다) 에 의존 합니다. 베 지어 곡선의 작동 원리를 더 잘 이해하기 위해 가장 간단한 형태 인 2 차 베 지어 곡선부터 시작하겠습니다.

2 차 베 지어 곡선

세 점을 취하십시오. 이것이 2 차 베 지어 곡선이 작동하는 데 필요한 최소 요구 사항입니다.

../../_images/bezier_quadratic_points.png

그 사이에 곡선을 그리려면 먼저 0에서 1 사이의 값을 사용하고 세 점으로 구성된 두 선분의 각 정점에있는 두 정점을 점차적으로 보간합니다. 이렇게하면 선분 값을 변경할 때 선분을 따라 두 점의 t를 0에서 1로 이동할 수 있습니다.

func _quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float):
    var q0 = p0.linear_interpolate(p1, t)
    var q1 = p1.linear_interpolate(p2, t)

그런 다음 q0, 우리는 보간하고 q1은 곡선을 따라 r의 단일 점을 얻습니다.

var r = q0.linear_interpolate(q1, t)
return r

이러한 유형의 곡선을 2 차 베 지어 곡선이라고합니다.

여기에 사진 설명 삽입
(이미지 출처 : Wikipedia)

큐빅 베 지어 곡선

앞의 예를 기반으로 네 점 사이를 보간하여 더 많은 제어를 얻을 수 있습니다.

../../_images/bezier_cubic_points.png

먼저 사용하는 함수는 p0, p1, p2 및 p3의 네 가지 매개 변수가있는 입력으로 네 점을 사용합니다.

func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float):

각 점에 선형 보간을 적용하여 3 개로 줄입니다.

var q0 = p0.linear_interpolate(p1, t)
var q1 = p1.linear_interpolate(p2, t)
var q2 = p2.linear_interpolate(p3, t)

그런 다음 세 점을 두 점으로 단순화했습니다.

var r0 = q0.linear_interpolate(q1, t)
var r1 = q1.linear_interpolate(q2, t)

그리고 하나를주세요 :

var s = r0.linear_interpolate(r1, t)
return s

이것은 전체 기능입니다.

func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float):
    var q0 = p0.linear_interpolate(p1, t)
    var q1 = p1.linear_interpolate(p2, t)
    var q2 = p2.linear_interpolate(p3, t)

    var r0 = q0.linear_interpolate(q1, t)
    var r1 = q1.linear_interpolate(q2, t)

    var s = r0.linear_interpolate(r1, t)
    return s

결과는 네 점 모두 사이에 보간 된 부드러운 곡선이됩니다.

여기에 사진 설명 삽입

(이미지 출처 : Wikipedia)

노트

3 차원 베 지어 보간은 Vector2 대신 Vector3가 사용된다는 점을 제외하면 3D에서 동일한 효과를 갖습니다.

제어점 추가

3 차 베 지어 곡선을 기반으로 두 점이 작동하는 방식을 변경하여 곡선의 모양을 자유롭게 제어 할 수 있습니다. 그러나이있다 p0, p1, p2그리고 p3우리는 그들을로 저장합니다 :

  • point0 = p0: 첫 번째 지점, 소스
  • control0 = p1 - p0: 첫 번째 제어점을 기준으로 한 벡터
  • control1 = p3 - p2: 두 번째 제어점에 상대적인 벡터입니다.
  • point1 = p3: 두 번째 지점, 목적지

이런 식으로 우리는 각 점에 대한 상대적인 벡터 인 두 개의 점과 두 개의 제어점을 갖게됩니다. 이전에 그래픽이나 애니메이션 소프트웨어를 사용해 본 적이 있다면 다음과 같이 익숙해 보일 것입니다.

../../_images/bezier_cubic_handles.png

이것이 그래픽 소프트웨어가 사용자에게 베 지어 곡선을 표시하는 방법과 Godot에서 작동하고 보이는 방법입니다.

Curve2D, Curve3D, Path 및 Path2D

커브를 포함하는 두 개체가 있습니다 : Curve3D 및 Curve2D (각각 3D 및 2D 용).

여러 지점을 포함 할 수 있으므로 더 긴 경로를 사용할 수 있습니다. 또한 노드로 설정할 수도 있습니다 : Path 및 Path2D (각각 3D 및 2D에도 사용됨) :

../../_images/bezier_path_2d.png

그러나 그것들을 사용하는 것이 그다지 명확하지 않을 수 있으므로 다음은 베 지어 곡선의 가장 일반적인 사용 사례에 대한 설명입니다.

평가

단순히 평가하는 것이 옵션 일 수 있지만 대부분의 경우 그다지 유용하지 않습니다. 베 지어 곡선의 가장 큰 단점은 일정한 속도로 t = 0에서 t = 1로 이동하면 실제 보간이 일정한 속도로 이동하지 않는다는 것입니다. 속도는 또한 점 p0, p1, p2 및 p3 사이의 거리의 보간이며, 일정한 속도로 곡선을 횡단하는 수학적으로 간단한 방법은 없습니다.

다음 의사 코드를 간단한 예제로 사용하겠습니다.

var t = 0.0

func _process(delta):
    t += delta
    position = _cubic_bezier(p0, p1, p2, p3, t)

여기에 사진 설명 삽입

보시다시피 t가 일정한 속도로 증가하더라도 원의 속도 (초당 픽셀 수)는 변경됩니다. 이로 인해 Bezier 곡선을 즉시 사용하기가 어렵습니다.

무승부

베 지어 곡선 (또는 곡선 기반 개체)을 그리는 것은 매우 일반적인 사용 사례이지만 쉽지는 않습니다. 거의 모든 경우에 베 지어 곡선은 일종의 선분으로 변환되어야합니다. 그러나 이것은 일반적으로 많은 코드를 작성하지 않고는 어렵습니다.

그 이유는 곡선의 일부 (특히 모서리)에 많은 수의 점이 필요할 수 있지만 다른 부분은 그렇지 않을 수 있기 때문입니다.

../../_images/bezier_point_amount.png

또한 두 제어점이 모두 0, 0이면 (상대 벡터라는 점을 기억하십시오) Bezier 곡선은 직선 일 것입니다 (따라서 많은 점을 그리는 것은 낭비입니다).

베 지어 곡선을 그리기 전에 세분화해야합니다. 이것은 일반적으로 곡률의 양이 특정 임계 값 미만이 될 때까지 곡선을 분할 할 수있는 재귀 또는 분할 및 정복 함수를 통해 수행됩니다.

커브 클래스는이 Curve2D.tessellate () 함수를 제공합니다 (선택적 단계의 재귀 및 각도 허용 오차 매개 변수를받습니다). 이러한 방식으로 커브를 기반으로 개체를 그리는 것이 더 쉽습니다.

횡단

커브의 마지막 일반적인 사용 사례는 커브를 횡단하는 것입니다. 이것은 또한 앞서 언급 한 정속 콘텐츠로 인해 매우 어렵습니다.

이 작업을 더 쉽게하려면 커브를 등거리 지점까지 구워야합니다. 이러한 방식으로 정규 보간 (3 차 옵션으로 더 개선 될 수 있음)에 의해 근사화 될 수 있습니다. 이렇게하려면 Curve2D.get_baked_length ()와 함께 Curve.interpolate_baked () 메서드를 사용하면됩니다. 이들 중 하나를 처음 호출하면 내부적으로 곡선이 구워집니다.

그런 다음 다음 의사 코드를 사용하여 정속 순회를 완료 할 수 있습니다.

var t = 0.0

func _process(delta):
    t += delta
    position = curve.interpolate_baked(t * curve.get_baked_length(), true)

그러면 출력이 일정한 속도로 이동합니다.

여기에 사진 설명 삽입

추천

출처blog.csdn.net/qq_44273429/article/details/111179930