总览

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

    为什么Class实例可以不是全局唯一的 通过定义两个类加载器加载同一字节码文件来证明Class实例为什么不是全局唯一的 1.将一个名为Demo(没有后缀)的字节码文件放在D盘根目录 2.定义两个类加载 ...

  2. OpenCV 之 透视 n 点问题

    透视 n 点问题,源自相机标定,是计算机视觉的经典问题,广泛应用在机器人定位.SLAM.AR/VR.摄影测量等领域 1  PnP 问题 1.1  定义 已知:相机的内参和畸变系数:世界坐标系中,n 个 ...

  3. springMVC学习总结(一) --springMVC搭建

    springMVC学习总结(一) --springMVC搭建 搭建项目 1.创建一个web项目,并在项目中的src文件夹下创建一个包com.myl.controller. 2.添加相应jar包 3.在 ...

  4. QT程序打包成多平台可执行文件

    一.简述 QT项目开发完成后,需要打包发布程序,在实际生产中不可能把源码发给别人,所以需要将源码打包正可执行文件或者安装程序. 二.设置应用图标 把 ico 文件放到源代码目录下,在QT项目中的'.p ...

  5. Element 对话框简单使用

    官方文档介绍的是页内对话框,但没有基于组件的对话框,这里记录一下,原理就是父子传值是否显示 父页导入组件 <template> <div class="home" ...

  6. key存活时间和五个类型通用的一些指令操作

    一.设置key的存活时间 1.失效后 ttl 是 -2,get返回 null : 2.不设置存活时候 ttl 返回是 -1: 二.查找指令和删除指令.重命名key.查看key类型 1.模糊查询 2.严 ...

  7. Django学习day05随堂笔记

    每日测验 """ 今日考题 1.反向解析的本质是什么,无名和有名反向解析如何操作? 2..路由分发能够实现的前提是什么,需要注意什么,名称空间什么时候使用 3..什么是虚 ...

  8. vue报错 Uncaught TypeError: Cannot read property of null

    有可能是点击a标签,但是a标签有click事件,未阻止默认事件导致报错,开始都看不出来是什么错误

  9. Jmeter系列(25)- 常用逻辑控制器 (4) | Include控制器Include Controller

    认识 Include Controller Include Controller :译为包含控制器,用来添加 Test Fragment(测试片段).具体是什么意思呢,我们先来了解下 Test Fra ...

  10. Promise源码实现与测试

    const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected' class MyPromise { construc ...