游戏引擎Flax Engine源码分析(十)渲染

2021SC@SDUSC


一、概述

        这篇博客我们继续之前的内容,分析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绘制调用的参数设置不同,关于调用的具体执行我们在后面会进行分析,这篇博客就到这里。

        感谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值