游戏引擎Flax Engine分析(八)渲染

 2021SC@SDUSC


一、简述

        我们继续之前的博客分析2D渲染服务后续的内容。边学习边分析渲染流程。

二、分析

         接下来一些服务主要提供对于变换矩阵、颜色等熟悉的操作,诸如入栈、弹出等,这里不再赘述。

        我们先看一下在之前分析的结束渲染时进行的批处理元素的刷新:

DrawBatch(batchStart, batchSize);

        对于DrawBatch()这个函数: 

    const Render2DDrawCall& d = DrawCalls[startIndex];
    GPUBuffer* vb = VB.GetBuffer();
    GPUBuffer* ib = IB.GetBuffer();
    uint32 countIb = 0;
    for (int32 i = 0; i < count; i++)
        countIb += DrawCalls[startIndex + i].CountIB;

        startIndex 是函数的第一个参数batchStart,首先根据该参数获取绘图调用对象;

        VB是允许在单帧期间渲染任何顶点的动态顶点缓冲区(支持动态调整大小);IB是允许在单帧期间呈现任何索引的动态索引缓冲区(支持动态调整大小);它们都继承自DynamicBuffer,是允许在单帧期间更新和使用 GPU 数据(索引/顶点/其他)的动态 GPU 缓冲区(支持动态调整大小)。

        循环中的count为函数的第二个参数,这里使用countIb记录总的IB大小。

    if (d.Type == DrawCallType::ClipScissors)
    {
        Rectangle* scissorsRect = (Rectangle*)&d.AsClipScissors.X;
        Context->SetScissor(*scissorsRect);
        IsScissorsRectEmpty = scissorsRect->Size.IsAnyZero();
        return;
    }
    if (IsScissorsRectEmpty)
        return;

        判断上面获取的绘图调用的类型,枚举的绘图调用类型如下:

enum class DrawCallType : byte
{
    FillRect,
    FillRectNoAlpha,
    FillRT,
    FillTexture,
    FillTexturePoint,
    DrawChar,
    DrawCharMaterial,
    Custom,
    Material,
    Blur,
    ClipScissors,
    LineAA,

    MAX
};

        这里首先判断绘图调用类型是否为剪刀,接下来获取绘图调用中的子结构体AsClipScissors的X属性,并设置剪刀矩形。获取值IsScissorsRectEmpty,该值指示任何向量分量是否为零。

        判断剪刀为特殊情况,下面根据绘图调用类型的不同,对Context进行不同的设置:

switch (d.Type)
    {
    case DrawCallType::FillRect:
        Context->SetState(CurrentPso->PS_Color);
        break;
    case DrawCallType::FillRectNoAlpha:
        Context->SetState(CurrentPso->PS_Color_NoAlpha);
        break;
    case DrawCallType::FillRT:
        Context->BindSR(0, d.AsRT.Ptr);
        Context->SetState(CurrentPso->PS_Image);
        break;
    case DrawCallType::FillTexture:
        Context->BindSR(0, d.AsTexture.Ptr);
        Context->SetState(CurrentPso->PS_Image);
        break;
    case DrawCallType::FillTexturePoint:
        Context->BindSR(0, d.AsTexture.Ptr);
        Context->SetState(CurrentPso->PS_ImagePoint);
        break;
    case DrawCallType::DrawChar:
        Context->BindSR(0, d.AsChar.Tex);
        Context->SetState(CurrentPso->PS_Font);
        break;

        BindSR所做的工作是将纹理绑定到着色器资源槽。

        这里我们重点看一下绘图调用类型 绘制字符材质 时所做的工作:

case DrawCallType::DrawCharMaterial:
    {
        // 应用和绑定材料
        auto material = d.AsChar.Mat;
        MaterialBase::BindParameters bindParams(Context, *(RenderContext*)nullptr);
        bindParams.CustomData = &ViewProjection;
        material->Bind(bindParams);

        // 绑定字体图集作为材质参数
        static StringView FontParamName = TEXT("Font");
        auto param = material->Params.Get(FontParamName);
        if (param && param->GetParameterType() == MaterialParameterType::Texture)
        {
            Context->BindSR(param->GetRegister(), d.AsChar.Tex);
        }

        // 绑定索引和顶点缓冲区
        Context->BindIB(ib);
        Context->BindVB(ToSpan(&vb, 1));

        // 绘制
        Context->DrawIndexed(countIb, 0, d.StartIB);

        // 恢复管道(材料应用覆盖它)
        const auto cb = GUIShader->GetShader()->GetCB(0);
        Context->BindCB(0, cb);

        return;
    }

        当绘图调用的类型为Material 材质时,流程与 绘制字符材质 大致相同:

case DrawCallType::Material:

        绘图调用类型为 模糊 时:

const Vector4 bounds(d.AsBlur.UpperLeftX, d.AsBlur.UpperLeftY, d.AsBlur.BottomRightX, d.AsBlur.BottomRightY);
        float blurStrength = Math::Max(d.AsBlur.Strength, 1.0f);
        auto& limits = GPUDevice::Instance->Limits;
        int32 renderTargetWidth = Math::Min(Math::RoundToInt(d.AsBlur.Width), limits.MaximumTexture2DSize);
        int32 renderTargetHeight = Math::Min(Math::RoundToInt(d.AsBlur.Height), limits.MaximumTexture2DSize);

        根据调用的参数定义边界 bounds,blurStrength模糊强度,由GPU实例对象获取GPU限制,

渲染目标宽度和长度取 绘图调用传来的参数 和 GPU限制的2D 纹理的最大尺寸 两者中的最小值

        int32 kernelSize = 0, downSample = 0;
        CalculateKernelSize(blurStrength, kernelSize, downSample);
        if (downSample > 0)
        {
            renderTargetWidth = Math::DivideAndRoundUp(renderTargetWidth, downSample);
            renderTargetHeight = Math::DivideAndRoundUp(renderTargetHeight, downSample);
            blurStrength /= downSample;
        }

        初始化内核大小和下采样为0,再以上面获取的模糊强度为参数计算内核大小,这里具体计算方法我们不在分析。然后判断下采样值,若大于零,则根据下采样的值改变渲染目标的宽度和高度以及模糊强度,这里DivideAndRoundUp()将两个整数相除并向上取整。

        对于模糊,下面还有一系列操作,这里就不再放上代码,而是简单介绍一下进行了哪些操作:

  • 如果没有机会渲染任何内容,则返回;
  • 获取临时纹理
  • 准备模糊数据
  • 缩小(或不缩小)并提取背景图像以进行模糊处理
  • 渲染模糊(第一遍)
  • 渲染模糊(第二遍)
  • 恢复输出
  • 将最终模糊绘制链接为纹理
  • 释放 

         这次的分析就到这里为止。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值