Unity URP延迟渲染流程简单分析(Deferred Rendering)

这篇具有很好参考价值的文章主要介绍了Unity URP延迟渲染流程简单分析(Deferred Rendering)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

  大概两个月前的某个Unity项目里选择使用了URP延迟渲染管线(其实没必要,主要是因为自己想用一用、学一学,因为后续自己的项目中需要使用延迟渲染),但在写Shader实现物体表面着色时却一直有个疑惑:

  延迟渲染应该是先一个Pass将信息存储在GBuffer中,然后再一个Pass进行实际的着色,而且通常来说使用的都是同一套着色模型,而自己在前向渲染中编写使用的自定义着色的Shader挪到延迟渲染中却看起来没有什么问题。这让我不禁怀疑起自己,又或者Unity有着强大的延迟渲染管线中的Shader扩展支持。但因为延迟渲染是准备之后仔细研究的,所以疑惑一直留到了前几天。

  就在前两天因为五一假期所以在做自己的项目,于是决定研究一下上边的问题。这时突然想起来之前有看到过Unity的延迟渲染其实是混合了前向渲染的,主要是为了支持透明物体的渲染。拿多个光源照一下物体发现有光源数量限制,又翻了一下Frame Debug,果然自己写的Shader其实走的是延迟渲染后的前向渲染(Render GBuffer里当然也没有对应的信息),问题还没开始就结束了:
urp延迟渲染,unity,游戏引擎,图形渲染
urp延迟渲染,unity,游戏引擎,图形渲染

  然后大概看了看,发现如果想自己自定义着色效果而且走延迟渲染,可能还得改一改URP的代码(不过写个Shader在GBuffer Pass里动动手脚大概也能不改管线实现点效果,但感觉限制比较大),而在改之前就先要了解一下URP中的延迟渲染到底是个什么流程。

  这篇文章原本是打算学习使用一段时间后再整理一下,但最近可能要去忙其他事情,这里先写一部分,后续再补充。对Unity渲染管线了解不多,以下内容是对假期时学习的总结,仅整理大致流程方便翻找代码,可能有误,仅供参考。

GBuffer

  首先来看一下GBuffer的准备部分。
  从Universal Render Pipeline/Lit(也就是默认使用的Lit.shader)入手,在项目Project窗口Packages/Universal RP/Shaders下可以找到(右键->Show in Explorer可以看到文件的实际位置),而在这个Shader中我们能看到这么一个Pass:

	Name "GBuffer"
    Tags{"LightMode" = "UniversalGBuffer"}

  也就是在这里,ps将所需的数据写入了GBuffer,但这个Shader里并没有具体的vs或ps的实现,而是被包装在了LitGBufferPass.hlsl中:

	#pragma vertex LitGBufferPassVertex
    #pragma fragment LitGBufferPassFragment

    #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
    #include "Packages/com.unity.render-pipelines.universal/Shaders/LitGBufferPass.hlsl"

  在Shaders/LitGBufferPass.hlsl中,我们主要关注ps阶段,其中主要的部分就是通过InitializeStandardLitSurfaceData()获取了物体表面的材质信息,中间InitializeBRDFData()将其转为了BRDF参数(感觉起来这两者主要是对输入的参数进行打包、整理等),然后获取了全局光照,最后BRDFDataToGbuffer()即输出的Gbuffer数据(数据类型为FragmentOutput)。

    SurfaceData surfaceData;
    InitializeStandardLitSurfaceData(input.uv, surfaceData);

    InputData inputData;
    InitializeInputData(input, surfaceData.normalTS, inputData);
    SETUP_DEBUG_TEXTURE_DATA(inputData, input.uv, _BaseMap);

#ifdef _DBUFFER
    ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData);
#endif

    // Stripped down version of UniversalFragmentPBR().

    // in LitForwardPass GlobalIllumination (and temporarily LightingPhysicallyBased) are called inside UniversalFragmentPBR
    // in Deferred rendering we store the sum of these values (and of emission as well) in the GBuffer
    BRDFData brdfData;
    InitializeBRDFData(surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.alpha, brdfData);

    Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask);
    MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, inputData.shadowMask);
    half3 color = GlobalIllumination(brdfData, inputData.bakedGI, surfaceData.occlusion, inputData.positionWS, inputData.normalWS, inputData.viewDirectionWS);

    return BRDFDataToGbuffer(brdfData, inputData, surfaceData.smoothness, surfaceData.emission + color, surfaceData.occlusion);

  其中InitializeStandardLitSurfaceData()在Shaders/LitInput.shader中定义,而其输出的SurfaceData结构则定义在ShaderLibrary/SurfaceData.hlsl中;InitializeBRDFData()及其输出的结构定义在ShaderLibrary/BRDF.hlsl中;BRDFDataToGbuffer()部分(也是我们比较关心的部分)定义在ShaderLibrary/UnityGBuffer.hlsl中。
  在ShaderLibrary/UnityGBuffer.hlsl中,我们可以看到FragmentOutput的定义:

struct FragmentOutput
{
    half4 GBuffer0 : SV_Target0;
    half4 GBuffer1 : SV_Target1;
    half4 GBuffer2 : SV_Target2;
    half4 GBuffer3 : SV_Target3; // Camera color attachment

    #ifdef GBUFFER_OPTIONAL_SLOT_1
    GBUFFER_OPTIONAL_SLOT_1_TYPE GBuffer4 : SV_Target4;
    #endif
    #ifdef GBUFFER_OPTIONAL_SLOT_2
    half4 GBuffer5 : SV_Target5;
    #endif
    #ifdef GBUFFER_OPTIONAL_SLOT_3
    half4 GBuffer6 : SV_Target6;
    #endif
};

可以看到也是使用了MRT。然后找到BRDFDataToGbuffer()函数,代码以及注释写出了对GBuffer的分配情况:

	FragmentOutput output;
    output.GBuffer0 = half4(brdfData.albedo.rgb, PackMaterialFlags(materialFlags));  // diffuse           diffuse         diffuse         materialFlags   (sRGB rendertarget)
    output.GBuffer1 = half4(packedSpecular, occlusion);                              // metallic/specular specular        specular        occlusion
    output.GBuffer2 = half4(packedNormalWS, smoothness);                             // encoded-normal    encoded-normal  encoded-normal  smoothness
    output.GBuffer3 = half4(globalIllumination, 1);                                  // GI                GI              GI              [optional: see OutputAlpha()] (lighting buffer)
    ...

	return output;

到此,GBuffer的信息被写入。

