2021SC@SDUSC
一、简述
这篇博客将继续上一篇未完成的内容,也就是分析Render2D的具体内容,包括一些函数的具体实现,一部分结构体等。
Render2D.cpp作为2D渲染部分最主要的部分,代码行数在2000行左右,渲染作为图像处理的最后阶段(除后期处理外),也是一个非常复杂的模块,对于一个游戏引擎来说更是非常重要,因此我们想要短时间内完全分析也是不可能的,所以我们会略去一部分。
二、分析
首先是一些预定义宏,C/C++编译系统编译程序的过程为预处理、编译、链接。预处理器是在程序源文件被编译之前根据预处理指令对程序源文件进行处理的程序。比如:
// The format for the blur effect temporary buffer
#define PS_Blur_Format PixelFormat::R8G8B8A8_UNorm
模糊效果临时缓冲区的格式
然后是一些结构体的具体实现,这里我们以其中一个或几个为例子进行分析:
struct Render2DDrawCall
{
DrawCallType Type;
uint32 StartIB;
uint32 CountIB;
union
{
struct
{
GPUTextureView* Ptr;
} AsRT;
struct
{
GPUTexture* Ptr;
} AsTexture;
struct
{
GPUTexture* Tex;
MaterialBase* Mat;
} AsChar;
struct
{
GPUTexture* Tex;
GPUPipelineState* Pso;
} AsCustom;
struct
{
MaterialBase* Mat;
} AsMaterial;
struct
{
float Strength;
float Width;
float Height;
float UpperLeftX;
float UpperLeftY;
float BottomRightX;
float BottomRightY;
} AsBlur;
struct
{
float X;
float Y;
float Width;
float Height;
} AsClipScissors;
};
};
这里的DrawCallType在代码中为一个byte类型的枚举类 ,值得注意的是这里的byte类型是通过预定义基本类型char,用以存储无符号的8个bit:
typedef unsigned char byte; // Unsigned 8 bits
下面的unit32类似,也就是无符号的int类型。
我们注意到接下来的多个结构体作为Render2DDrawCall这个结构体的元素,通过关键字union进行定义。
union即为联合,它是一种特殊的类:在任意时刻,联合中只能有一个数据成员可以有值。当给联合中某个成员赋值之后,该联合中的其它成员就变成未定义状态了,因此,联合的存储空间至少能够容纳其最大的数据成员。
接下来看命名空间
我注意到在声明时,一般使用的是nullptr而不是NULL,比如:
// Private Stuff
GPUContext* Context = nullptr;
GPUTextureView* Output = nullptr;
在C语言中,NULL通常被定义为:#define NULL ((void *)0),所以说NULL实际上是一个空指针;而在C++中,因为C++是强类型语言,void*是不能隐式转换成其他类型的指针的,所以为了结果空指针的表示问题,C++引入了0来表示空指针,因此在使用NULL时在函数重载时会出现二义性,在我们希望使用NULL表示空指针输入函数中时,可能将其认为int类型,所以,C++11加入了nullptr,可以保证在任何情况下都代表空指针。
然后是Transform
注意:使用 Matrix3x3 而不是 Matrix 因为在 CPU 端只使用 2D 转换
矩阵布局:
[ m1, m2, 0 ]
[ m3, m4, 0 ]
[ t1, t2, 1 ]
其中“m”是二维变换(缩放、剪切和旋转),“t”是平移
Array<Matrix3x3, InlinedAllocation<64>> TransformLayersStack;
Matrix3x3 TransformCached;
1.第一个转换函数:
FORCE_INLINE void ApplyTransform(const Vector2& value, Vector2& result)
{
Matrix3x3::Transform2DPoint(value, TransformCached, result);
}
两个参数都是自定义的二维向量 ,调用Matrix3x3类的Transform2DPoint()方法,参数TransformCached是Matrix3x3类型对象,为转换缓存,下面是Transform2DPoint()方法的实现:
static void Transform2DPoint(const Vector2& point, const Matrix3x3& transform, Vector2& result)
{
result = Vector2(
point.X * transform.M11 + point.Y * transform.M21 + transform.M31,
point.X * transform.M12 + point.Y * transform.M22 + transform.M32);
}
Transform2DPoint()方法的作用是通过矩阵变换给定的点(在 2D 中);用于转换点的位置。参数point即为给的点,transform为变换矩阵,result为结果,根据我们上面提到的矩阵布局:
[ m1, m2, 0 ]
[ m3, m4, 0 ]
[ t1, t2, 1 ]
其中“m”是二维变换(缩放、剪切和旋转),“t”是平移
通过矩阵的M11,M12,M21,M22进行二维变换,通过M31,M32实现平移。
2.第二个转换函数:
void ApplyTransform(const Rectangle& value, RotatedRectangle& result)
{
const RotatedRectangle rotated(value);
Matrix3x3::Transform2DPoint(rotated.TopLeft, TransformCached, result.TopLeft);
Matrix3x3::Transform2DVector(rotated.ExtentX, TransformCached, result.ExtentX);
Matrix3x3::Transform2DVector(rotated.ExtentY, TransformCached, result.ExtentY);
}
结构体Rectangle主要构成,location为矩形位置(左上角坐标),size为矩形大小(宽,高):
API_FIELD() Vector2 Location;
API_FIELD() Vector2 Size;
结构体RotatedRectangle(表示已被任意渲染变换所转换的矩形)主要构成: TopLeft为变换后的左上角,ExtentX转换后的X范围(从右到左),ExtentY转换后的Y范围(自下而上)
Vector2 TopLeft;
Vector2 ExtentX;
Vector2 ExtentY;
关于函数Transform2DPoint()我们上面已经分析过,这里不再赘述,我们看一下Transform2DVector():
static void Transform2DVector(const Vector2& vector, const Matrix3x3& transform, Vector2& result)
{
result = Vector2(
vector.X * transform.M11 + vector.Y * transform.M21,
vector.X * transform.M12 + vector.Y * transform.M22);
}
通过矩阵变换给定的向量(在 2D 中),用于转换大小或距离。
这次的分析暂时到此为止,接下来几篇博客也将继续关注与2D渲染部分。