在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(const Triangle& t)。
但这一次,你需要自己填写并调用函数 rasterize_triangle(const Triangle& t)
。该函数的内部工作流程如下:
注意
[20 分] 正确实现三角形栅格化算法。
[10 分] 正确测试点是否在三角形内。
[10 分] 正确实现z-buffer 算法, 将三角形按顺序画在屏幕上。
[提高项5 分] 用super-sampling 处理Anti-aliasing
你可能会注意到,当我们放大图像时,图像边缘会有锯齿感。我们可以用super-sampling来解决这个问题,即对每个像素进行2 * 2 采样,并比较前后的结果(这里并不需要考虑像素与像素间的样本复用)。需要注意的点有,对于像素内的每一个样本都需要维护它自己的深度值,即每一个像素都需要维护一个samplelist。最后,如果你实现正确的话,你得到的三角形不应该有不正常的黑边。
静态展示
由于绘制看不出锯齿优化效果,因此在像素点取点间隔略作处理,每五个点取一个像素点,如果在三角形内,则自动将未扫描的五个点补全。
rasterize_triangle()
: 执行三角形栅格化算法static bool insideTriangle()
: 测试点是否在三角形内。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&lt;FVector&gt;&amp; positions);
ind_buf_id load_indices(const TArray&lt;FIntVector&gt;&amp; indices);
col_buf_id load_colors(const TArray&lt;FVector&gt;&amp; colors);
void set_model(const FMatrix&amp; m);
void set_view(const FMatrix&amp; v);
void set_projection(const FMatrix&amp; p);
void set_pixel(const FVector&amp; point, const FVector&amp; 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&lt;FColor&gt;&amp; 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&lt;int,TArray&lt;FVector&gt;&gt; pos_buf;
TMap&lt;int,TArray&lt;FIntVector&gt;&gt; ind_buf;
TMap&lt;int,TArray&lt;FVector&gt;&gt; col_buf;
TArray&lt;FColor&gt; frame_buf;
TArray&lt;float&gt; 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&amp; vec : v) {
vec *= 1 / vec.W;
}
//Viewport transformation
for (auto&amp; 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 &lt; 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 &lt;= x_r; x++)
for (int y = y_b; y &lt;= y_t; y++) {
float minDepth = TNumericLimits&lt;float&gt;::Max();
float percentage = 0;
for (int i = 0; i &lt; 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&lt;0&gt;(BarycentricParam);
float beta = std::get&lt;1&gt;(BarycentricParam);
float gamma = std::get&lt;2&gt;(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 &gt; 0 &amp;&amp; depth_buf[get_index(x, y)] &gt; 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&lt;0&gt;(BarycentricParam);
float beta = std::get&lt;1&gt;(BarycentricParam);
float gamma = std::get&lt;2&gt;(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)] &gt; 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 vertexvoid 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));
}
}
*/
手机扫一扫
移动阅读更方便
你可能感兴趣的文章