【UE4】GAMES101 圖形學作業4:貝塞爾曲線


總覽

  • Bézier 曲線是一種用於計算機圖形學的參數曲線。

    image

  • 在本次作業中,你需要實現de Casteljau 算法來繪制由4 個控制點表示的Bézier 曲線(當你正確實現該算法時,你可以支持繪制由更多點來控制的Bézier 曲線)。

  • 你需要修改的函數在提供的main.cpp 文件中。

    • bezier:該函數實現繪制Bézier 曲線的功能。
      它使用一個控制點序列和一個OpenCV::Mat 對象作為輸入,沒有返回值。它會使t 在0 到1 的范圍內進行迭代,並在每次迭代中使t 增加一個微小值。對於每個需要計算的t,將調用另一個函數recursive_bezier,然后該函數將返回在Bézier 曲線上t處的點。最后,將返回的點繪制在OpenCV ::Mat 對象上。
    • recursive_bezier:該函數使用一個控制點序列和一個浮點數t 作為輸入,實現de Casteljau 算法來返回Bézier 曲線上對應點的坐標。

實現

  • 版本 4.26.2

  • 原文地址

  • naive_bezier

    • 數學公式

      image

    • 代碼

      void AActor_BezierCuve::naive_bezier()
      {
      	FVector& p_0 = m_points[0];
      	FVector& p_1 = m_points[1];
      	FVector& p_2 = m_points[2];
      	FVector& p_3 = m_points[3];
      	FVector& p_4 = m_points[4];
      	for (double t = 0.0; t <= 1.0; t += 0.001)
      	{
      		auto point = std::pow(1 - t, 4) * p_0 + 4 * t * std::pow(1 - t, 3) * p_1 +
      			6 * std::pow(t, 2) * std::pow((1 - t), 2) * p_2 + 4 * std::pow(t, 3) * (1 - t) * p_3 + std::pow(t, 4) * p_4;
      		DrawDebugPoint(GetWorld(), point, 2.0f, FColor::Green,true,5.0f);
      		//UKismetSystemLibrary::PrintString(GetWorld(), point.ToString());
      	}
      	
      }
      
  • recursive_bezier

    • De Casteljau 算法說明如下:

      1. 考慮一個p0, p1, ... pn 為控制點序列的Bézier 曲線。首先,將相鄰的點連接起來以形成線段。
      2. 用t : (1 − t) 的比例細分每個線段,並找到該分割點。
      3. 得到的分割點作為新的控制點序列,新序列的長度會減少一。
      4. 如果序列只包含一個點,則返回該點並終止。否則,使用新的控制點序列並轉到步驟1。使用[0,1] 中的多個不同的t 來執行上述算法,你就能得到相應的Bézier 曲線。
    • 代碼

      void AActor_BezierCuve::bezier()
      {
      	for (double t = 0.0; t <= 1.0; t += 0.001)
      	{
      		FVector point = recursive_bezier(m_points, t);
      		DrawDebugPoint(GetWorld(), point, 2.0f, FColor(10,214,255,255),true,5.0f);
      	}
      }
      
      // De Casteljau 算法,遞歸
      FVector AActor_BezierCuve::recursive_bezier(TArray<FVector>& points, float t)
      {
      	if (points.Num() < 3) {
      		return (1 - t) * points[0] + t * points[1];
      	}
      
      	TArray<FVector> newPoint;
      	for (int i = 0; i < points.Num() - 1; i++) {
      		newPoint.Add((1 - t) * points[i] + t * points[i + 1]);
      	}
      	return recursive_bezier(newPoint, t);
      }
      
  • 最終效果
    image

附錄

