Skip to content

渲染管线

渲染管线特性支持

以下内容将包含前向渲染管线中的功能支持信息。 为了方便参考,功能分为以下类别:

  • 平台支持
  • 灯光
  • 全局光照
  • 相机

平台支持

平台前向渲染管线
Windows支持
Linux支持
Android平台支持
XR平台支持
OpenHarmony支持

灯光

光类型前向渲染管线
平行光支持
点光源支持
聚光灯支持
区域光支持

实时全局光照

功能前向渲染管线
VXGI支持
烘焙全局光照不支持
屏幕空间反射不支持
光照探针支持

相机

功能前向渲染管线
多显示器支持
硬件动态分辨率不支持
软件动态分辨率支持
深度学习超级采样DLSS不支持
AMD 超级分辨率2.0不支持
TAA支持
MASS支持
FXAA支持
运动矢量支持

前向渲染管线

前向渲染是一种简单直观的渲染管线,适用于光源数量较少的场景,它的基本思路是:对于每个像素,进行一次完整的光照计算,并结合所有的光源信息来生成最终的像素颜色。前向渲染的优势在于实现简单,且能够很好地处理透明度、抗锯齿等效果。尽管如此,在光源数量增多时,性能开销较大,尤其在大规模场景中可能会遇到瓶颈。

前向渲染管线渲染流程:

  • 收集场景中的可渲染物队列
  • 对可渲染队列进行分类和排序
  • 遍历相机列表
  • 通过脏标记机制,对可渲染物队列进行视锥剔除,Hiz剔除以及层过滤剔除
  • 提前深度渲染
  • 渲染不透明物体队列
  • 渲染天空盒(如果相机配置需要渲染天空盒)
  • 渲染半透明物体队列
  • 渲染UI队列
  • 后处理
  • 上屏显示

渲染流程图如下:alt text

性能注意事项: 对于所有像素,动态光照会为每个受影响的像素增加渲染工作,可能导致对象在多个 pass 中被渲染。避免在性能较弱的设备(如移动端或低端 PC GPU)上使用多个光照来照射单个对象,应使用光照贴图实现静态对象的光照,而不是每帧计算其光照。

在渲染过程中,像素着色器会遍历所有光源,进行像素着色计算,最后进行混合叠加,因此尽量减少场景中不必要的光源,同时要严格控制点光源,聚光灯的影响范围避免不必要的计算。

延迟渲染管线

延迟渲染是一种现代的高效渲染管线技术,适用于光源较多、场景复杂的应用。其通过将几何渲染与光照计算分开,提高了性能,但也带来了一些挑战,如 G-buffer 的内存开销、透明物体的渲染难度等。通过优化和后处理技术,延迟渲染在现代游戏和图形应用中发挥着重要作用。

延迟渲染管线的优点: 延迟渲染特别适合光源数量多的场景。由于光照计算与几何处理分离,渲染时光源的数量对性能的影响大大降低。在每次光照计算时,只需要将所有光源的影响加到片元上,而不是为每个像素分别处理每个光源。

延迟渲染管线的缺点:

  1. 延迟渲染需要存储多个 G-buffer(例如颜色、法线、深度、材质等),这会增加内存的开销,尤其在高分辨率下,G-buffer 的尺寸会显著增大。
  2. 延迟渲染不适合处理透明物体,因为透明物体的光照计算和合成通常需要在几何阶段进行,而延迟渲染将几何信息分开处理,导致透明物体的渲染比较困难。一般需要使用前向渲染或混合技术来处理透明物体。
  3. 由于光照计算发生在光照阶段,阴影的计算需要额外的处理。例如,计算阴影图时,光源的深度信息必须单独处理,这使得阴影处理较为复杂。

要求: 延迟着色要求显卡具有多渲染目标 (MRT)、着色器模型 3.0(或更高版本)并支持深度渲染纹理。从 GeForce 8xxx、Radeon X2400、Intel G45 开始,2006 年以后制造的大多数 PC 显卡都支持延迟着色。所有至少运行 OpenGL ES 3.0 的移动设备都支持延迟着色。

性能注意事项: 当然,有阴影的光源比没有阴影的光源的成本高得多。在延迟着色中,对于每个阴影投射光源,仍然需要将投射阴影的游戏对象渲染一次或多次。此外,应用阴影的光照着色器的渲染开销高于禁用阴影时的渲染开销。

管线渲染流程:

  1. 几何阶段:
  • 渲染场景中的所有几何体,并将物体的几何信息(如颜色、法线、深度等)存储到多个纹理中,这些纹理通常被称为 G-buffer(几何缓冲区)。
  • 在这一阶段,所有的光源计算都被延迟,光照不会直接应用到片元上,而是将每个物体的几何信息保存下来。
  1. 光照阶段:
  • 通过 G-buffer 中的数据,对场景中所有的光源进行光照计算,并将光照结果合成到最终的图像中。
  • 每个光源都会影响所有的像素,但由于几何信息已经存储在 G-buffer 中,光照计算可以统一进行,而不需要在每个像素中进行多次计算。
  1. 合成阶段:
  • 在这一阶段,所有的光照结果、材质属性、阴影等都被合成到最终图像中,生成屏幕上的最终渲染结果。

渲染管线流程图如下: alt text

自定义渲染管线

可编程渲染管线(Scriptable Render Pipeline)是一种允许开发者自定义和控制渲染过程的架构。SRP提供了一种新的方式来替代内置的渲染管线(如传统的Forward Rendering、Deferred Rendering等),使得开发者可以根据项目需求灵活地控制渲染步骤,优化性能,并实现定制化的渲染效果。 注:可编程渲染管线只有运行时生效,编辑器模式不生效。

可编程渲染管线的优点:

  • 高灵活性和可定制性,SRP使开发者能够完全控制渲染过程,包括每个渲染阶段的细节,能够针对特定的需求进行高度自定义。
  • 性能优化,通过SRP,开发者可以去除不必要的渲染操作,减少GPU负担,优化渲染过程。SRP的灵活性使得在不同平台和硬件上可以实现最佳的性能表现。

可编程渲染管线的缺点:

  • 复杂性,开发自定义渲染管线,开发者需要掌握更复杂的渲染管线知识,会增加开发的难度,对于没有渲染管线经验的开发者,理解和编写自定义渲染管线需要更多的时间学习。

自定义渲染管线核心类:

  • NPipeLineRenderer:自定义渲染管线的一个基类,开发者通过继承此类,然后重写render方法,便创建了一个自定义渲染管线实例,Render方法是自定义渲染管线的入口方法。
  • NRenderPassBase:渲染pass的基类,开发者通过继承此类,重写里面的render方法,便可以得到一个渲染pass,在这个渲染pass里面可以定制渲染内容,里面封装了很多常用的渲染接口,例如清除背景颜色和深度的接口,设置gl上下文状态的接口,以及剔除接口等。

NRenderPassBase常用接口定义:

接口名称返回值接口含义
InitResourcesvoid初始化资源
Renderbool渲染函数
RenderActorvoid渲染控件
DrawRenderablesvoid渲染可渲染队列
GetDefualtRenderTargetNRenderTexturePtr获取场景RT
SetRenderTargetvoid设置渲染RT
BindDefualtRenderTargetvoid绑定场景RT
ClearBackgroundvoid清除背景
Clearvoid清除颜色,深度,模版缓冲区
SetPassNamevoid设置pass名称
GetPassNameconst std::string&获取pass名称
GetDepthStateDepthState*获取上下文深度状态
GetStencilStateStencilState*获取上下文模版状态
GetBlendingStateBlendingState*获取上下文颜色混合状态
GetRasterStateRasterState*获取上下文光栅裁剪状态
SetupScissorRectAndViewportvoid设置视口裁剪矩形
MarkRenderObjectContextDirtyvoid标记渲染物脏了的接口
SetNeedFrustumCullvoid设置是否需要视锥剔除
  • 初始化资源  virtual void InitResources()override; 描述:此接口是渲染pass进行初始化一些资源的接口,例如初始化纹理,初始化材质实例等。 代码示例:
cpp
void VolumePass::InitResources()
{
    Super::InitResources();
    if (!m_BackMaterial)
    {
        m_BackMaterial = NResources::CreateMaterial("BackRenderMaterial", "BackRenderMaterial");
    }
}
  • 每帧渲染回调接口 virtual bool Render()override; 描述:此接口每帧都会调用,用来进行可渲染物的渲染操作。 代码示例:
cpp
bool VolumePass::Render()
{
    DrawRenderables(ERenderQueueRange::RQI_Skybox);
    if (!m_BackMaterial || !m_FrontMaterial || !m_VolumeDataTex)return false;
    auto viewport = NCamera::GetCurrent()->GetViewport();
    if (!m_CubeAct)
    {
        m_CubeAct = NActorManager::GetActor("Cube_1");
    }
    if (!m_BackFaceRT)
    {
        m_BackFaceRT = NResources::GetTempRenderTexture(viewport.width, viewport.height, EPixelFormat::PF_R16G16B16A16, TextureType::TT_2D, ERenderTextureContentType::RT_Default, m_RenderTextureConfig, ERenderTextureRenderMode::Direct);
    }
    SetRenderTarget(m_BackFaceRT);
    Clear(EClearFlags::CF_Color | EClearFlags::CF_Depth, 0, 1.0f, 0);
    MarkRenderObjectContextDirty();
    RenderActor(m_CubeAct, EPassLightMode::EPLM_ForwardBase, m_BackMaterial);
    BindDefualtRenderTarget();
    m_FrontMaterial->SetShaderProperty("ExitPoints", m_BackFaceRT->GetColorSurface());
    MarkRenderObjectContextDirty();
    RenderActor(m_CubeAct, EPassLightMode::EPLM_ForwardBase, m_FrontMaterial);
    return true;
}
  • 指定控件渲染 void RenderActor(NActorPtr actor, EPassLightMode lightMode= EPassLightMode::EPLM_ForwardBase, NMaterialPtr material=nullptr);
    void DrawRenderables(const std::vector< NRenderableInfo>& renderables);

参数:

参数说明
actor需要渲染的控件指针
lightMode指定渲染的pass类型
material指定渲染控件用到的材质(如果不指定就用actor自身的材质)

描述:此接口可以用来渲染指定Actor,并且通过参数可以指定渲染actor所用的材质。 代码示例:

cpp
bool VolumePass::Render()
{
    RenderActor(m_CubeAct, EPassLightMode::EPLM_ForwardBase, m_BackMaterial);
}
  • 渲染指定队列 void DrawRenderables(const ERenderQueueRange& renderqueue);

参数:

参数说明
renderqueue是一个枚举类型,目前枚举定义了RQI_Opaque不透明物体,RQI_Transparent半透明物体,RQI_UI 2d UI ,RQI_Skybox天空盒。

描述:此方法是用来渲染指定内置队列的 代码示例:

cpp
bool VolumePass::Render()
{
    DrawRenderables(ERenderQueueRange::RQI_Skybox);//渲染天空盒
    return true;
}
  • 设置和获取RT NRenderTexturePtr GetDefualtRenderTarget();
    void SetRenderTarget(NRenderTexturePtr rt);
    void BindDefualtRenderTarget();

参数:

参数说明
rt要设置的RT共享指针

描述: GetDefualtRenderTarget接口可以获取在调用Render时,场景渲染的RT, BindDefualtRenderTarget接口用来重行邦回场景渲染的RT, SetRenderTarget接口用来设置新的渲染RT

代码示例:

cpp
bool VolumePass::Render()
{
	auto viewport = NCamera::GetCurrent()->GetViewport();
    if (!m_BackFaceRT)
    {
        m_BackFaceRT = NResources::GetTempRenderTexture(viewport.width, viewport.height, EPixelFormat::PF_R16G16B16A16, TextureType::TT_2D, ERenderTextureContentType::RT_Default, m_RenderTextureConfig, ERenderTextureRenderMode::Direct);
    }
    SetRenderTarget(m_BackFaceRT);
    return true;
}
  • 清除缓冲区 void ClearBackground();
    void Clear(uint32_t flag, const Color& color, float depth, int stencil);
    void Clear(EClearFlags flag, const Color& color, float depth, int stencil);

参数:

参数说明
flag清除缓冲区的类型,包括颜色,深度,模版测试等
color清除颜色缓冲区给的默认值
depth清除深度缓冲区给的默认值
stencil清除模版测试缓冲区给的默认值

描述:以上方法主要是在渲染之前清除上一帧RT缓冲区内容的接口。 代码示例:

cpp
bool VolumePass::Render()
{
	auto viewport = NCamera::GetCurrent()->GetViewport();
    if (!m_BackFaceRT)
    {
        m_BackFaceRT = NResources::GetTempRenderTexture(viewport.width, viewport.height, EPixelFormat::PF_R16G16B16A16, TextureType::TT_2D, ERenderTextureContentType::RT_Default, m_RenderTextureConfig, ERenderTextureRenderMode::Direct);
    }
    SetRenderTarget(m_BackFaceRT);
    Clear(EClearFlags::CF_Color | EClearFlags::CF_Depth, 0, 1.0f, 0);
    return true;
}
  • 设置视口剪裁矩形 void SetupScissorRectAndViewport(const Rectf& rect, int rtWidth, int rtHeight);
    void SetupScissorRectAndViewport(uint32 x, uint32 y, uint32 width, uint32 height);

参数:

参数说明
rect矩形对象
rtWidth视口矩形的宽
rtHeight视口矩形的高
x视口矩形右上角x像素偏移量
y视口矩形右上角y像素偏移量
width视口矩形的宽
height视口矩形的高

描述:通过上面接口可以设置渲染的视口大小以及偏移量。

代码示例:

cpp
bool VolumePass::Render()
{
    auto viewport = NCamera::GetCurrent()->GetViewport();
    if (!m_BackFaceRT)
    {
        m_BackFaceRT = NResources::GetTempRenderTexture(viewport.width, viewport.height, EPixelFormat::PF_R16G16B16A16, TextureType::TT_2D, ERenderTextureContentType::RT_Default, m_RenderTextureConfig, ERenderTextureRenderMode::Direct);
    }
    SetRenderTarget(m_BackFaceRT);
    Clear(EClearFlags::CF_Color | EClearFlags::CF_Depth, 0, 1.0f, 0);
    SetupScissorRectAndViewport(0,0, viewport.width, viewport.height);
    return true;
}
  • 渲染物的脏标记 void MarkRenderObjectContextDirty();

描述:调用此接口可以标记渲染物脏了,然后底层就会更新渲染对象进行渲染。当渲染物的材质发生改变时需要主动调用,否则渲染还是用的之前的材质。

代码示例:

cpp
bool VolumePass::Render()
{
    MarkRenderObjectContextDirty();
    RenderActor(m_CubeAct, EPassLightMode::EPLM_ForwardBase, m_BackMaterial);
    MarkRenderObjectContextDirty();
    RenderActor(m_CubeAct, EPassLightMode::EPLM_ForwardBase, m_FrontMaterial);
    return true;
}
  • 是否需要视锥剔除 void SetNeedFrustumCull(bool isneed);

参数:

参数说明
isneed是否需要进行视锥剔除,true进行视锥剔除,默认是不进行视锥剔除

描述:此方法用来设置渲染时是否进行视锥剔除,默认是不进行视锥剔除。此方法只对调用指定渲染控件RenderActor的逻辑生效。而且不需要每帧调用,在初始化pass是调用一次即可。

代码示例:

cpp
void VolumePass::InitResources()
{
    Super::InitResources();
    SetNeedFrustumCull(true);
}

NRenderPassManager:这是一个渲染pass的管理类,此类负责增删改查渲染pass.

自定义渲染管线开发示例: 1. 设置相机走自定义渲染管线 alt text 2. 通过模版创建渲染通道和自定义渲染器组件。 alt text 3. 将渲染管线类绑定到相机上 alt text 4. 在自定义渲染管线类里面创建pass以及调用pass的render函数 alt text 5. 在RenderPass里面实现自定义渲染管线具体逻辑 alt text