【UE4】GAMES101 圖形學作業1:mvp 模型、視圖、投影變換


總覽

  • 到目前為止,我們已經學習了如何使用矩陣變換來排列二維或三維空間中的對象。所以現在是時候通過實現一些簡單的變換矩陣來獲得一些實際經驗了。在接下來的三次作業中,我們將要求你去模擬一個基於CPU 的光柵化渲染器的簡化版本。
  • 本次作業的任務是填寫一個旋轉矩陣和一個透視投影矩陣。給定三維下三個點v0(2.0, 0.0,−2.0), v1(0.0, 2.0,−2.0), v2(−2.0, 0.0,−2.0), 你需要將這三個點的坐標變換為屏幕坐標並在屏幕上繪制出對應的線框三角形。簡而言之,我們需要進行模型、視圖、投影、視口等變換來將三角形顯示在屏幕上
  • get_model_matrix(float rotation_angle)
    逐個元素地構建模型變換矩陣並返回該矩陣。在此函數中,你只需要實現三維中繞 z 軸旋轉的變換矩陣,而不用處理平移與縮放。
  • get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
    使用給定的參數逐個元素地構建透視投影矩陣並返回該矩陣。
  • 當你在上述函數中正確地構建了模型與投影矩陣,光柵化器會創建一個窗口顯示出線框三角形。由於光柵化器是逐幀渲染與繪制的,所以你可以使用A 和D 鍵去將該三角形繞z 軸旋轉

基礎與提高

  • [5 分] 正確構建模型矩陣。
  • [5 分] 正確構建透視投影矩陣。
  • [10 分] 你的代碼可以在現有框架下正確運行,並能看到變換后的三角形。
  • [10 分] 當按A 鍵與D 鍵時,三角形能正確旋轉。或者正確使用命令行得到旋轉結果圖像。
  • [提高項5 分] 在main.cpp 中構造一個函數,該函數的作用是得到繞任意過原點的軸的旋轉變換矩陣。
    Eigen::Matrix4f get_rotation(Vector3f axis, float angle)

實現

  • 版本 4.26.2

  • 原文地址

  • 投影變換矩陣

    image

  • 模型任意軸旋轉

    image

代碼

