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

总览

  • 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(const Triangle& t)。
  • 但这一次,你需要自己填写并调用函数 rasterize_triangle(const Triangle& t)。该函数的内部工作流程如下:
    1. 创建三角形的2 维bounding box。
    2. 遍历此bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
    3. 如果在内部,则将其位置处的插值深度值(interpolated depth value) 与深度缓冲区(depth buffer) 中的相应值进行比较。
    4. 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区(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实现

效果

  • 静态展示

    image

  • 由于绘制看不出锯齿优化效果,因此在像素点取点间隔略作处理,每五个点取一个像素点,如果在三角形内,则自动将未扫描的五个点补全。
    image

代码结构说明

  • 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));
      	}	
      }
      */
      
posted @ 2021-10-22 00:25  砥才人  阅读(773)  评论(0编辑  收藏  举报