所有代碼

  • Actor_BezierCuve.h

    點擊查看代碼
      ```cpp
      UCLASS()
      class GAMES101_API AActor_BezierCuve : public AActor
      {
      	GENERATED_BODY()
    
      public:	
      	// Sets default values for this actor's properties
      	AActor_BezierCuve();
    
      protected:
      	// Called when the game starts or when spawned
      	virtual void BeginPlay() override;
    
      public:	
      	// Called every frame
      	virtual void Tick(float DeltaTime) override;
    
      	UFUNCTION(BlueprintCallable)
      	void naive_bezier();
    
      	UFUNCTION(BlueprintCallable)
      		void bezier();
    
      	UFUNCTION(BlueprintCallable)
      		FVector recursive_bezier(TArray<FVector>& points,float t);
    
      public:
      	UPROPERTY(VisibleAnywhere)
      		USceneComponent* root;
      	UPROPERTY(VisibleAnywhere)
      		UStaticMeshComponent* point0;
      	UPROPERTY(VisibleAnywhere)
      		UStaticMeshComponent* point1;
      	UPROPERTY(VisibleAnywhere)
      		UStaticMeshComponent* point2;
      	UPROPERTY(VisibleAnywhere)
      		UStaticMeshComponent* point3;
      	UPROPERTY(VisibleAnywhere)
      		UStaticMeshComponent* point4;
    
      	UPROPERTY();
      	TArray<FVector> m_points;
    
      	UPROPERTY(EditAnywhere);
      	bool m_bUseRecursiveBezier;
    
      };
      ```
    
  • AActor_BezierCuve.cpp

    點擊查看代碼
    #include "Actor_BezierCuve.h"
    #include "DrawDebugHelpers.h"
    #include <cmath>
    #include "Kismet/KismetSystemLibrary.h"
    
    // Sets default values
    AActor_BezierCuve::AActor_BezierCuve()
    {
     	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    	PrimaryActorTick.bCanEverTick = true;
    	root = CreateDefaultSubobject<USceneComponent>(TEXT("root"));
    	SetRootComponent(root);
    
    	point0 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point0"));
    	point1 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point1"));
    	point2 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point2"));
    	point3 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point3"));
    	point4 = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("point4"));
    
    	point0->SetupAttachment(root);
    	point1->SetupAttachment(root);
    	point2->SetupAttachment(root);
    	point3->SetupAttachment(root);
    	point4->SetupAttachment(root);
    	m_points.Init(FVector::ZeroVector, 5);
    
    	m_bUseRecursiveBezier = false;
    }
    
    // Called when the game starts or when spawned
    void AActor_BezierCuve::BeginPlay()
    {
    	Super::BeginPlay();
    	m_points[0] = point0->GetComponentLocation();
    	m_points[1] = point1->GetComponentLocation();
    	m_points[2] = point2->GetComponentLocation();
    	m_points[3] = point3->GetComponentLocation();
    	m_points[4] = point4->GetComponentLocation();
    
    	if (!m_bUseRecursiveBezier)
    		naive_bezier();
    	else
    		bezier();
    }
    
    // Called every frame
    void AActor_BezierCuve::Tick(float DeltaTime)
    {
    	Super::Tick(DeltaTime);
    
    	
    }
    
    // 多項式 
    void AActor_BezierCuve::naive_bezier()
    {
    	FVector& p_0 = m_points[0];
    	FVector& p_1 = m_points[1];
    	FVector& p_2 = m_points[2];
    	FVector& p_3 = m_points[3];
    	FVector& p_4 = m_points[4];
    	for (double t = 0.0; t <= 1.0; t += 0.001)
    	{
    		auto point = std::pow(1 - t, 4) * p_0 + 4 * t * std::pow(1 - t, 3) * p_1 +
    			6 * std::pow(t, 2) * std::pow((1 - t), 2) * p_2 + 4 * std::pow(t, 3) * (1 - t) * p_3 + std::pow(t, 4) * p_4;
    		DrawDebugPoint(GetWorld(), point, 2.0f, FColor::Green,true,5.0f);
    		//UKismetSystemLibrary::PrintString(GetWorld(), point.ToString());
    	}
    	
    }
    
    void AActor_BezierCuve::bezier()
    {
    	for (double t = 0.0; t <= 1.0; t += 0.001)
    	{
    		FVector point = recursive_bezier(m_points, t);
    		DrawDebugPoint(GetWorld(), point, 2.0f, FColor(10,214,255,255),true,5.0f);
    	}
    }
    
    // De Casteljau 算法,遞歸
    FVector AActor_BezierCuve::recursive_bezier(TArray<FVector>& points, float t)
    {
    	if (points.Num() < 3) {
    		return (1 - t) * points[0] + t * points[1];
    	}
    
    	TArray<FVector> newPoint;
    	for (int i = 0; i < points.Num() - 1; i++) {
    		newPoint.Add((1 - t) * points[i] + t * points[i + 1]);
    	}
    	return recursive_bezier(newPoint, t);
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM