总览

  • 到目前为止,我们已经学习了如何使用矩阵变换来排列二维或三维空间中的对象。所以现在是时候通过实现一些简单的变换矩阵来获得一些实际经验了。在接下来的三次作业中,我们将要求你去模拟一个基于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)

实现

  • 投影变换矩阵

  • 模型任意轴旋转

代码

创建 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);
    }
  • 创建蓝图派生类,添加面片

创建 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;
    }

效果

【UE4】GAMES101 图形学作业1:mvp 模型、视图、投影变换的更多相关文章

  1. WebGL简易教程(六):第一个三维示例(使用模型视图投影变换)

    目录 1. 概述 2. 示例:绘制多个三角形 2.1. Triangle_MVPMatrix.html 2.2. Triangle_MVPMatrix.js 2.2.1. 数据加入Z值 2.2.2. ...

  2. three.js中的矩阵变换(模型视图投影变换)

    目录 1. 概述 2. 基本变换 2.1. 矩阵运算 2.2. 模型变换矩阵 2.2.1. 平移矩阵 2.2.2. 旋转矩阵 2.2.2.1. 绕X轴旋转矩阵 2.2.2.2. 绕Y轴旋转矩阵 2.2 ...

  3. WebGL或OpenGL关于模型视图投影变换的设置技巧

    目录 1. 具体实例 2. 解决方案 1) Cube.html 2) Cube.js 3) 运行结果 3. 详细讲解 1) 模型变换 2) 视图变换 3) 投影变换 4) 模型视图投影矩阵 4. 存在 ...

  4. 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色

    总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...

  5. 【UE4】GAMES101 图形学作业2:光栅化和深度缓存

    总览 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣.所以这一次我们继续推进一步--在屏幕上画出一个实心三角形,换言之,栅格化一个三角形.上一次作业中,在视口变化之后,我 ...

  6. 【UE4】GAMES101 图形学作业5:光线与物体相交(球、三角面)

    总览 在这部分的课程中,我们将专注于使用光线追踪来渲染图像.在光线追踪中最重要的操作之一就是找到光线与物体的交点.一旦找到光线与物体的交点,就可以执行着色并返回像素颜色. 在这次作业中,我们要实现两个 ...

  7. 【UE4】GAMES101 图形学作业4:贝塞尔曲线

    总览 Bézier 曲线是一种用于计算机图形学的参数曲线. 在本次作业中,你需要实现de Casteljau 算法来绘制由4 个控制点表示的Bézier 曲线(当你正确实现该算法时,你可以支持绘制由更 ...

  8. 【UE4】GAMES101 图形学作业0:矩阵初识

    作业描述 给定一个点P=(2,1), 将该点绕原点先逆时针旋转45◦,再平移(1,2), 计算出变换后点的坐标(要求用齐次坐标进行计算). UE4 知识点 主要矩阵 FMatrix FBasisVec ...

  9. OpenGL(五) 三维变换之模型视图矩阵

    计算机三维图形学中,一个基本的任务是如何描述三维空间中一个物体位置的变化,也就是如何 描述物体的运动.通常情况下,物体位置的变化包含三个基本的变化:平移.旋转和缩放,物体的运动也可以用这三个基本的运动 ...

随机推荐

  1. 安装redis 6.0.6

    1.规划目录:下载目录.安装目录.redis数据目录mkdir -p /data/appmkdir -p /opt/redis_cluster/redis_6379/{conf,logs,pid}mk ...

  2. 20210712 noip12

    考场 第一次和 hzoi 联考,成功给 sdfz 丢人 尝试戴耳罩,发现太紧了... 决定改变策略,先用1h看题,想完3题再写. T1 一下想到枚举最大值,单调栈求出每个点能作为最大值的区间,然后以这 ...

  3. 算法:实现strStr(),字符串indexOf方法

    描述 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返回  -1. 个人思路: ...

  4. ☕【Java技术指南】「并发编程专题」CompletionService框架基本使用和原理探究(基础篇)

    前提概要 在开发过程中在使用多线程进行并行处理一些事情的时候,大部分场景在处理多线程并行执行任务的时候,可以通过List添加Future来获取执行结果,有时候我们是不需要获取任务的执行结果的,方便后面 ...

  5. ABP VNext发布遇到的坑

    本地调试没有问题,发布后通过Token调用其他API时,出现返回JSON中提示:Authorization failed! Given policy has not granted. 需要修改apps ...

  6. weblogic漏洞分析之CVE-2016-0638

    weblogic漏洞分析之CVE-2016-0638 一.环境搭建: 这里使用前一篇文章的环境,然后打上补丁 上一篇文章:https://www.cnblogs.com/yyhuni/p/151370 ...

  7. Tars | 第8篇 TarsJava Subset最终代码的执行流程与原理分析

    目录 前言 1. SubsetConf配置项的结构 1.1 SubsetConf 1.2 RatioConfig 1.3 KeyConfig 1.4 KeyRoute 1.5 SubsetConf的结 ...

  8. vue-cli 项目中使用 v-chart 及导出 chart 图片

    安装: npm i v-charts echarts -S 组件中使用: 1 <template> 2 <div class="app-chart"> 3 ...

  9. 为什么在匿名内部类中引用外部对象要加final修饰符

    当所在的方法的形参需要被内部类里面使用时,该形参必须为final. 为什么必须要为final呢? 首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一clas ...

  10. pip国内源设置

    在目录 C:\Users\Administrator下新建pip目录 C:\Users\Administrator\pip 添加 pip.ini 文件 pip.ini内容设置为 [global] in ...