一、概述
? ? ? ? 这篇博客我们继续之前的内容,分析2D渲染。
二、分析
? ? ? ? 接下来的大多数渲染方法,诸如?绘制纹理,绘制Sprite,绘制纹理(使用点采样器),绘制Sprite(使用点采样器)(关于Sprite在游戏引擎中的含义这里不再赘述,若是感兴趣的话可以自己去搜索一下),本质上都调用了一个WriteRect()方法。
static void DrawTexture(GPUTextureView* rt, const Rectangle& rect, const Color& color = Color::White);
static void DrawSprite(const SpriteHandle& spriteHandle, const Rectangle& rect, const Color& color);
static void DrawTexturePoint(GPUTexture* t, const Rectangle& rect, const Color& color = Color::White);
? ? ? ? 首先,我们?看一下DrawTexture()方法是如何一步一步调用WriteRect()的:
void Render2D::DrawTexture(GPUTextureView* rt, const Rectangle& rect, const Color& color)
{
RENDER2D_CHECK_RENDERING_STATE;
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = DrawCallType::FillRT;
drawCall.StartIB = IBIndex;
drawCall.CountIB = 6;
drawCall.AsRT.Ptr = rt;
WriteRect(rect, color);
}
? ? ? ? 这里可以很简单的看到在判断当前渲染状态之后,创建了一个2D绘图调用的对象,对对象内部的参数进行了配置之后就调用了WriteRect()。
????????我们再看一下DrawSprite()方法:
void Render2D::DrawSprite(const SpriteHandle& spriteHandle, const Rectangle& rect, const Color& color)
{
RENDER2D_CHECK_RENDERING_STATE;
if (spriteHandle.Index == INVALID_INDEX || !spriteHandle.Atlas || !spriteHandle.Atlas->GetTexture()->HasResidentMip())
return;
Sprite* sprite = &spriteHandle.Atlas->Sprites.At(spriteHandle.Index);
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = DrawCallType::FillTexture;
drawCall.StartIB = IBIndex;
drawCall.CountIB = 6;
drawCall.AsTexture.Ptr = spriteHandle.Atlas->GetTexture();
WriteRect(rect, color, sprite->Area.GetUpperLeft(), sprite->Area.GetBottomRight());
}
? ? ? ? 与DrawTexture()方法大致相同,因此接下来我们直接看最关键的WriteRect()方法:
? ? ? ? 首先WriteRect()共三个重载:
void WriteRect(const Rectangle& rect, const Color& color1, const Color& color2, const Color& color3, const Color& color4);
void WriteRect(const Rectangle& rect, const Color& color, const Vector2& uvUpperLeft, const Vector2& uvBottomRight);
FORCE_INLINE void WriteRect(const Rectangle& rect, const Color& color);
? ? ? ? 前两个重载所做的事情差不多,而第三个重载函数调用第二的重载函数,因此这里我们只看第二个重载函数:
void WriteRect(const Rectangle& rect, const Color& color, const Vector2& uvUpperLeft, const Vector2& uvBottomRight)
{
Render2DVertex quad[4];
quad[0] = MakeVertex(rect.GetBottomRight(), uvBottomRight, color);
quad[1] = MakeVertex(rect.GetBottomLeft(), Vector2(uvUpperLeft.X, uvBottomRight.Y), color);
quad[2] = MakeVertex(rect.GetUpperLeft(), uvUpperLeft, color);
quad[3] = MakeVertex(rect.GetUpperRight(), Vector2(uvBottomRight.X, uvUpperLeft.Y), color);
VB.Write(quad, sizeof(quad));
uint32 indices[6];
RENDER2D_WRITE_IB_QUAD(indices);
VBIndex += 4;
IBIndex += 6;
}
? ? ? ? 对顶点进行运算后存入缓冲区中。?
? ? ? ? ?然后是其他几个诸如使用 9 切片绘制纹理,使用 9 切片(使用点采样器)绘制纹理,使用 9 切片绘制Sprite,使用 9 切片(使用点采样器)绘制Sprite。
static void Draw9SlicingTexture(TextureBase* t, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White);
static void Draw9SlicingTexturePoint(TextureBase* t, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White);
static void Draw9SlicingSprite(const SpriteHandle& spriteHandle, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White);
static void Draw9SlicingSpritePoint(const SpriteHandle& spriteHandle, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White);
? ? ? ? ?同样的我们据其中一个为例子看一下它的执行过程,这里我们选择Draw9SlicingTexturePoint():
void Render2D::Draw9SlicingTexturePoint(TextureBase* t, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color)
{
RENDER2D_CHECK_RENDERING_STATE;
Render2DDrawCall drawCall;
drawCall.Type = DrawCallType::FillTexturePoint;
drawCall.StartIB = IBIndex;
drawCall.CountIB = 6 * 9;
drawCall.AsTexture.Ptr = t ? t->GetTexture() : nullptr;
DrawCalls.Add(drawCall);
Write9SlicingRect(rect, color, border, borderUVs);
}
? ? ? ? 可以看到实际的执行过程与之前我们分析的DrawTexture()并无太大区别,那让我们看一下Write9SlicingRect():
void Write9SlicingRect(const Rectangle& rect, const Color& color, const Vector4& border, const Vector4& borderUVs)
{
const Rectangle upperLeft(rect.Location.X, rect.Location.Y, border.X, border.Z);
const Rectangle upperRight(rect.Location.X + rect.Size.X - border.Y, rect.Location.Y, border.Y, border.Z);
const Rectangle bottomLeft(rect.Location.X, rect.Location.Y + rect.Size.Y - border.W, border.X, border.W);
const Rectangle bottomRight(rect.Location.X + rect.Size.X - border.Y, rect.Location.Y + rect.Size.Y - border.W, border.Y, border.W);
const Vector2 upperLeftUV(borderUVs.X, borderUVs.Z);
const Vector2 upperRightUV(1.0f - borderUVs.Y, borderUVs.Z);
const Vector2 bottomLeftUV(borderUVs.X, 1.0f - borderUVs.W);
const Vector2 bottomRightUV(1.0f - borderUVs.Y, 1.0f - borderUVs.W);
WriteRect(upperLeft, color, Vector2::Zero, upperLeftUV);
WriteRect(upperRight, color, Vector2(upperRightUV.X, 0), Vector2(1, upperLeftUV.Y));
WriteRect(bottomLeft, color, Vector2(0, bottomLeftUV.Y), Vector2(bottomLeftUV.X, 1));
WriteRect(bottomRight, color, bottomRightUV, Vector2::One);
WriteRect(Rectangle(upperLeft.GetUpperRight(), upperRight.GetBottomLeft() - upperLeft.GetUpperRight()), color, Vector2(upperLeftUV.X, 0), upperRightUV);
WriteRect(Rectangle(upperLeft.GetBottomLeft(), bottomLeft.GetUpperRight() - upperLeft.GetBottomLeft()), color, Vector2(0, upperLeftUV.Y), bottomLeftUV);
WriteRect(Rectangle(bottomLeft.GetUpperRight(), bottomRight.GetBottomLeft() - bottomLeft.GetUpperRight()), color, bottomLeftUV, Vector2(bottomRightUV.X, 1));
WriteRect(Rectangle(upperRight.GetBottomLeft(), bottomRight.GetUpperRight() - upperRight.GetBottomLeft()), color, upperRightUV, Vector2(1, bottomRightUV.Y));
WriteRect(Rectangle(upperLeft.GetBottomRight(), bottomRight.GetUpperLeft() - upperLeft.GetBottomRight()), color, upperRightUV, bottomRightUV);
}
九切片:
????????背景:很多用于背景显示的图片,本身内容很简单,有规律的重复,那么,没有必要做一张很大的图片,可以将图片切割成不同的区域,有的区域保持不变,有的区域进行复制或拉伸,从而实现通过编程的手段,来实现图片的有规律的方法或缩小
????????解释:将图片横向切两刀,纵向切两刀,就分成了9个区域,其中最外边的四个角区域,属于不会被改变,保持原样的区域,其余的5个区域,就可以进行复制或拉伸,从而实现图片变大或缩小
? ? ? ? 很明显能看出使用九切片绘制时一共调用了九次上面我们分析过的WriteRect()方法?
? ? ? ? 还有一个执行自定义渲染(DrawCustom)的方法,同样是最后调用了WriteRect()方法,仅仅是对2D绘制调用的参数设置不同,关于调用的具体执行我们在后面会进行分析,这篇博客就到这里。
? ? ? ? 感谢。
|