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