【UE4】GAMES101 图形学作业2:光栅化和深度缓存
总览
- 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(const Triangle& t)。
- 但这一次,你需要自己填写并调用函数
rasterize_triangle(const Triangle& t)
。该函数的内部工作流程如下:- 创建三角形的2 维bounding box。
- 遍历此bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
- 如果在内部,则将其位置处的插值深度值(interpolated depth value) 与深度缓冲区(depth buffer) 中的相应值进行比较。
- 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区(depth buffer)。你需要修改的函数如下:
rasterize_triangle()
: 执行三角形栅格化算法static bool insideTriangle()
: 测试点是否在三角形内。
你可以修改此函数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。因为我们只知道三角形三个顶点处的深度值,所以对于三角形内部的像素,我们需要用插值的方法得到其深度值。我们已经为你处理好了这一部分,因为有关这方面的内容尚未在课程中涉及。插值的深度值被储存在变量z_interpolated中。
- 注意
- 请注意我们是如何初始化depth buffer 和注意z values 的符号。为了方便同学们写代码,我们将z 进行了反转,保证都是正数,并且越大表示离视点越远。
- 在你自己的计算机或虚拟机上下载并使用我们更新的框架代码。你会注意到,在main.cpp 下的get_projection_matrix() 函数是空的。请复制粘贴你在第一次作业中的实现来填充该函数。
基础与提高
- [20 分] 正确实现三角形栅格化算法。
- [10 分] 正确测试点是否在三角形内。
- [10 分] 正确实现z-buffer 算法, 将三角形按顺序画在屏幕上。
- [提高项5 分] 用super-sampling 处理Anti-aliasing
你可能会注意到,当我们放大图像时,图像边缘会有锯齿感。我们可以用super-sampling来解决这个问题,即对每个像素进行2 * 2 采样,并比较前后的结果(这里并不需要考虑像素与像素间的样本复用)。需要注意的点有,对于像素内的每一个样本都需要维护它自己的深度值,即每一个像素都需要维护一个samplelist。最后,如果你实现正确的话,你得到的三角形不应该有不正常的黑边。
UE4实现
效果
静态展示
由于绘制看不出锯齿优化效果,因此在像素点取点间隔略作处理,每五个点取一个像素点,如果在三角形内,则自动将未扫描的五个点补全。
代码结构说明
- class AActor_Assignment2 作为测试,相当于main入口
- class URasterizer2Widget 作为显示
- class Rasterizer2 作为光栅器 (从原代码框架移植移植)
rasterize_triangle()
: 执行三角形栅格化算法static bool insideTriangle()
: 测试点是否在三角形内。
- class Triangle2 作为三角形 (从原代码框架移植移植)
代码
class AActor_Assignment2
Actor_Assignment2 .h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Rasterizer2.h"
#include "Rasterizer2Widget.h"
#include "Actor_Assignment2.generated.h" UCLASS()
class GAMES101_API AActor_Assignment2 : public AActor
{
GENERATED_BODY() public:
// Sets default values for this actor's properties
AActor_Assignment2(); protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override; public:
// Called every frame
virtual void Tick(float DeltaTime) override; 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 TextureFromImage_Internal(const TArray<FColor>& SrcData, const bool UseAlpha = false); void SwitchMASS() { m_rasterizer->SwitchMASS();UKismetSystemLibrary::PrintString(GetWorld(), TEXT("SwitchMASS")); } public:
UPROPERTY(BlueprintReadWrite)
TArray<FVector> pos; UPROPERTY(BlueprintReadWrite)
TArray<FIntVector> ind; UPROPERTY(BlueprintReadWrite)
TArray<FVector> cols; UPROPERTY(EditAnywhere)
FVector2D CanvasSize; UPROPERTY(EditAnywhere)
TSubclassOf<URasterizer2Widget> RasterizerWidgetClass; UPROPERTY()
URasterizer2Widget* screenUi; UPROPERTY(VisibleAnywhere)
UTexture2D* T_Result; private:
rst::pos_buf_id pos_id;
rst::ind_buf_id ind_id;
rst::col_buf_id col_id;
float angle;
FVector eye_pos;
private:
TSharedPtr<rst::Rasterizer2> m_rasterizer;
};
Actor_Assignment2 .cpp
#include "Actor_Assignment2.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h" // Sets default values
AActor_Assignment2::AActor_Assignment2()
{
// 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;
CanvasSize = FVector2D(700, 700);
} // Called when the game starts or when spawned
void AActor_Assignment2::BeginPlay()
{
Super::BeginPlay(); angle = 0;
eye_pos = { 0,0,5 }; pos = {
{2, 0, -2},
{0, 2, -2},
{-2, 0, -2},
{3.5, -1, -5},
{2.5, 1.5, -5},
{-1, 0.5, -5}
}; ind = {
{0, 1, 2},
{3, 4, 5}
}; cols = {
{217.0, 238.0, 185.0},
{217.0, 238.0, 185.0},
{217.0, 238.0, 185.0},
{185.0, 217.0, 238.0},
{185.0, 217.0, 238.0},
{185.0, 217.0, 238.0}
}; m_rasterizer = MakeShareable(new rst::Rasterizer2(CanvasSize.X, CanvasSize.Y));
pos_id = m_rasterizer->load_positions(pos);
ind_id = m_rasterizer->load_indices(ind);
col_id = m_rasterizer->load_colors(cols); if (RasterizerWidgetClass != nullptr) {
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
PC->SetShowMouseCursor(true);
screenUi = CreateWidget<URasterizer2Widget>(PC, RasterizerWidgetClass);
T_Result = UTexture2D::CreateTransient(CanvasSize.X, CanvasSize.Y, PF_B8G8R8A8);
screenUi->TCanvas->SetBrushFromTexture(T_Result);
screenUi->AddToViewport();
}
else {
UKismetSystemLibrary::PrintString(GetWorld(), TEXT("RasterizerWidgetClass is null"));
}
} // Called every frame
void AActor_Assignment2::Tick(float DeltaTime)
{
Super::Tick(DeltaTime); m_rasterizer->clear(rst::Buffers::Color | rst::Buffers::Depth); //angle += 0.2f;
m_rasterizer->set_model(get_model_matrix_anyAxis(FVector(0, 0.2, 1), angle));
m_rasterizer->set_view(get_view_matrix(eye_pos));
m_rasterizer->set_projection(get_projection_matrix(45, 1, 0.1, 50)); //此处 Znear 和 Zfar 取负号 m_rasterizer->draw(pos_id, ind_id, col_id, rst::Primitive::Triangle);
TextureFromImage_Internal(m_rasterizer->frame_buffer());
} FMatrix AActor_Assignment2::get_view_matrix(FVector eye_loc)
{
FMatrix view = FMatrix::Identity; FMatrix translate = FMatrix(
FPlane(1, 0, 0, -eye_loc.X),
FPlane(0, 1, 0, -eye_loc.Y),
FPlane(0, 0, 1, -eye_loc.Z),
FPlane(0, 0, 0, 1)); view = translate * view;
return view;
} // 绕 Z 轴旋转
FMatrix AActor_Assignment2::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_Assignment2::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_Assignment2::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;
} // 修改Texture里的内容
void AActor_Assignment2::TextureFromImage_Internal( const TArray<FColor>& SrcData, const bool UseAlpha)
{
const int32 SrcWidth = CanvasSize.X;
const int32 SrcHeight = CanvasSize.Y;
// Create the texture //T_Result->ReleaseResource();
// Lock the texture so it can be modified
uint8* MipData = static_cast<uint8*>(T_Result->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE)); // Create base mip.
uint8* DestPtr = NULL;
const FColor* SrcPtr = NULL;
for (int32 y = 0; y < SrcHeight; y++)
{
DestPtr = &MipData[(SrcHeight - 1 - y) * SrcWidth * sizeof(FColor)];
SrcPtr = const_cast<FColor*>(&SrcData[(SrcHeight - 1 - y) * SrcWidth]);
for (int32 x = 0; x < SrcWidth; x++)
{
*DestPtr++ = SrcPtr->B;
*DestPtr++ = SrcPtr->G;
*DestPtr++ = SrcPtr->R;
if (UseAlpha)
{
*DestPtr++ = SrcPtr->A;
}
else
{
*DestPtr++ = 0xFF;
}
SrcPtr++;
}
} // Unlock the texture
T_Result->PlatformData->Mips[0].BulkData.Unlock();
T_Result->UpdateResource();
}
class Rasterizer2
Rasterizer2.h
#pragma once
#include "CoreMinimal.h"
#include "Triangle2.h"
#include "Kismet/KismetSystemLibrary.h" namespace rst
{
enum class Buffers
{
Color = 1,
Depth = 2
}; inline Buffers operator|(Buffers a, Buffers b)
{
return Buffers((int)a | (int)b);
} inline Buffers operator&(Buffers a, Buffers b)
{
return Buffers((int)a & (int)b);
} enum class Primitive
{
Line,
Triangle
}; /*
* For the curious : The draw function takes two buffer id's as its arguments. These two structs
* make sure that if you mix up with their orders, the compiler won't compile it.
* Aka : Type safety
* */
struct pos_buf_id
{
int pos_id = 0;
}; struct ind_buf_id
{
int ind_id = 0;
}; struct col_buf_id
{
int col_id = 0;
}; class GAMES101_API Rasterizer2
{
public: Rasterizer2(int w, int h);
pos_buf_id load_positions(const TArray<FVector>& positions);
ind_buf_id load_indices(const TArray<FIntVector>& indices);
col_buf_id load_colors(const TArray<FVector>& colors); void set_model(const FMatrix& m);
void set_view(const FMatrix& v);
void set_projection(const FMatrix& p); void set_pixel(const FVector& point, const FVector& color);
void clear(Buffers buff);
void draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type); TArray<FColor>& frame_buffer() { return frame_buf; }
void SwitchMASS() { bMASS = !bMASS;} private:
//void draw_line(Eigen::Vector3f begin, Eigen::Vector3f end);
void rasterize_triangle(const Triangle2& t);
// VERTEX SHADER -> MVP -> Clipping -> /.W -> VIEWPORT -> DRAWLINE/DRAWTRI -> FRAGSHADER private:
FMatrix model;
FMatrix view;
FMatrix projection; TMap<int,TArray<FVector>> pos_buf;
TMap<int,TArray<FIntVector>> ind_buf;
TMap<int,TArray<FVector>> col_buf; TArray<FColor> frame_buf;
TArray<float> depth_buf; int get_index(int x, int y); int width, height; int next_id = 0;
int get_next_id() { return next_id++; } bool bMASS = false;
};
}
Rasterizer2.cpp
#include "Rasterizer2.h"
#include <tuple>
#include <cmath>
#include "Kismet/KismetMathLibrary.h"
// Sets default values rst::pos_buf_id rst::Rasterizer2::load_positions(const TArray<FVector>& positions)
{
auto id = get_next_id();
pos_buf.Emplace(id, positions);
return { id };
} rst::ind_buf_id rst::Rasterizer2::load_indices(const TArray<FIntVector>& indices)
{
auto id = get_next_id();
ind_buf.Emplace(id, indices);
return { id };
} rst::col_buf_id rst::Rasterizer2::load_colors(const TArray<FVector>& cols)
{
auto id = get_next_id();
col_buf.Emplace(id, cols);
return { id };
} auto to_vec4(const FVector& v3, float w = 1.0f)
{
return FVector4(v3.X, v3.Y, v3.Z, w);
} static bool insideTriangle(float x, float y, const TArray<FVector>& _v)
{
// TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
FVector2D point=FVector2D(x, y); FVector2D v0 = FVector2D(_v[0].X, _v[0].Y);
FVector2D v1 = FVector2D(_v[1].X, _v[1].Y);
FVector2D v2 = FVector2D(_v[2].X, _v[2].Y); FVector2D AB = v1 - v0;
FVector2D BC = v2 - v1;
FVector2D CA = v0 - v2; FVector2D AP = point - v0;
FVector2D BP = point - v1;
FVector2D CP = point - v2; return UKismetMathLibrary::CrossProduct2D(AB, AP) > 0
&& UKismetMathLibrary::CrossProduct2D(BC, BP) > 0
&& UKismetMathLibrary::CrossProduct2D(CA, CP) > 0;
} static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const TArray<FVector>& v)
{
float c1 = (x * (v[1].Y - v[2].Y) + (v[2].X - v[1].X) * y + v[1].X * v[2].Y - v[2].X * v[1].Y) / (v[0].X * (v[1].Y - v[2].Y) + (v[2].X - v[1].X) * v[0].Y + v[1].X * v[2].Y - v[2].X * v[1].Y);
float c2 = (x * (v[2].Y - v[0].Y) + (v[0].X - v[2].X) * y + v[2].X * v[0].Y - v[0].X * v[2].Y) / (v[1].X * (v[2].Y - v[0].Y) + (v[0].X - v[2].X) * v[1].Y + v[2].X * v[0].Y - v[0].X * v[2].Y);
float c3 = (x * (v[0].Y - v[1].Y) + (v[1].X - v[0].X) * y + v[0].X * v[1].Y - v[1].X * v[0].Y) / (v[2].X * (v[0].Y - v[1].Y) + (v[1].X - v[0].X) * v[2].Y + v[0].X * v[1].Y - v[1].X * v[0].Y);
return { c1,c2,c3 };
} void rst::Rasterizer2::draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type)
{
auto& buf = pos_buf[pos_buffer.pos_id];
auto& ind = ind_buf[ind_buffer.ind_id];
auto& col = col_buf[col_buffer.col_id]; float f1 = (50 - 0.1) / 2.0;
float f2 = (50 + 0.1) / 2.0; FMatrix mvp = projection * view * model;
for (auto& i : ind)
{
Triangle2 t;
TArray<FVector4> v;
v.Add(mvp.GetTransposed().TransformFVector4(to_vec4(buf[i[0]], 1.0f)));
v.Add(mvp.GetTransposed().TransformFVector4(to_vec4(buf[i[1]], 1.0f)));
v.Add(mvp.GetTransposed().TransformFVector4(to_vec4(buf[i[2]], 1.0f))); //Homogeneous division
for (auto& vec : v) {
vec *= 1 / vec.W;
}
//Viewport transformation
for (auto& vert : v)
{
vert.X = 0.5 * width * (vert.X + 1.0);
vert.Y = 0.5 * height * (vert.Y + 1.0);
vert.Z = vert.Z * f1 + f2;
} for (int j = 0; j < 3; ++j)
{
t.setVertex(j, UKismetMathLibrary::Conv_Vector4ToVector(v[j]));
//t.setVertex(j, UKismetMathLibrary::Conv_Vector4ToVector(v[j]));
//t.setVertex(j, UKismetMathLibrary::Conv_Vector4ToVector(v[j]));
} auto col_x = col[i[0]];
auto col_y = col[i[1]];
auto col_z = col[i[2]]; t.setColor(0, col_x[0], col_x[1], col_x[2]);
t.setColor(1, col_y[0], col_y[1], col_y[2]);
t.setColor(2, col_z[0], col_z[1], col_z[2]); rasterize_triangle(t);
}
} //Screen space rasterization
void rst::Rasterizer2::rasterize_triangle(const Triangle2& t) {
auto v = t.toVector4(); // 画出三角形所在边界
// x_l = x_min ; x_r = x_max ; y_b = y_min ; y_t = y_max
int x_l = std::floor(std::min(v[0].X, std::min(v[1].X, v[2].X)));
int x_r = std::ceil(std::max(v[0].X, std::max(v[1].X, v[2].X)));
int y_b = std::floor(std::min(v[0].Y , std::min(v[1].Y, v[2].Y)));
int y_t = std::ceil(std::max(v[0].Y, std::max(v[1].Y, v[2].Y))); if (bMASS) {
// 四等分后中心点,如果像素间隔扩大,中心也相对变化
TArray<FVector2D> posOffset = { {0.25,0.25},{0.25,0.75},{0.75,0.25},{0.75,0.75} }; //由于过于清晰,可在此处将递增改为 5 或 10,相应四等分中心点也会变化
for (int x = x_l; x <= x_r; x++)
for (int y = y_b; y <= y_t; y++) { float minDepth = TNumericLimits<float>::Max();
float percentage = 0; for (int i = 0; i < 4; i++) {
if (insideTriangle((float)x + posOffset[i].X, (float)y + posOffset[i].Y, t.v)) {
//重心坐标插值
auto BarycentricParam = computeBarycentric2D((float)x + posOffset[i].X, (float)y + posOffset[i].Y, t.v);
float alpha = std::get<0>(BarycentricParam);
float beta = std::get<1>(BarycentricParam);
float gamma = std::get<2>(BarycentricParam); float w_reciprocal = 1.0f / (alpha / v[0].W + beta / v[1].W + gamma / v[2].W);
float z_interpolated = alpha * v[0].Z / v[0].W + beta * v[1].Z / v[1].W + gamma * v[2].Z / v[2].W;
z_interpolated *= w_reciprocal;
minDepth = std::min(minDepth, z_interpolated);
percentage += 0.25f;
}
}
if (percentage > 0 && depth_buf[get_index(x, y)] > minDepth) {
// 递增改变的话,应补充未被扫面的部分
FVector color = t.getColor() * percentage;
FVector point = FVector((float)x, (float)y, minDepth);
depth_buf[get_index(x, y)] = minDepth;
set_pixel(point, color);
}
}
}
else {
for (int x = x_l; x <= x_r; x++)
for (int y = y_b; y <= y_t; y++) { if (insideTriangle((float)x + 0.5, (float)y + 0.5, t.v)) {
//重心坐标插值
auto BarycentricParam = computeBarycentric2D((float)x + 0.5f, (float)y + 0.5f, t.v);
float alpha = std::get<0>(BarycentricParam);
float beta = std::get<1>(BarycentricParam);
float gamma = std::get<2>(BarycentricParam); float w_reciprocal = 1.0f / (alpha / v[0].W + beta / v[1].W + gamma / v[2].W);
float z_interpolated = alpha * v[0].Z / v[0].W + beta * v[1].Z / v[1].W + gamma * v[2].Z / v[2].W;
z_interpolated *= w_reciprocal; if (depth_buf[get_index(x, y)] > z_interpolated) {
FVector color = t.getColor();
FVector point = FVector((float)x, (float)y, z_interpolated);
depth_buf[get_index(x, y)] = z_interpolated;
set_pixel(point, color);
}
}
}
}
} void rst::Rasterizer2::set_model(const FMatrix& m)
{
model = m;
} void rst::Rasterizer2::set_view(const FMatrix& v)
{
view = v;
} void rst::Rasterizer2::set_projection(const FMatrix& p)
{
projection = p;
} void rst::Rasterizer2::clear(rst::Buffers buff)
{
if ((buff & rst::Buffers::Color) == rst::Buffers::Color)
{
for (FColor& item : frame_buf) {
item = FColor(0, 0, 0, 0);
}
}
if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
{
for (float& item : depth_buf) {
item = TNumericLimits<float>::Max();
}
}
} rst::Rasterizer2::Rasterizer2(int w, int h) : width(w), height(h)
{
frame_buf.SetNum(w * h);
depth_buf.SetNum(w * h);
} int rst::Rasterizer2::get_index(int x, int y)
{
return (height - 1 - y) * width + x;
} void rst::Rasterizer2::set_pixel(const FVector& point, const FVector& color)
{
//old index: auto ind = point.y() + point.x() * width;
auto ind = (height - 1 - point.Y) * width + point.X;
frame_buf[ind] = FColor(color.X, color.Y, color.Z, 255); }
class Triangle2
Triangle2.h
#pragma once
#include "CoreMinimal.h" class GAMES101_API Triangle2
{
public:
Triangle2();
~Triangle2(); public:
TArray<FVector> v; /*the original coordinates of the triangle, v0, v1, v2 in counter clockwise order*/
/*Per vertex values*/
FVector color[3]; //color at each vertex;
FVector2D tex_coords[3]; //texture u,v
FVector normal[3]; //normal vector for each vertex void setVertex(int ind, FVector ver); /*set i-th vertex coordinates */
void setNormal(int ind, FVector n); /*set i-th vertex normal vector*/
void setColor(int ind, float r, float g, float b); /*set i-th vertex color*/
FVector getColor() const { return color[0] * 255; } // Only one color per triangle.
void setTexCoord(int ind, float s, float t); /*set i-th vertex texture coordinate*/
TArray<FVector4> toVector4() const;
};
Triangle2.cpp
#include "Triangle2.h" Triangle2::Triangle2()
{
v.Add(FVector::ZeroVector);
v.Add(FVector::ZeroVector);
v.Add(FVector::ZeroVector); color[0] = color[1] = color[2] = FVector::ZeroVector;
tex_coords[0] = tex_coords[1] = tex_coords[2] = FVector2D::ZeroVector;
} Triangle2::~Triangle2(){} void Triangle2::setVertex(int ind, FVector ver) {
v[ind] = ver;
} void Triangle2::setNormal(int ind, FVector n) {
normal[ind] = n;
} void Triangle2::setColor(int ind, float r, float g, float b) {
if ((r < 0.0) || (r > 255.) ||
(g < 0.0) || (g > 255.) ||
(b < 0.0) || (b > 255.)) {
fprintf(stderr, "ERROR! Invalid color values");
fflush(stderr);
exit(-1);
} color[ind] = FVector((float)r / 255., (float)g / 255., (float)b / 255.);
return;
} void Triangle2::setTexCoord(int ind, float s, float t) {
tex_coords[ind] = FVector2D(s, t);
} TArray<FVector4> Triangle2::toVector4() const
{
TArray<FVector4> res;
for(int i = 0; i < 3; i++)
{
res.Add(FVector4(v[i].X, v[i].Y, v[i].Z, 1.0f));
}
return res;
}
class URasterizer2Widget
Rasterizer2Widget.h
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Slate/SlateBrushAsset.h"
#include "Components/Image.h"
#include "Components/Button.h"
#include "Rasterizer2Widget.generated.h" UCLASS()
class GAMES101_API URasterizer2Widget : public UUserWidget
{
GENERATED_BODY()
protected:
virtual void NativePreConstruct();
//virtual int32 NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
public:
//UFUNCTION(BlueprintCallable,Category="GAMES101 | 2")
//void DrawBackground(FPaintContext& Context) const; UFUNCTION()
void SwitchMASS(); public:
UPROPERTY()
USlateBrushAsset* SlateBrushAsset; UPROPERTY(EditAnywhere, meta = (BindWidget))
UImage* TCanvas; UPROPERTY(EditAnywhere, BlueprintReadWrite , meta = (BindWidget))
UButton* Btn_Mass; private:
FVector2D CanvasSize;
bool bDrawSwitch = false;
int32 drawInterval = 10; };
Rasterizer2Widget.cpp
#include "Rasterizer2Widget.h"
#include "Blueprint/WidgetBlueprintLibrary.h"
#include "Actor_Assignment2.h"
#include "Kismet/GameplayStatics.h" void URasterizer2Widget::NativePreConstruct()
{
Super::NativePreConstruct();
PreConstruct(IsDesignTime());
if (Btn_Mass!=nullptr)
{
Btn_Mass->OnClicked.AddDynamic(this, &URasterizer2Widget::SwitchMASS);
UKismetSystemLibrary::PrintString(GetWorld(), TEXT("NativeConstruct"));
}
}
void URasterizer2Widget::SwitchMASS()
{
TArray<AActor*> OutActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor_Assignment2::StaticClass(), OutActors);
if (OutActors.IsValidIndex(0)) {
Cast<AActor_Assignment2>(OutActors[0])->SwitchMASS();
}
}
/*
int32 URasterizer2Widget::NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
if (bHasScriptImplementedPaint)
{
FPaintContext Context(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
//OnPaint(Context);
//DrawBackground(Context);
return FMath::Max(LayerId, Context.MaxLayer);
}
return LayerId;
} void URasterizer2Widget::DrawBackground(FPaintContext& Context) const
{
if (CanvasSize == FVector2D::ZeroVector) return; UWidgetBlueprintLibrary::DrawBox(Context, FVector2D::ZeroVector, CanvasSize, SlateBrushAsset, FColor(0, 0, 0, 0.6 * 255));
for (int i = 0; i <= CanvasSize.X / 20; i++) {
int start = i * 20;
UWidgetBlueprintLibrary::DrawLine(Context, FVector2D(start, 0), FVector2D(start, CanvasSize.Y), FColor(120, 120, 120, 255));
UWidgetBlueprintLibrary::DrawLine(Context, FVector2D(0, start), FVector2D(CanvasSize.X, start), FColor(120, 120, 120, 255));
}
}
*/
【UE4】GAMES101 图形学作业2:光栅化和深度缓存的更多相关文章
- 【UE4】GAMES101 图形学作业1:mvp 模型、视图、投影变换
总览 到目前为止,我们已经学习了如何使用矩阵变换来排列二维或三维空间中的对象.所以现在是时候通过实现一些简单的变换矩阵来获得一些实际经验了.在接下来的三次作业中,我们将要求你去模拟一个基于CPU 的光 ...
- 【UE4】GAMES101 图形学作业5:光线与物体相交(球、三角面)
总览 在这部分的课程中,我们将专注于使用光线追踪来渲染图像.在光线追踪中最重要的操作之一就是找到光线与物体的交点.一旦找到光线与物体的交点,就可以执行着色并返回像素颜色. 在这次作业中,我们要实现两个 ...
- 【UE4】GAMES101 图形学作业4:贝塞尔曲线
总览 Bézier 曲线是一种用于计算机图形学的参数曲线. 在本次作业中,你需要实现de Casteljau 算法来绘制由4 个控制点表示的Bézier 曲线(当你正确实现该算法时,你可以支持绘制由更 ...
- 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色
总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...
- 【UE4】GAMES101 图形学作业0:矩阵初识
作业描述 给定一个点P=(2,1), 将该点绕原点先逆时针旋转45◦,再平移(1,2), 计算出变换后点的坐标(要求用齐次坐标进行计算). UE4 知识点 主要矩阵 FMatrix FBasisVec ...
- 【Notes_4】现代图形学入门——光栅化、离散化三角形、深度测试与抗锯齿
光栅化 Viewport Transform(视口变换) 将经过MVP变换后得到的单位空间模型变换到屏幕上,屏幕左边是左下角为原点. 所以视口变换的矩阵 \[M_{viewport}=\begin{p ...
- [计算机图形学]光栅化算法:DDA和Bresenham算法
目录 一.DDA 二.Bresenham 三.绘制图形 1. 绘制直线 2. 绘制圆 3. 绘制椭圆 一.DDA DDA算法是最简单的直线绘制算法.主要思想是利用直线的斜截式:\(y=kx+b\) 对 ...
- 回顾Games101图形学(一)几何变换中一些公式的推导
回顾Games101 chatper1 - 6 前言 本文只写回顾后重新加深认识的知识 透视除法的意义 经过MVP矩阵之后,将模型空间下某点的坐标,转换成了裁剪空间下的坐标,此时因为裁剪空间的范围是x ...
- 基于显卡的光栅化渲染器Gaius计划
决定实现一个基于显卡的光栅化渲染器,能将一些基于显卡的新算法融入其中.
随机推荐
- fetch ios低版本兼容cannot clone a disturbed response
报错信息 ios 11以下 cannot clone a disturbed response github.com/github/fetc- 问题发生场景 使用了一个或者多个三方库 三方库或者自己的 ...
- MongoDB(4)- Collection 集合相关
Collection MongoDB 将文档存储在集合中 集合存储在 Database 中 集合类似于关系数据库(Mysql)中的表 如果集合不存在,则 MongoDB 会在第一次存储该集合数据时创建 ...
- c# 递归树形菜单
首先创建模型类Menus public class Menus { //菜单Id public int Id { get; set; } //菜单名 public string MenuName { ...
- Spring基于XML方式加载Bean定义信息(又名:Spring IOC源码时序图)-图解
- jquery实现强制刷新
$('iframe.active').attr('src', $('iframe.active').attr('src'));
- 【第八篇】- Git 查看提交历史之Spring Cloud直播商城 b2b2c电子商务技术总结
Git 查看提交历史 Git 提交历史一般常用两个命令: git log 在使用 Git 提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,我们可以使用 git log 命令查看. 针对 ...
- tar解压缩问题
gzip: stdin: unexpected end of filetar: 归档文件中异常的 EOFtar: 归档文件中异常的 EOFtar: Error is not recoverable: ...
- js简单化技巧
1.交换两个变量而没有第三个 let x = 1;let y = 2;[x, y] = [y, x];console.log(x, y); 输出: 2 1 2.将数字转换为字符串 const num ...
- Java面向对象系列(3)- 回顾方法的调用
方法的调用 静态方法 非静态方法 形参和实参 值传递和引用传递 this关键字(继承篇讲解) 静态方法 非静态方法 形参和实参 package oop.demo01; public class Dem ...
- 再谈OAuth授权
工作场景流程 大家都知道OAuth是用于第三方授权的,当你用其他的APP想访问微信账号的昵称.粉丝.联系人等信息,这里就需要微信进行授权,此时在APP的网页端是可以发现有微信登录的,点开会出现弹框,在 ...