創建 AActor 派生類 AActor_Assignmen1

  • AActor_Assignmen1.h

    UCLASS()
    class GAMES101_API AActor_Assignmen1 : public AActor
    {
    	GENERATED_BODY()
    	
    public:	
    	// Sets default values for this actor's properties
    	AActor_Assignmen1();
    
    protected:
    	// Called when the game starts or when spawned
    	virtual void BeginPlay() override;
    
    public:	
    	// Called every frame
    	virtual void Tick(float DeltaTime) override;
    	void DrawTriangleIn3D();
    	
    	//主要函數
    	FMatrix get_view_matrix(FVector eye_pos);
    	FMatrix get_model_matrix(float rotation_angle);
    	FMatrix get_model_matrix_anyAxis(FVector axis, float angle);
    	FMatrix get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar);
    
    	void RasterizerDraw();
    
    public:
    	UPROPERTY(EditAnywhere, BlueprintReadWrite,  meta = (MakeEditWidget))
    	FTransform ponitA;
    	UPROPERTY(EditAnywhere,BlueprintReadWrite,  meta = (MakeEditWidget))
    	FTransform ponitB;
    	UPROPERTY(EditAnywhere,BlueprintReadWrite,  meta = (MakeEditWidget))
    	FTransform ponitC;
    
    	UPROPERTY()
    	USceneComponent* root;
    
    	UPROPERTY()
    	TArray<FVector> Points;
    	int32 width, height;
    	FVector eye_loc;
    	float angle;
    
    	FMatrix modelMatrix;
    	FMatrix viewMatrix;
    	FMatrix projectionMatrix;
    };
    
  • AActor_Assignmen1.cpp

    #include "Actor_Assignmen1.h"
    #include "Kismet/KismetSystemLibrary.h"
    #include "Kismet/KismetMathLibrary.h"
    #include "GameFramework/HUD.h"
    #include "Kismet/GameplayStatics.h"
    #include "MyHUD.h"
    
    // Sets default values
    AActor_Assignmen1::AActor_Assignmen1()
    {
    	// 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>("root");
    	SetRootComponent(root);
    
    	// 三個點v0(2.0, 0.0,−2.0), v1(0.0, 2.0,−2.0), v2(−2.0, 0.0,−2.0), 
    	Points.Add(FVector(2.0f, 0, -2.0f));
    	Points.Add(FVector(0, 2.0f,-2.0f));
    	Points.Add(FVector(-2.0f, 0,-2.0f));
    
    	// 初始化
    	width = height = 700;
    	eye_loc = FVector(0, 0, 5);
    	angle = 0;
    
    	modelMatrix.SetIdentity();
    	viewMatrix.SetIdentity();
    	projectionMatrix.SetIdentity();
    
    	ponitA.SetTranslation(Points[0]);
    	ponitB.SetTranslation(Points[1]);
    	ponitC.SetTranslation(Points[2]);	
    }
    
    // Called every frame
    void AActor_Assignmen1::Tick(float DeltaTime)
    {
    	Super::Tick(DeltaTime);
    	FVector2D ViewportSize;
    	GetWorld()->GetGameViewport()->GetViewportSize(ViewportSize);
    	width = height = ViewportSize.X / 2;
    	
    	// 繞z軸
    	//modelMatrix = get_model_matrix(angle);
    
    	//繞任意軸
    	modelMatrix = get_model_matrix_anyAxis(FVector(0, 0, 5), angle);
    	viewMatrix = get_view_matrix(eye_loc);
    	projectionMatrix = get_projection_matrix(45, 1, 0.1, 50);
    
    	RasterizerDraw();
    	angle += 0.5;
    	root->SetWorldRotation(FRotator(0, angle, 0));
    }
    
    FMatrix AActor_Assignmen1::get_view_matrix(FVector eye_pos)
    {
    	FMatrix view = FMatrix::Identity;
    
    	FMatrix translate = FMatrix(
    		FPlane(1, 0, 0, -eye_pos.X),
    		FPlane(0, 1, 0, -eye_pos.Y),
    		FPlane(0, 0, 1, -eye_pos.Z),
    		FPlane(0, 0, 0, 1));
    
    	view = translate * view;
    	return view;
    }
    
    // 繞 Z 軸旋轉
    FMatrix AActor_Assignmen1::get_model_matrix(float rotation_angle)
    {
    	FMatrix model = FMatrix::Identity;
    
    	// TODO: Implement this function
    	// Create the model matrix for rotating the triangle around the Z axis.
    	// Then return it.
    	float fcos = UKismetMathLibrary::DegCos(rotation_angle);
    	float fsin = UKismetMathLibrary::DegSin(rotation_angle);
    	FMatrix rotate = FMatrix(
    		FPlane(fcos, -fsin, 0, 0),
    		FPlane(fsin,  fcos, 0, 0),
    		FPlane(	  0,     0, 1, 0),
    		FPlane(	  0,     0, 0, 1));
    
    	model = rotate * model;
    
    	return model;
    }
    
    // 任意軸旋轉
    FMatrix AActor_Assignmen1::get_model_matrix_anyAxis(FVector axis, float rotation_angle)
    {
    	FMatrix model = FMatrix::Identity;
    
    	axis.Normalize(0.0001);
    	FMatrix N = FMatrix(
    		FPlane(0, -axis.Z, axis.Y, 0),
    		FPlane(axis.Z, 0, -axis.X, 0),
    		FPlane(-axis.Y, axis.X, 0, 0),
    		FPlane(0, 0, 0, 0));
    
    	FMatrix rotate4f = FMatrix::Identity * UKismetMathLibrary::DegCos(rotation_angle);
    
    	// nnt = axis x axis的轉置
    	FMatrix nnT = FMatrix(
    		FPlane(axis.X*axis.X, axis.X*axis.Y, axis.X*axis.Z, 0),
    		FPlane(axis.Y*axis.X, axis.Y*axis.Y, axis.Y*axis.Z, 0),
    		FPlane(axis.Z*axis.X, axis.Z*axis.Y, axis.Z*axis.Z, 0),
    		FPlane(0, 0, 0, 0));
    
    	rotate4f +=  nnT * (1 - UKismetMathLibrary::DegCos(rotation_angle));
    
    	rotate4f += N * UKismetMathLibrary::DegSin(rotation_angle);
    
    	rotate4f.M[3][3] = 1;
    	model = rotate4f * model;
    	return model;
    }
    
    FMatrix AActor_Assignmen1::get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
    {
    	// Students will implement this function
    	FMatrix projection = FMatrix::Identity;
    
    	float t = zNear * UKismetMathLibrary::DegTan(eye_fov / 2);
    	float b = -t;
    	float r = t * aspect_ratio;
    	float l = -r;
    
    	FMatrix translate = FMatrix(
    		FPlane(2 * zNear / (r - l), 0, -(r + l) / (r - l), 0),
    		FPlane(0, 2 * zNear / (t - b), -(t + b) / (t - b), 0),
    		FPlane(0, 0, -(zNear + zFar) / (zNear - zFar), 2 * zNear * zFar / (zNear - zFar)),
    		FPlane(0, 0, 1, 0));
    	projection = translate * projection;
    
    	return projection;
    }
    
    
    void AActor_Assignmen1::RasterizerDraw()
    {
    	FMatrix mvp = projectionMatrix * viewMatrix * modelMatrix;
    
    	float f1 = (100 - 0.1) / 2.0;
    	float f2 = (100 + 0.1) / 2.0;
    	TArray<FVector4> v;
    	for (FVector& p : Points) {
    		v.Add(mvp.GetTransposed().TransformFVector4(FVector4(p.X, p.Y, p.Z, 1.0f)));
    	}
    
    	for (FVector4& vert : v) {
    		vert *= 1/vert.W;
    		vert.X = 0.5 * width * (vert.X + 1.0);
    		vert.Y = 0.5 * height * (vert.Y + 1.0);
    		vert.Z = vert.Z * f1 + f2;
    	}
    
    	TArray<FVector> triangleVerts;
    	for (FVector4& vert : v) {
    		triangleVerts.Add(UKismetMathLibrary::Conv_Vector4ToVector(vert));
    	}
    
    	// 調用AHUD 屏幕繪制函數
    	AMyHUD* myHUD = Cast<AMyHUD>(UGameplayStatics::GetPlayerController(GetWorld(), 0)->GetHUD());
    	if (myHUD) {
    		myHUD->rasterize_wireframe(triangleVerts);
    	}
    	/*
    	UKismetSystemLibrary::PrintString(GetWorld(), triangleVerts[0].ToString());
    	UKismetSystemLibrary::PrintString(GetWorld(), triangleVerts[1].ToString());
    	UKismetSystemLibrary::PrintString(GetWorld(), triangleVerts[2].ToString());
    	UKismetSystemLibrary::PrintString(GetWorld(), TEXT("----------"));
    	*/
    }
    
    // 場景里的繪線
    void AActor_Assignmen1::DrawTriangleIn3D() {
    	UKismetSystemLibrary::DrawDebugLine(GetWorld(), Points[0], Points[1], FLinearColor::Green, 0.02f, .2f);
    	UKismetSystemLibrary::DrawDebugLine(GetWorld(), Points[1], Points[2], FLinearColor::Green, 0.02f, .2f);
    	UKismetSystemLibrary::DrawDebugLine(GetWorld(), Points[2], Points[0], FLinearColor::Green, 0.02f, .2f);
    }
    
  • 創建藍圖派生類,添加面片
    image

