【渲染】Unity-分析URP的延迟渲染-DeferredShading

我是一名资深游戏开发,小时候喜欢看十万个为什么

介绍

  • 本文旨在搞清楚延迟渲染在unity下如何实现的,为自己写延迟渲染打一个基础,打开从知到行的大门
  • 延迟渲染 = 输出物体表面信息(rt1, rt2, rt3, …) + 着色(rt1, rt2, rt3, …)
  • 研究完感觉核心特征像后处理,像屏幕空间效果

要研究的问题

怎么生成G-Buffer,生成了哪些数据

生成G-Buffer

DeferredLights类里创建G-Buffer纹理资源句柄,管线setup时创建实际的RT资源

GBufferPass类里填充G-Buffer

  • Config方法里ConfigureTarget
    • 调用CoreUtils绑定RenderTarget,最终调用CommandBuffer的SetRenderTarget把GBuffer对应纹理绑定输出
  • ExecutePass方法中绘制物体
    • context.DrawRenderers(renderingData.cullResults, ref data.drawingSettings, ref data.filteringSettings, s_ShaderTagUniversalMaterialType, false, tagValues, stateBlocks);
  • shader中填充数据,见 UnityGBuffer.hlsl 中 SurfaceDataToGbuffer、 BRDFDataToGbuffer 方法

输出了下面的数据

见文件:UnityGBuffer.hlsl

half4 GBuffer0 : SV_Target0; //diffuse,表面颜色
half4 GBuffer1 : SV_Target1; //metallic/specular,高光
half4 GBuffer2 : SV_Target2; //encode normal,法线
half4 GBuffer3 : SV_Target3; // Camera color attachment,GI,全局光#ifdef GBUFFER_OPTIONAL_SLOT_1
GBUFFER_OPTIONAL_SLOT_1_TYPE GBuffer4 : SV_Target4; //clip z,剪裁空间z值,即深度
#endif

怎么传入多个光源计算光照

逐类型光源

DeferredLights里RenderStencilLights,逐个调用直射光、点光源、射灯进行渲染

using (new ProfilingScope(cmd, m_ProfilingSamplerDeferredStencilPass))
{NativeArray<VisibleLight> visibleLights = renderingData.lightData.visibleLights;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);
}

绘制命令

  • 直射光,遍历光源,逐光源DrawCall
for (int soffset = m_stencilVisLightOffsets[(int)LightType.Directional]; soffset < m_stencilVisLights.Length; ++soffset)省略cmd.SetGlobalVector(ShaderConstants._LightColor, lightColor); // VisibleLight.finalColor already returns color in active color spacecmd.SetGlobalVector(ShaderConstants._LightDirection, lightDir);cmd.SetGlobalInt(ShaderConstants._LightFlags, lightFlags);cmd.SetGlobalInt(ShaderConstants._LightLayerMask, (int)lightLayerMask);// 因为GBufferPass已经把光照数据都输出到纹理,这里只需要绘制全屏的mesh,在shader中采样之前输出的GBuffer计算光照// Lighting pass.cmd.DrawMesh(m_FullscreenMesh, Matrix4x4.identity, m_StencilDeferredMaterial, 0, m_StencilDeferredPasses[(int)StencilDeferredPasses.DirectionalLit]);cmd.DrawMesh(m_FullscreenMesh, Matrix4x4.identity, m_StencilDeferredMaterial, 0, m_StencilDeferredPasses[(int)StencilDeferredPasses.DirectionalSimpleLit]);省略
  • 点光源、射灯也是遍历光源,逐光源DrawCall,但是mesh不是全屏mesh,是代表光源形状的memsh,代码不赘述,今天不水文

渲染

入口StencilDeferred.shader

