1.unity hlsl 9 多光源阴影投射和接收
2.UGUI源码阅读之Mask
3.《Unity 3D 内建着色器源码剖析》第七章 Unity3D全局光照和阴影
4.unity Mask遮罩效果
5.builtIn 转 URP 实操记录
6.Unity源码学习遮罩:Mask与Mask2D
unity hlsl 9 多光源阴影投射和接收
在Unity开发中,实现多光源阴影投射和接收的过程涉及到一些特定的关键字定义和函数调用,本文将详细阐述实现细节以及URP(Unity Render Pipeline)源代码分析,以帮助开发者更好地理解和实现这一功能。
实现多光源阴影投射和接收的核心在于定义必要的关键字和调用相关函数。首先需要定义MainLight关键字,大亨尾数源码这涉及到使用`TransformWorldToShadowCoord()`获取阴影坐标,并通过`MainLightRealtimeShadow(shadowCoord)`获取阴影衰减。对于AdditionalLight,函数调用较为复杂,个人理解可能存在一定混淆。GetAdditionalLight中应使用有shadowMask参数的函数,并以`half4(1,1,1,1)`作为shadowMask参数传递,以正确计算阴影衰减。ShadowCaster Pass负责计算物体的阴影映射并写入纹理,这可以通过调用`UsePass`实现,但需注意支持SRP(Shader Reference Package)的Batcher时,需自行实现ShadowCaster Pass。在设置LightMode以及在计算裁剪空间坐标时,需使用`ApplyShadowBias`函数,并注意z值的正反向判断。
在URP相关源代码分析中,多光源光照和阴影计算主要涉及`Lighting.hlsl`和`Shadows.hlsl`中的函数。GetMainLight函数在无参数的情况下默认阴影衰减为1.0,而有参数时调用`MainLightRealtimeShadow()`或`MainLightShadow()`进行计算。`MainLightShadow`函数直接调用`MainLightRealtimeShadow`并进行混合插值,实现烘焙光照和实时光照的融合。对于AdditionalLight获取,通过调用相关函数并提供index值来获取。rabbit源码解析在计算阴影时,若定义了相关关键字,则会进行阴影映射采样并返回结果。
在实现过程中,开发者需确保正确使用关键字定义、调用函数以及处理相关参数,以确保多光源阴影投射和接收的准确性和效率。具体实现包括使用URP自带的`ShadowCaster`功能或自定义`ShadowCaster Pass`。最终效果展示则需通过实际项目测试和优化,以确保阴影效果的稳定性和视觉表现。在实践中,可能遇到的问题需仔细排查和解决,以获得满意的结果。
UGUI源码阅读之Mask
Mask主要基于模版测试来进行裁剪,因此先来了解一下unity中的模版测试。
Unity Shader中的模版测试配置代码大致如上
模版测试的伪代码大概如上
传统的渲染管线中,模版测试和深度测试一般发生在片元着色器(Fragment Shader)之后,但是现在又出现了Early Fragment Test,可以在片元着色器之前进行。
Mask直接继承了UIBehaviour类,同时继承了ICanvasRaycastFilter和IMaterialModifier接口。
Mask主要通过GetModifiedMaterial修改graphic的Material。大致流程:
1.获取当前Mask的层stencilDepth
2.StencilMaterial.Add修改baseMaterial的模板测试相关配置,并将其缓存
3.StencilMaterial.Add设置一个unmaskMaterial,用于最后将模板值还原
MaskableGraphic通过MaskUtilities.GetStencilDepth计算父节点的Mask层数,然后StencilMaterial.Add修改模板测试的配置。
通过Frame Debugger看看具体每个batch都做了什么。先看第一个,mypetshop源码解读是Mask1的m_MaskMaterial,关注Stencil相关的数值,白色圆内的stencil buffer的值设置为1
这个是Mask2的m_MaskMaterial,根据stencil的计算公式,Ref & ReadMask=1,Comp=Equal,只有stencil buffer & ReadMask=1的像素可以通过模板测试,即第一个白色圆内的像素,然后Pass=Replace,会将通过的像素写入模板值(Ref & WriteMask=3),即两圆相交部分模板值为3
这个是RawImage的Material,只有模板值等于3的像素可以通过模板测试,所以只有两个圆相交的部分可以写入buffer,其他部分舍弃,通过或者失败都不改变模板值
这是Mask2的unmaskMaterial,将两个圆相交部分的模板值设置为1,也就是还原Mask2之前的stencil buffer
这是Mask1的unmaskMaterial,将第一个圆内的模板值设置为0,还有成最初的stencil buffer
可以看到Mask会产生比较严重的overdraw。
2.drawcall和合批
每添加一个mask,一般会增加2个drawcall(加上mask会阻断mask外和mask内的合批造成的额外drawcall),一个用于设置遮罩用的stencil buffer,一个用于还原stencil buffer。
如图,同一个Mask下放置两个使用相同的RawImage,通过Profiler可以看到两个RawImage可以进行合批
如图,两个RawImage使用相同的,它们处于不同的winaoe源码分析Mask之下,但是只要m_StencilValue相等,两个RawImage还是可以进行合批。同时可以看到Mask1和Mask1 (1),Mask2和Mask2 (1)也进行了合批,说明stencilDepth相等的Mask符合合批规则也可以进行合批。
StencilMaterial.Add会将修改后的材质球缓存在m_List中,因此调用StencilMaterial.Add在相同参数情况下将获得同一个材质球。
《Unity 3D 内建着色器源码剖析》第七章 Unity3D全局光照和阴影
在Unity 3D中,全局光照和阴影是实现逼真渲染的重要手段。全局光照分为烘焙式和实时两种方式。静态物体通过烘焙式全局照明(Baked GI)处理,预先计算间接照明并存储,而动态物体则通过光探针获取静态物体的反射光。引擎提供了点光源、聚光灯、有向平行光源和区域面光源等光源类型,其中环境光源与天空盒系统关联,可模拟日出日落效果。
实时光照模式下的光源仅产生直接照明,不涉及间接照明,但在Unity 3D的Lighting设置中,勾选Realtime Global Illumination选项,可实现全局照明,主要适用于主机平台游戏。烘焙式光照贴图通过预先计算并存储直接和间接照明信息,节省运行时计算,但内存占用较大。
混合光照模式允许光源实时调整属性,棋类后台源码提供动态照明,包括Baked Indirect(仅预计算间接照明)、Shadowmask(预计算静态阴影)和Subtractive(烘焙光源信息)等。其中,Shadowmask存储静态阴影信息,Subtractive模式下动态阴影实时投射到静止物体。
光探针技术弥补了光照贴图对动态物体的限制,通过预计算并插值光照信息,提供更真实的动态物体照明效果。然而,光探针有其局限性,如不适用于大物体内部和大凹面表面。此外,还有反射用光探针,用于环境映射。
渲染阴影功能通过光源空间和屏幕空间确定阴影区域,使用阴影贴图(如阴影映射)和层叠式阴影贴图技术来减少透视走样的问题,提高渲染效率和精度。通过这些技术,Unity 3D能为游戏场景提供丰富多样的光照效果和阴影细节。
unity Mask遮罩效果
Unity官方2DMask屏幕遮罩效果,是一种后处理效果,其原理是将源像素与遮罩图形像素相乘。
该效果的处理过程发生在Camera的OnRenderImage方法中。
实现该效果需要使用shader和MaskEffect,同时运用C#编程语言中的Mask类。
Mask类继承自PostEffectsBase,其源码在网络上广泛传播,但无法确定最早的作者。
3D Mask方面,可参考魔镜的相关内容。
在Frame Debugger中,2盏灯会渲染2次,且会导致无法合批batched。
创建一个Plane以及几个Cube,并为Plane创建ASE-Surface和Material,同样为Cube创建相应的Material。
镜子Shader-Plane用于实现场景中的mask遮罩效果。
其原理是利用Plane和Cube的Render Queue值来控制半透明和不透明的渲染顺序。
在Frame Debugger中,魔镜的做法是用天空盒覆盖场景,并设置总是通过模板测试,以覆盖魔镜背后的现实物体。
builtIn 转 URP 实操记录
官方介绍概述了 Universal Render Pipeline(URP),虽然内容详尽,但仍需自行研究细节。网络上其他帖子与教程可以结合阅读。
Catlike Coding 的教程较为详尽,尽管版本可能稍旧。
由于初期未仔细阅读文档,踩了许多坑,以下为记录。
builtIn 转换至 URP 的主要处理内容涉及三个方面:shader、后处理和相机。
一、shader
需将旧的 builtIn 替换为 URP 对应的 hlsl 函数,网上有许多相关帖子。
具体内容需根据项目调整,不明函数可查阅源码。
(1)shader 代码修改
编写了一个简单的转换工具,参考 shaderToy 的工具,使用正则表达式替换相应函数。
(2)SRPBatch
SRPBatch 的开启和使用主要涉及多 pass 合批。
例如,三个正方形使用双 pass,同一 shader,不同材质球。按照 SRPBatch,应合成一个。
需解决 CBUFF 和 LightMode 问题。
1、多 pass 情况下,将面板自定义参数写入 CBUFF。
使用 HLSLINCLUDE...ENDHLSL 写入 SubShader 中,无需每个 pass 添加。完成后,Shader 面板将显示可合批。
但在 Frame Debug 面板中发现因多 pass 导致无法合批,涉及第二个问题。
2、LightMode
使用 URP shader 时,光照 pass 会添加额外的 pass。
SRPDefaultUnlit 和 UniversalForward 在同一渲染队列,导致合批失败。
正确做法是自定义 LightMode。
在创建 URP 管线 PipelineAsset_Renderer 添加 Renderer Feature 添加 Render Object。
添加名称、作用 Layer Mask 等。
将多 pass 中的 LightMode 替换为自定义的 customModel。
此时,Frame Debug 将显示合批成功。
(3)C# 部分渲染代码
涉及 commandBuff 以及 OnPreCull、OnPreRender、OnPostRender 和 OnRenderImage 等函数。
这部分尚不熟悉,后续将深入学习。
二、后处理
PostProcessing-v2 是 unity builtIn 的后处理插件,在 URP 下不支持,需使用 URP 的 Volume。
但在使用过程中发现,原本仅对场景进行后处理,结果 UI 界面也被影响,因此需修改相机。
三、相机
URP 下的相机与 builtIn 不同,具体参数和设置可查看官方文档。
Render Type 包括 Base 和 Overlay。
场景中至少需要一个 Base 类型的相机。
为了解决后处理对 UI 的影响,渲染 UI 的 camera 需设置为 Overlay,并在 Base 类型的 camera 中添加进队列。
cameraData 在相机初始化时不一定存在,需添加。
cameraData.cameraStack.Add(uiCamera) 原则上为 Base 类型 camera,uiCamera 为 Overlay。
camera 设置需自行编写管理类。
例如,打开面板时,场景 Base 类型 camera 被禁用,UI Overlay 相机出现问题,需添加不渲染的 Base 类型 camera。
cameraData 和 uiCamera 均为 Base 时,在 loading 场景时设置会警告并卡死。
需在添加前判断 camera 类型。
在非 loading 情况下,uiCamera 已修改为 Overlay,但判断仍为 Base,但添加后正常。
添加判断导致原本可堆叠的 camera 失效。
需进一步验证。
目前仅记录以上内容,可能存在不足,后续将深入学习弥补。
Unity源码学习遮罩:Mask与Mask2D
Unity源码学习遮罩详解:Mask与Mask2D UGUI裁切功能主要有两种方式:Mask和Mask2D。它们各自有独特的原理和适用场景。1. Mask原理与实现
Mask利用IMaskable和IMaterialModifier功能,通过指定一张裁切图,如圆形,限定子元素的显示区域。GPU通过StencilBuffer(一个用于保存像素标记的缓存)来控制渲染,当子元素像素位于Mask指定区域时,才会被渲染。 StencilBuffer像一个画板,每个像素有一个1字节的内存区域,记录是否被遮盖。当多个UI元素叠加时,通过stencil buffer传递信息,实现精确裁切。2. Mask2D原理
RectMask2D则基于IClippable接口,其裁剪基于RectTransform的大小。在C#层,它找出所有RectMask2D的交集并设置剪裁区域,然后Shader层依据这些区域判断像素是否在内,不满足则透明度设为0。 RectMask2D的性能优化在于无需依赖Image组件,直接使用RectTransform的大小作为裁剪区域。3. 性能区别
Mask需要Image组件,裁剪区域受限于Image,而RectMask2D独立于Image,裁剪灵活。因此,Mask2D在不需要复杂裁剪时更高效。 总结:虽然Mask和Mask2D各有优势,选择哪种遮罩取决于具体需求,合理使用能提高性能和用户体验。