【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)
实现
投影变换矩阵

模型任意轴旋转

代码
创建 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 模型、视图、投影变换的更多相关文章
- WebGL简易教程(六):第一个三维示例(使用模型视图投影变换)
目录 1. 概述 2. 示例:绘制多个三角形 2.1. Triangle_MVPMatrix.html 2.2. Triangle_MVPMatrix.js 2.2.1. 数据加入Z值 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 ...
- WebGL或OpenGL关于模型视图投影变换的设置技巧
目录 1. 具体实例 2. 解决方案 1) Cube.html 2) Cube.js 3) 运行结果 3. 详细讲解 1) 模型变换 2) 视图变换 3) 投影变换 4) 模型视图投影矩阵 4. 存在 ...
- 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色
总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...
- 【UE4】GAMES101 图形学作业2:光栅化和深度缓存
总览 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣.所以这一次我们继续推进一步--在屏幕上画出一个实心三角形,换言之,栅格化一个三角形.上一次作业中,在视口变化之后,我 ...
- 【UE4】GAMES101 图形学作业5:光线与物体相交(球、三角面)
总览 在这部分的课程中,我们将专注于使用光线追踪来渲染图像.在光线追踪中最重要的操作之一就是找到光线与物体的交点.一旦找到光线与物体的交点,就可以执行着色并返回像素颜色. 在这次作业中,我们要实现两个 ...
- 【UE4】GAMES101 图形学作业4:贝塞尔曲线
总览 Bézier 曲线是一种用于计算机图形学的参数曲线. 在本次作业中,你需要实现de Casteljau 算法来绘制由4 个控制点表示的Bézier 曲线(当你正确实现该算法时,你可以支持绘制由更 ...
- 【UE4】GAMES101 图形学作业0:矩阵初识
作业描述 给定一个点P=(2,1), 将该点绕原点先逆时针旋转45◦,再平移(1,2), 计算出变换后点的坐标(要求用齐次坐标进行计算). UE4 知识点 主要矩阵 FMatrix FBasisVec ...
- OpenGL(五) 三维变换之模型视图矩阵
计算机三维图形学中,一个基本的任务是如何描述三维空间中一个物体位置的变化,也就是如何 描述物体的运动.通常情况下,物体位置的变化包含三个基本的变化:平移.旋转和缩放,物体的运动也可以用这三个基本的运动 ...
随机推荐
- 并发编程之:CountDownLatch
大家好,我是小黑,一个在互联网苟且偷生的农民工. 先问大家一个问题,在主线程中创建多个线程,在这多个线程被启动之后,主线程需要等子线程执行完之后才能接着执行自己的代码,应该怎么实现呢? Thread. ...
- JDK1.8源码(六)——java.util.ArrayList类
ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Clonea ...
- C#新版本风格(NetCore)项目文件
在VisualStudio中创建NetCore以上版本的项目,使用的都是新版本风格的项目文件. 和旧版本.NetFramework版本的项目文件区别: 双击项目可直接打开csproj文件进行编辑配置 ...
- C语言中动态内存分配的本质是什么?
摘要:C语言中比较重要的就是指针,它可以用来链表操作,谈到链表,很多时候为此分配内存采用动态分配而不是静态分配. 本文分享自华为云社区<[云驻共创]C语言中动态内存分配的本质>,作者: G ...
- 20210809 Merchant,Equation,Rectangle
做过,但当时咕了 T3 Merchant 先特判 \(t=0\),之后斜率一定会起作用. 考虑最终选择的物品集合,它们的斜率和一定大于 \(0\),因此答案具有单调性,可以二分. 实现的时候注意细节 ...
- openswan协商流程之(二):main_inI1_outR1()
主模式第二包:main_inI1_outR1() 文章目录 主模式第二包:main_inI1_outR1() 1. 序言 2. `main_inI1_outR1()`处理流程图 3. `main_in ...
- openswan发送状态机分析
openswan发送状态机分析 1. 函数调用关系 2. 函数说明 如果按用户空间.内核空间划分的话,此部分代码更多是运行在内核空间的. 2.1 ipsec_tunnel_init_devices() ...
- 【PHP数据结构】图的应用:最短路径
上篇文章的最小生成树有没有意犹未尽的感觉呀?不知道大家掌握得怎么样,是不是搞清楚了普里姆和克鲁斯卡尔这两种算法的原理了呢?面试的时候如果你写不出,至少得说出个大概来吧,当然,如果你是要考研的学生,那就 ...
- use关键字在PHP中的几种用法
在学习了和使用了这么多年的PHP之后,您知道use这个关键字在PHP中都有哪些用法吗?今天我们就来看一下它的三种常见用法. 1. 用于命名空间的别名引用 // 命名空间 include 'namesp ...
- Java基础系列(10)- 类型转换
类型转换 由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换.运算中,不同类型的数据先转换为同一类型,然后进行运算. 低 ------------------------------ ...