half4 DeferredShading(Varyings input) : SV_Target//省略部分代码,这些代码是:取GBuffer的值,拼出计算光照需要的数据//计算光照InputData inputData = InputDataFromGbufferAndWorldPosition(gbuffer2, posWS.xyz);#if defined(_LIT)#if SHADER_API_MOBILE || SHADER_API_SWITCH// Specular highlights are still silenced by setting specular to 0.0 during gbuffer pass and GPU timing is still reduced.bool materialSpecularHighlightsOff = false;#elsebool materialSpecularHighlightsOff = (materialFlags & kMaterialFlagSpecularHighlightsOff);#endifBRDFData brdfData = BRDFDataFromGbuffer(gbuffer0, gbuffer1, gbuffer2);color = LightingPhysicallyBased(brdfData, unityLight, inputData.normalWS, inputData.viewDirectionWS, materialSpecularHighlightsOff);#elif defined(_SIMPLELIT)SurfaceData surfaceData = SurfaceDataFromGbuffer(gbuffer0, gbuffer1, gbuffer2, kLightingSimpleLit);half3 attenuatedLightColor = unityLight.color * (unityLight.distanceAttenuation * unityLight.shadowAttenuation);half3 diffuseColor = LightingLambert(attenuatedLightColor, unityLight.direction, inputData.normalWS);half smoothness = exp2(10 * surfaceData.smoothness + 1);half3 specularColor = LightingSpecular(attenuatedLightColor, unityLight.direction, inputData.normalWS, inputData.viewDirectionWS, half4(surfaceData.specular, 1), smoothness);// TODO: if !defined(_SPECGLOSSMAP) && !defined(_SPECULAR_COLOR), force specularColor to 0 in gbuffer codecolor = diffuseColor * surfaceData.albedo + specularColor;#endifreturn half4(color, alpha);

渲染物体

不透明物体

输出G-Buffer,渲染着色

半透物体

延迟渲染不支持半透,所以走Forward渲染,用额外一个 ScriptableRenderPass 渲染半透,见UniversalRenderer 的 m_RenderTransparentForwardPass

贴花

使用方法

  • URP管线资源里创建Renderer,设置使用Deferred
  • 相机里Renderer选上面创建的Renderer

延伸知识

SSAO

  • Screen Space Ambient Occlusion -> 屏幕空间环境光遮蔽
  • SSAO 通过使用深度缓冲、法线缓冲和随机采样核生成遮蔽效果,模拟场景中物体周围环境光被遮挡的情况,从而增强画面的真实感和层次感,让场景看起来更有深度

源码分析

结论

管线流程

  • 生成GBuffer
    • shader中通过SV_TargetXXX指定输出到某个绑定的缓冲区
    • 要求图形接口支持一次输出到多个目标
  • 通过GBuffer渲染

管线

GBufferPass

输出GBuffer

DeferredPass

用GBuffer着色

DrawObjectsPass

绘制物体,不透、半透

RenderGraph

渲染节点图,可以通过编辑器定制渲染管线

DeferredLights

延迟渲染具体的逻辑

shader源码分析

Lit.shader

GBuffer Pass,输出GBuffer数据的Pass

LitGBufferPass.hlsl

输出GBuffer的Pass源码

UnityGBuffer.hlsl

真干活的着色代码

像素着色器输出
struct FragmentOutput
{half4 GBuffer0 : SV_Target0; //diffuse,表面颜色half4 GBuffer1 : SV_Target1; //metallic/specular,高光half4 GBuffer2 : SV_Target2; //encode normal,法线half4 GBuffer3 : SV_Target3; // Camera color attachment,GI,全局光#ifdef GBUFFER_OPTIONAL_SLOT_1GBUFFER_OPTIONAL_SLOT_1_TYPE GBuffer4 : SV_Target4; //clip z,剪裁空间z值,即深度#endif#ifdef GBUFFER_OPTIONAL_SLOT_2half4 GBuffer5 : SV_Target5;#endif#ifdef GBUFFER_OPTIONAL_SLOT_3half4 GBuffer6 : SV_Target6;#endif
};输出GBuffer
FragmentOutput SurfaceDataToGbuffer(SurfaceData surfaceData, InputData inputData, half3 globalIllumination, int lightingMode)
{half3 packedNormalWS = PackNormal(inputData.normalWS);uint materialFlags = 0;// SimpleLit does not use _SPECULARHIGHLIGHTS_OFF to disable specular highlights.#ifdef _RECEIVE_SHADOWS_OFFmaterialFlags |= kMaterialFlagReceiveShadowsOff;#endif#if defined(LIGHTMAP_ON) && defined(_MIXED_LIGHTING_SUBTRACTIVE)materialFlags |= kMaterialFlagSubtractiveMixedLighting;#endifFragmentOutput output;output.GBuffer0 = half4(surfaceData.albedo.rgb, PackMaterialFlags(materialFlags));   // albedo          albedo          albedo          materialFlags   (sRGB rendertarget)output.GBuffer1 = half4(surfaceData.specular.rgb, surfaceData.occlusion);            // specular        specular        specular        occlusionoutput.GBuffer2 = half4(packedNormalWS, surfaceData.smoothness);                     // encoded-normal  encoded-normal  encoded-normal  smoothnessoutput.GBuffer3 = half4(globalIllumination, 1);                                      // GI              GI              GI              unused          (lighting buffer)#if _RENDER_PASS_ENABLEDoutput.GBuffer4 = inputData.positionCS.z;#endif#if OUTPUT_SHADOWMASKoutput.GBUFFER_SHADOWMASK = inputData.shadowMask; // will have unity_ProbesOcclusion value if subtractive lighting is used (baked)#endif#ifdef _WRITE_RENDERING_LAYERSuint renderingLayers = GetMeshRenderingLayer();output.GBUFFER_LIGHT_LAYERS = float4(EncodeMeshRenderingLayer(renderingLayers), 0.0, 0.0, 0.0);#endifreturn output;
}