創建 AHUD 派生類 AMyHUD

自創建gamemode 並指定HUD,用於繪制屏幕上的線段

  • MyHUD.h

    UCLASS()
    class GAMES101_API AMyHUD : public AHUD
    {
    	GENERATED_BODY()
    
    public:
    	virtual void DrawHUD() override;
    	// 重載用於繪制
    	void rasterize_wireframe(TArray<FVector>& t);
    	// 存儲三角形的三個點
    	TArray<FVector> TriangleVerts;
    };
    
  • MyHUD.cpp

    void AMyHUD::DrawHUD()
    {
    	Super::DrawHUD();
    	if (TriangleVerts.IsValidIndex(0)) {
    		DrawLine(TriangleVerts[0].X, TriangleVerts[0].Y, TriangleVerts[1].X, TriangleVerts[1].Y,FLinearColor::Red);
    		DrawLine(TriangleVerts[1].X, TriangleVerts[1].Y, TriangleVerts[2].X, TriangleVerts[2].Y,FLinearColor::Red);
    		DrawLine(TriangleVerts[2].X, TriangleVerts[2].Y, TriangleVerts[0].X, TriangleVerts[0].Y,FLinearColor::Red);		
    	}	
    }
    
    void AMyHUD::rasterize_wireframe(TArray<FVector>& t)
    {
    	TriangleVerts = t;
    }
    

效果

image


免責聲明!

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



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