Lighting

  Gbuffer有了,那么使用GBuffer进行实际画面渲染的部分在哪?在Runtime/UniversalRenderer.cs的构造函数中我们可以看到这么一段代码,包括Material的赋值、new DeferredLights、new DeferredPass:

	public UniversalRenderer(UniversalRendererData data) : base(data)
	{
		...
	    //m_TileDeferredMaterial = CoreUtils.CreateEngineMaterial(data.shaders.tileDeferredPS);
	    m_StencilDeferredMaterial = CoreUtils.CreateEngineMaterial(data.shaders.stencilDeferredPS);
	
		...
		
	    if (this.renderingMode == RenderingMode.Deferred)
	    {
	        var deferredInitParams = new DeferredLights.InitParams();
	        ...
	        deferredInitParams.tileDeferredMaterial = m_TileDeferredMaterial;
	        deferredInitParams.stencilDeferredMaterial = m_StencilDeferredMaterial;
	        ...
	        m_DeferredLights = new DeferredLights(deferredInitParams, useRenderPassEnabled);
	        ...
	        m_DeferredLights.TiledDeferredShading = false;
	        
	        ...
	        
	        m_DeferredPass = new DeferredPass(RenderPassEvent.BeforeRenderingDeferredLights, m_DeferredLights);
	        ...
	    }
	
	    ...
	    
	}

  从后往前看,DeferredPass(Runtime/Passes/DeferredPass.cs)就是一个ScriptableRenderPass,在Execute中执行了DeferredLights.ExecuteDeferredPass()。而DeferredLights(Runtime/DeferredLights.cs)中ExecuteDeferredPass部分如下:

	internal void ExecuteDeferredPass(ScriptableRenderContext context, ref RenderingData renderingData)
	{
	    ...
	
	    CommandBuffer cmd = CommandBufferPool.Get();
	    using (new ProfilingScope(cmd, m_ProfilingDeferredPass))
	    {
	        ...
	        
	        RenderStencilLights(context, cmd, ref renderingData);
	
	        RenderTileLights(context, cmd, ref renderingData);
	
	        ...
	    }
	
	    ...
	
	    context.ExecuteCommandBuffer(cmd);
	    CommandBufferPool.Release(cmd);
	}

  执行了两个RenderXXX函数,但RenderTileLights()并没有实际执行,注意之前所说的UniversalRenderer的构造函数中,tileDeferredMaterial被赋了空值,TiledDeferredShading也被置为false。而RenderStencilLights()中便是我们想找到的东西:

	void RenderStencilLights(ScriptableRenderContext context, CommandBuffer cmd, ref RenderingData renderingData)
	{
	    ...
	    
	    using (new ProfilingScope(cmd, m_ProfilingSamplerDeferredStencilPass))
	    {
	        ...
	
	        if (HasStencilLightsOfType(LightType.Directional))
	            RenderStencilDirectionalLights(cmd, ref renderingData, visibleLights, renderingData.lightData.mainLightIndex);
	        if (HasStencilLightsOfType(LightType.Point))
	            RenderStencilPointLights(cmd, ref renderingData, visibleLights);
	        if (HasStencilLightsOfType(LightType.Spot))
	            RenderStencilSpotLights(cmd, ref renderingData, visibleLights);
	    }
	
	    ...
	}
	
	void RenderStencilPointLights(CommandBuffer cmd, ref RenderingData renderingData, NativeArray<VisibleLight> visibleLights)
	{
	    ...
	
	    for (int soffset = m_stencilVisLightOffsets[(int)LightType.Point]; soffset < m_stencilVisLights.Length; ++soffset)
	    {
	        ...
	
	        // Stencil pass.
	        cmd.DrawMesh(m_SphereMesh, transformMatrix, m_StencilDeferredMaterial, 0, m_StencilDeferredPasses[(int)StencilDeferredPasses.StencilVolume]);
	
	        // Lighting pass.
	        cmd.DrawMesh(m_SphereMesh, transformMatrix, m_StencilDeferredMaterial, 0, m_StencilDeferredPasses[(int)StencilDeferredPasses.PunctualLit]);
	        cmd.DrawMesh(m_SphereMesh, transformMatrix, m_StencilDeferredMaterial, 0, m_StencilDeferredPasses[(int)StencilDeferredPasses.PunctualSimpleLit]);
	    }
	
	    ...
	}
	

  所以我们想找的shader就是m_StencilDeferredMaterial所对应的shader。于是回到开头,这个m_StencilDeferredMaterial是从UniversalRenderer构造函数中的UniversalRendererData(Runtime/UniversalRendererData.cs)里来的,而这个data的实例就是我们创建URP项目使用的那几个URP管线配置文件的其中一个(也可以通过Create->Rendering->URP Universal Renderer新建一个,或者URP包里Runtime/Data/UniversalRendererData也可以看到):

urp延迟渲染,unity,游戏引擎,图形渲染
  我们在UniversalRendererData.cs代码里或者点击这个界面最右上角三个小点并选择Debug,都会发现stencilDeferredPS使用的是Shaders/Utils/StencilDeferred.shader(具体光照在ShaderLibrary/Lighting.hlsl中实现)。

  到此,一切差不多都串了起来。

Stencil

  但还有一个疑问,Stencil(模板)这个词在代码中频繁出现,而TiledDeferred却并没有实际使用。实际上Unity自己在这里讲的很清楚,他们最后采用了基于模板剔除光照的延迟渲染(Stencil,就是管线中深度检测、模板检测中的模板)而非经常被讨论的分块剔除光照的延迟渲染(Tiled-Based)。(一开始我只是在网上简单翻过一些资料了解Unity的延迟渲染管线,还以为Stencil只是说Unity自己写了一个延迟渲染的模板所以叫StencilDeferred…)

  而在之前提到的RenderStencilXXXLights函数中(当然,DirectionalLight除外),我们也会发现Unity先执行了Stencil Pass,然后执行了Lighting Pass。简单来说就是在光源处先使用一个凸几何体写入Stencil,仅覆盖了光能够照到的区域,然后再只对这部分区域进行该光源光照的着色。所以其实是Unity原本实现了Stencil和Tiled-Based两种,但综合考虑(不同设备支持、性能等)最终选择了Stencil。

urp延迟渲染,unity,游戏引擎,图形渲染

urp延迟渲染,unity,游戏引擎,图形渲染文章来源地址https://www.toymoban.com/news/detail-733874.html