StencilDeferred.shader

使用GBuffer进行着色

StencilDeferred.hlsl

顶点、像素方法

着色
half4 DeferredShading(Varyings input) : SV_Target...省略代码,不水字数,感兴趣的朋友看URP源码即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/84279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为OD机考- 简单的自动曝光/平均像素

import java.util.Arrays; import java.util.Scanner;public class DemoTest4 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint[] arr Array…

java 乐观锁的实现和注意细节

文章目录 1. 前言乐观锁 vs. 悲观锁&#xff1a;基本概念对比使用场景及优势简述 2. 基于版本号的乐观锁实现代码示例注意事项 3. 基于CAS机制的乐观锁实现核心思想代码示例关键点说明 4. 框架中的乐观锁实践MyBatis中基于版本号的乐观锁实现示例代码 JPA&#xff08;Hibernate…

河北对口计算机高考C#笔记(2026高考适用)---持续更新~~~~

C#笔记 C#发展史 1998年,C#发布第一个版本。2002年,visual studio开发环境推出C#的特点 1.语法简洁,不允许直接操作内存,去掉了指针操作 2.彻底面向对象设计。 3.与Web紧密结合。 4.强大的安全机制,语法错误提示,引入垃圾回收器机制。 5.兼容性。 6.完善的错误,异常处理…

C# dll版本冲突解决方案

随着项目功能逐渐增加&#xff0c;引入三方库数量也会增多。不可避免遇到库的间接引用dll版本冲突&#xff0c;如System.Memory.dll、System.Buffer.dll等。编译会报警&#xff0c;运行可能偶发异常。 可使用ILMerge工具合并动态库&#xff0c;将一个库的多个dll合并为一个dll。…

深度解析:etcd 在 Milvus 向量数据库中的关键作用

目录 &#x1f680; 深度解析&#xff1a;etcd 在 Milvus 向量数据库中的关键作用 &#x1f4a1; 什么是 etcd&#xff1f; &#x1f9e0; Milvus 架构简介 &#x1f4e6; etcd 在 Milvus 中的核心作用 &#x1f527; 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…

随机访问介质访问控制:网络中的“自由竞争”艺术

想象一场自由辩论赛——任何人随时可以发言&#xff0c;但可能多人同时开口导致混乱。这正是计算机网络中随机访问协议的核心挑战&#xff1a;如何让多个设备在共享信道中高效竞争&#xff1f;本文将深入解析五大随机访问技术及其智慧。 一、核心思想&#xff1a;自由竞争 冲突…

设计模式作业