到了这里,关于Unity URP延迟渲染流程简单分析(Deferred Rendering)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity升级到URP渲染管线,

    首先 需要安装URP的包 安装后 然后打开ProjectSettings 替换 上一步创建 URP的配置文件 这个时候有些材质就会变成洋红色 选择RenderPipelineConverter 等待转换完成即可,

    2024年02月15日
    浏览(54)
  • Unity URP渲染管线与内置渲染管线的性能差别

    首先,我们来了解一下Unity的内置渲染管线。内置渲染管线是Unity较早版本中使用的默认渲染管线,它使用的是传统的图形渲染技术。内置渲染管线提供了一系列的渲染功能,如阴影、反射、抗锯齿等。但是,由于其较为庞大且复杂的设计,它的性能相对较低。在高质量图形效

    2024年02月08日
    浏览(41)
  • Unity内置渲染管线升级URP教程

    URP全称为Universal Render Pipeline(通用渲染管线),可以提供更加灵活的渲染方案,通过添加Render Feature实现各种渲染效果。并且可以针对移动平台进行专门的优化,同时还提供了SRPBatcher提高渲染效率。Unity的一些工具,比如ShaderGraph,也是必须在URP管线下才可以使用,可以说许多方

    2024年02月16日
    浏览(56)
  • Unity——URP & HDRP 渲染模式学习笔记

    目录 HDRP和UPR是两种完全不同的渲染模式,不能混用,只能选择其中一种,最好在项目刚开始的时候就确定好。否则后期所有模型的材质都要改变非常麻烦。 修改渲染模式方法: HDRP, URP, BRP区别 备注 project settins: 1. graphics-Scriptable render pipeline settings 要修改,假如从hdrp改为

    2024年02月04日
    浏览(48)
  • [Unity/URP学习]风格化水体渲染(一)

    着色:水体颜色、水体反射、水体折射、岸边泡沫、水面于天空沿边线消除、水体焦散 动画处理:水体流动、顶点动画、水体交互、水体浮力 (实现顺序没有严格按照着色和动画处理的分类来实现) 要制作水体颜色,要考虑的内容如下: 风格化的水体渐变颜色、水体深浅区

    2024年02月13日
    浏览(57)
  • unity build-in 渲染管线升级urp渲染 shader篇

            由于工作原因需要对项目进行升级,从build-in渲染管线升级到urp渲染管线,我自己对应的unity版本是2018.版本升级到2021.3.2版本,由于最近几年unity版本升级比较快,个体版本差异有所不同,如遇与版本不一致问题敬请谅解。以下是根据官网等系列网站整理的内容

    2023年04月16日
    浏览(59)
  • Unity Shader从内置渲染管线迁移到URP

    Unity 在URP中将shader更新为了HLSL语言,使用build-in shader 无法直接在URP中使用 这里讲一下关于shader的更新方法 参考 From Built-in to URP Tags 添加 \\\"RenderPipeline\\\" = \\\"UniversalPipeline\\\" CGPROGRAM ENDCG 改变为 HLSLPROGRAM ENDHLSL #include \\\"UnityCG.cginc\\\" 更改为 #include \\\"Packages/com.unity.render-pipelines.universal/Sh

    2024年02月05日
    浏览(47)
  • 渲染路径 - Deferred Texturing

    目录 Deferred Texturing 为什么需要 Deferred Texturing? 光栅化的 Helper Lane 开销 Draw Call 更容易合批 利用 V-Buffer 可以做更多事情 Visibility Buffer Material Culling 是否抛弃 G-Buffer? 抛弃 G-Buffer 的 Deferred Texturing 保留 G-Buffer 的 Deferred Texturing Deferred Texturing with G-Buffer 流程 Z Pre-pass Visibility

    2024年02月13日
    浏览(27)
  • Unity-3DRPG游戏 学习笔记(1)--使用URP渲染管线

    教程地址: Unity2020 3DRPG游戏开发教程|Core核心功能01:Create Project 创建项目导入素材|Unity中文课堂_哔哩哔哩_bilibili 创建URP通用渲染管线(2021版本) 1. 打开:Windows--Package Manager--左上角下拉选择Unity Registry--搜索Universal RP--Install 2. Project窗口--Assets--右键Create--Rendering--URP Asstes

    2024年02月11日
    浏览(46)
  • 【URP趟坑】Unity在URP中使用RenderTexture在UI中渲染带透明相机背景的PostProcessing场景

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 最近有个需求,需要使用Unity的URP通用管线来开发项目,其中有个场景是需要在UI中显示动态的角色模型,而且背景是透明的。初闻这个需求感觉挺简单的,普通管线下使用Rendertexture输出相机视角的内容

    2024年02月04日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包