package sdau;public class man {public static void main(String[] args) {show(new Cat()); // 以 Cat 对象调用 show 方法show(new Dog()); // 以 Dog 对象调用 show 方法Animal a new Cat(); // 向上转型 a.eat(); // 调用的是 Cat 的 eatCat c (Cat)a…

Kaspa Wasm SDK

文章目录 1. 简要2. github地址 1. 简要 kaspa wallet SDK&#xff0c;在官方WASM基础上封装了应用层的方法&#xff0c;简便了WASM的初始化及调用。 核心功能包括如下&#xff1a; 账户地址生成及管理Kaspa Api 和 Kasplex Api的封装kaspa结点RPC 封装P2SH的各个场景script封…

ROS mapserver制作静态地图

ROS mapserver制作静态地图 静态地图构建 1、获取一个PNG地图&#xff0c;二值化 2、基于PNG地图&#xff0c;生成PGM地图&#xff0c;可以通过一些网站在线生成&#xff0c;例如Convertio 文件配置 1、将文件放置于/package/map路径下。 2、编写yaml文件&#xff0c;如下…

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…

浏览器工作原理05 [#] 渲染流程(上):HTML、CSS和JavaScript是如何变成页面的

引用 浏览器工作原理与实践 一、提出问题 在上一篇文章中我们介绍了导航相关的流程&#xff0c;那导航被提交后又会怎么样呢&#xff1f;就进入了渲染阶段。这个阶段很重要&#xff0c;了解其相关流程能让你“看透”页面是如何工作的&#xff0c;有了这些知识&#xff0c;你可…

DrissionPage爬虫包实战分享

一、爬虫 1.1 爬虫解释 爬虫简单的说就是模拟人的浏览器行为&#xff0c;简单的爬虫是request请求网页信息&#xff0c;然后对html数据进行解析得到自己需要的数据信息保存在本地。 1.2 爬虫的思路 # 1.发送请求 # 2.获取数据 # 3.解析数据 # 4.保存数据 1.3 爬虫工具 Dris…

android 布局小知识点 随记

1. 布局属性的命名前缀规律 与父容器相关的前缀 layout_alignParent&#xff1a;相对于父容器的对齐方式。 例如&#xff1a;layout_alignParentTop"true"&#xff08;相对于父容器顶部对齐&#xff09;。layout_margin&#xff1a;与父容器或其他控件的边距。 例如…

GeoDrive:基于三维几何信息有精确动作控制的驾驶世界模型

25年5月来自北大、理想汽车和 UC Berkeley 的论文“GeoDrive: 3D Geometry-Informed Driving World Model with Precise Action Control”。 世界模型的最新进展彻底改变动态环境模拟&#xff0c;使系统能够预见未来状态并评估潜在行动。在自动驾驶中&#xff0c;这些功能可帮…

Java高频面试之并发编程-25

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天又来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;CAS都有哪些问题&#xff1f;如何解决&#xff1f; CAS 的问题及解决方案 CAS&#xff08;Compare and Swap&#xff0…

从碳基羊驼到硅基LLaMA:开源大模型家族的生物隐喻与技术进化全景

在人工智能的广袤版图上&#xff0c;一场从生物学羊驼到数字智能体的奇妙转变正在上演。Meta推出的LLaMA(Large Language Model Meta AI)系列模型&#xff0c;不仅名字源自美洲驼(llama)&#xff0c;更以其开源特性和强大性能&#xff0c;引领了开源大模型社区的“驼类大爆发”…

可下载旧版app屏蔽更新的app市场

软件介绍 手机用久了&#xff0c;app越来越臃肿&#xff0c;老手机卡顿成常态。这里给大家推荐个改善老手机使用体验的方法&#xff0c;还能帮我们卸载不需要的app。 手机现状 如今的app不断更新&#xff0c;看似在优化&#xff0c;实则内存占用越来越大&#xff0c;对手机性…

Python_day47

作业&#xff1a;对比不同卷积层热图可视化的结果 一、不同卷积层的特征特性 卷积层类型特征类型特征抽象程度对输入的依赖程度低层卷积层&#xff08;如第 1 - 3 层&#xff09;边缘、纹理、颜色、简单形状等基础特征低高&#xff0c;直接与输入像素关联中层卷积层&#xff08…

比较数据迁移后MySQL数据库和达梦数据库中的表

设计一个MySQL数据库和达梦数据库的表数据比较的详细程序流程&#xff0c;两张表是相同的结构&#xff0c;都有整型主键id字段&#xff0c;需要每次从数据库分批取得2000条数据&#xff0c;用于比较&#xff0c;比较操作的同时可以再取2000条数据&#xff0c;等上一次比较完成之…

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…