转载:https://blog.csdn.net/weixin_42163773/article/details/84317223

近日在学习Unity3d的SRP,由于官方未正式发布,故几乎没有文档支持,考虑到官方一贯的风格,即使正式发布了,估计Shader部分也不会有详尽的文档。所以,干脆自己在研究学习源码之余,写了一份文档,既加深自己的理解,也方便后继的学习者。目前只完成了轻量管线部分的文档,先发布初版,欢迎指正。后续还有Core库、高清管线和ShaderGraph的文档正在编写中。

Runtime

LightweightPipelineAsset.cs

管线描述文件,继承自RenderPipelineAsset,是渲染管线的主入口,通过在GraphicsSettings中指定Scriptable Render Pipeline Settings接入引擎主体渲染框架。

s_SearchPathProject = "Assets"

定义了工程默认搜索路径,会优先从这个目录搜寻管线内建资源清单。

s_SearchPathPackage = "Packages/com.unity.render-pipelines.lightweight"

定义了Package默认搜索路径,如果工程路径中没有找到,会从这里搜寻管线内建资源清单。

CreateLightweightPipeline

创建管线描述文件自身。

CreateLightweightPipelineResources

创建管线内建资源清单文件。

CreateLightweightPipelineEditorResources

创建编辑器用管线内建资源清单文件。

CreateLightweightPipelineAsset

管线资源文件改名功能。

InternalCreatePipeline

内部用创建轻量管线。

GetMaterial

获取材质。

GetDefaultMaterial

获得默认的材质。

GetDefaultParticleMaterial

获得默认的粒子材质。

GetDefaultLineMaterial

获得默认的线条材质。

GetDefaultTerrainMaterial

获得默认的地形材质。

GetDefaultUIMaterial

获得默认的界面材质。

GetDefaultUIOverdrawMaterial

获得默认的界面过绘制材质。

GetDefaultUIETC1SupportedMaterial

获得默认的界面ETC1兼容材质。

GetDefault2DMaterial

获得默认的2D材质。

GetDefaultShader

获得默认的Shader

LightweightPipelineEditorResources.cs

编辑器用管线内建资源清单文件

DefaultMaterial

默认材质

DefaultParticleMaterial

默认粒子材质

DefaultTerrainMaterial

默认地形材质

LightweightPipelineResources.cs

管线内建资源清单文件

BlitShader

全屏拷贝用Shader

CopyDepthShader

拷贝深度用Shader

ScreenSpaceShadowShader

屏幕空间阴影Shader

SamplingShader

采样Shader

以下以Pass结尾的均为渲染步骤

架构上极度类似我们RenderStep的设计,用一个List对这些步骤进行一定自由度的排列组合,在执行每个相机的渲染时,依次调用该列表中所有渲染步骤的Execute接口。

BeginXRRenderingPass.cs

开始XX现实多眼睛渲染。

CopyColorPass.cs

色彩拷贝渲染过程。在渲染过非透明物体以及非透明物体的屏幕后处理效果之后,把当前的颜色缓存保存到额外的一个RT中,“_CameraOpaqueTexture”。这里拷贝出的颜色缓冲的值根据配置可能执行不同程度的降采样或者多重采样。目的可能是为了方便后处理的某些方法进行优化。

CopyDepthPass.cs

深度拷贝渲染过程。在渲染过非透明物体以及非透明物体的屏幕后处理效果之后,透明物体之前,把当前的深度缓存保存到额外的一个RT中,“_CameraDepthTexture”。不同于DepthOnlyPass.cs只进行一次采样,这里会根据MSAA渲染设置,进行多重采样。

CreateLightweightRenderTexturesPass.cs

创建轻量渲染纹理。根据渲染配置是否需要额外的颜色缓冲或者深度缓冲,决定是否创建新的渲染纹理。

DepthOnlyPass.cs

仅渲染深度。深度信息提前处理,把深度信息填入单独的RT中,shader引用名称为“_CameraDepthTexture”。后面渲染非透明物体时的深度信息填入”_CameraDepthAttachment”或者最终的相机的深度缓存中。这里对深度缓冲不进行MSAA,只执行一次标准的采样。调用物体Shader里的LightMode:“DepthOnly”进行渲染。

这里指定的排序标记是SortFlags.CommonOpaque,这将告诉Unity按照从近到远的顺序来排序这些可见的渲染物体(Renderers),从而起到减少重复绘制(OverDraw)的作用。在默认的最简渲染流程下,这个Pass是不执行的,而当诸如全屏后处理等特性被启用时,这个Pass会被加入渲染流程中,导致DrawCall倍增。

DirectionalShadowsPass.cs

渲染方向光产生的级联阴影,仅支持一个主光源产生阴影,其它光源根本不会被处理。点光源和区域光源不会产生任何阴影。生成平行光的阴影贴图,把shadowmap保存到“_DirectionalShadowmapTexture”里。调用物体Shader里的LightMode:“ShadowCaster”进行渲染。

DrawSkyboxPass.cs

绘制天空盒。轻量管线中天空盒并不像通常认为的在所有物体之前渲染,而是在非透明物体之后才渲染,目的应该是为了可以使用提前的深度测试降低overdraw。

EndXRRenderingPass.cs

结束XX现实多眼睛渲染。

FinalBlitPass.cs

使用Blit shader执行一次渲染纹理拷贝。在渲染流程的最后如果没有开启透明物体的后处理并且当前的颜色缓冲对象不是管线最终的颜色缓冲区,则执行拷贝把当前颜色缓冲复制到管线最终输出的颜色缓冲区。

ForwardLitPass.cs

空文件,没有代码。

LightweightForwardPass.cs

轻量的前向渲染,重定义了出错情况下的显示用shader,但是依然是用的内建shader。

RenderObjectsWithError,如果检测到Shader使用了Unity旧有内置管线中的LightMode,包括Always、ForwardBase、PrepassBase、Vertex、VertexMRGBM、VertexLM都会用默认的ErrorMaterial(紫色)进行显示,表示这些内置管线用的LightMode在轻量管线中是不支持的,不可以使用。如果指定了轻量管线专用的LightMode,但Shader编译错误,也会用默认的ErrorMaterial(紫色)进行显示。

errorSettings.SetShaderPassName(i, m_LegacyShaderPassNames[i]);//标识当前渲染使用的LightMode的名称。

errorSettings.SetOverrideMaterial(m_ErrorMaterial, 0);//标识使用改材质的第一个pass覆盖掉当前渲染设置指定的pass。

如果Pass指定了不能识别的LightMode,则该物体不会被渲染,也没有错误信息,遇到有模型没渲出来,可能就是LightMode指定错了。

如果Pass未指定LightMode,Unity会自动将其设置为SRPDefaultUnlit,由于CSharp脚本中注册了SRPDefaultUnlit这种LightMode,所以这个Pass也会被渲染出来。如果手动注释掉对SRPDefaultUnlit注册的代码,那么这个Pass就不会被渲染。

LocalShadowsPass.cs

渲染聚光灯产生的阴影,光源类型仅支持聚光灯,光源数量未有限制。点光源和区域光源不会产生任何阴影。

非烘焙(Realtime/Mixed)聚光灯光源数目目前限制为4个。这里的设置不合理,既然是处理阴影,应该是设置可以投射阴影的聚光灯光源数目不超过某个限制,而不是直接限制所有光源数目。推测应该是写错了,实际应该用产生阴影的灯光数量进行断言。调用物体shader里的LightMode:”ShadowCaster”进行渲染。

实测,8个聚光灯,其中4个产生阴影,4个不产生阴影,修改Assert后能够正常工作。

对第152行进行如上图所示的修改,工作正常。从代码逻辑的角度看,这个写法也是不合理的,在执行0~ localLightsCount的循环过程中,去断言循环次数上限不大于4,怎么看都是不正确的逻辑。

OpaquePostProcessPass.cs

不透明的后处理,由于渲染步骤的关系,此时仅不透明物体被渲染完毕,故此时所做的后处理仅对不透明物体有效。

RenderOpaqueForwardPass.cs

前向渲染不透明物体,继承自LightweightForwardPass,如果没有在这个Pass中正确执行渲染操作,就不会显示任何不透明物体。

这里指定的排序标记是SortFlags.CommonOpaque,这将告诉Unity按照从近到远的顺序来排序这些可见的渲染物体(Renderers),从而起到减少重复绘制(OverDraw)的作用。

RenderTransparentForwardPass.cs

前向渲染半透明物体,继承自LightweightForwardPass,如果没有在这个Pass中正确执行渲染操作,就不会显示任何半透明物体。

这里指定的排序标记是SortFlags.CommonTransparent,这将告诉Unity按照从远到近(画家算法)的顺序来排序这些可见的渲染物体(Renderers),从而正确混合半透明物体和其后的物体的颜色。

SceneViewDepthCopyPass.cs

编辑器模式下Scene视图的深度拷贝。

ScreenSpaceShadowResolvePass.cs

屏幕空间的阴影处理。

ScriptableRenderPass.cs

所有渲染步骤的基类,定义了Execute接口,提供了基础的设置功能和设置渲染纹理的功能。为派生类提供了注册使用的渲染通道的功能RegisterShaderPassName,用来指定使用shader里的哪个pass进行渲染。

SetupForwardRenderingPass.cs

设置前向渲染的基本摄像机参数,后续Shader用到的各种变换矩阵等一些通用参数。

SetupLightweightConstanstPass.cs(这个Constanst不是我拼错的,源码里就是这样)

设置灯光相关的基本参数,后续Shader中将会用到的一些通用参数。

TransparentPostProcessPass.cs

透明的后处理,此时渲染纹理中已经包含了渲染完的不透明物体及后处理,再加上透明物体。

默认的执行顺序(在不同情况下生效的步骤不一样):

  1. DirectionalShadowPass(方向光主光源)
  2. LocalShadowsPass(视域内所有的聚光灯)
  3. SetupForwardRenderingPass
  4. DepthOnlyPass
  5. ScreenSpaceShadowResolvePass
  6. CreateLightweightRenderTexturesPass
  7. SetupLightweightConstanstPass
  8. RenderOpaqueForwardPass
  9. DrawSkyboxPass
  10. RenderTransparentForwardPass
  11. FinalBlitPass
  12. SceneViewDepthCopyPass

DefaultRendererSetup.cs

默认渲染器的初始化设置,包含了所有渲染步骤的初始化(即便是暂时没有用到的)。实现了Setup接口。

Init:创建渲染步骤,初始化渲染纹理。

Setup:调用了Init,初始化渲染器和渲染步骤。

IRendererSetup.cs

渲染器初始化的基接口,定义了Setup接口。

Setup:接口。

LightweightAdditionalCameraData.cs

轻量管线用额外摄像机数据。

renderShadows:是否启用阴影。

requiresDepthTexture:是否启用深度纹理,会影响到是否激活DepthOnlyPass和CopyDepthPass。

requiresColorTexture:是否启用色彩纹理,会影响到是否激活CopyColorPass。

version:版本号,没有地方用到。

LightweightConstantBuffer.cs

空文件,没有任何代码。

LightweightForwardRenderer.cs

轻量的前向渲染器,受到LightweightPipeline管理和调用,负责管理激活的渲染步骤并执行这些步骤,以及提供了一些渲染器层面的参数设置和获取。

这里预创建了两个过滤设置,RenderQueueRange.opaque和RenderQueueRange.transparent,分别用于不透明物体(0~2500)和半透明物体(2501~5000)的过滤。这将告知Unity在渲染的时候,只会渲染渲染队列编号(RenderQueue)在范围内的物体,从而实现只渲染不透明物体和只渲染半透明物体。

在两者之间可以渲染天空盒,减少对天空盒被遮挡部分的着色,减少重复绘制(OverDraw)。

Dispose:销毁逐对象光照索引和材质。

CreateRTDesc:创建渲染纹理描述信息

Execute:执行激活的渲染步骤,然后销毁这些渲染步骤。

GetMaterial:通过枚举获取材质实例。

Clear:清空激活的渲染步骤列表。

EnqueuePass:在末尾增加一个渲染步骤。

RequiresIntermediateColorTexture:判断是否需要额外渲染纹理用于渲染,或者直接渲染到摄像机的目标纹理。

CanCopyDepth:判断是否可以拷贝深度(在开启全屏抗锯齿等情况下是不能拷贝深度的)。

DisposePasses:销毁渲染步骤。

SetupPerObjectLightIndices:设置逐对象的光源索引,把平行光从索引中剔除,因为进行了全局处理。

GetCameraClearFlag:基于摄像机的清除标志重新计算清除标志掩码,仅对深度和颜色有效,不清除stencil。

GetRendererConfiguration:获取渲染器的功能开关设置掩码,包含逐对象反射探针、逐对象光照贴图、逐对象光照探针、以及针对非平行光源处理光照索引。

LightweightPipeline.cs

轻量渲染管线,继承自RenderPipeline, 由LightweightPipelineAsset创建和调用,本文件仅包含该类部分代码。

Dispose:销毁渲染器。

Render:遍历所有摄像机,逐相机进行渲染。

SetSupportedRenderingFeatures:设置支持的渲染特性,禁用反射探针旋转功能、指定默认烘焙模式为Subtractive、烘焙模式仅支持Subtractive、烘焙光源仅支持Baked和Mixed类型、烘焙的方向模式有向、无向两种都支持、禁用光照探针代理体积、禁用速度矢量(所以运动模糊用不了)、启用阴影接收、启用反射探针。

InitializeCameraData:基于管线指定的相关参数(深度、粒子、不透明纹理及降采样等)初始化相机数据,并设置相机额外数据。

InitializeRenderingData:初始化渲染数据,设置相机并调用光源和阴影的初始化,根据管线参数设置是否启用动态合批。

InitializeShadowData:根据管线参数覆写阴影相关的参数。

InitializeLightData:根据管线参数覆写光源相关的数据。

GetMainLight:从可见光源列表中获得主光源(最亮的平行光)。

SetupPerFrameShaderConstants:设置逐帧的Shader常量(相机无关的全局参数)。

SetupPerCameraShaderConstants:设置逐相机的Shader常量(相机有关的参数)。

LightweightPipelineCore.cs

LightweightPipeline类的另一部分代码,共同向下维护渲染器和向上提供接口。

fullscreenMesh:全屏大小的网格。

GetPipelineCapabilities:获取静态的PipelineCapabilities枚举值,用于ShaderPreprocessor中对Shader的裁剪。

SetPipelineCapabilities:根据管线参数设置Shader变体的开/关。

DrawFullScreen:对全屏网格执行绘制。

GetLightCookieMatrix:获取世界坐标系到主光源的本地坐标系的变换矩阵,用于传递给Shader的_WorldToLight变量。

CopyTexture:执行渲染纹理的拷贝(blit)。

IsSupportedShadowType:判断光源类型是否为平行光或聚光灯,仅此两种光源支持阴影。

IsSupportedCookieType:判断光源类型是否为平行光或聚光灯,仅此两种光源支持信息记录(Cookie)。

IsStereoEnabled:判断是否为XR模式下启用双眼的Scene视图。

RenderPostProcess:渲染后处理效果。

LightweightShaderUtils.cs

提供Shader相关的杂项功能。

m_ShaderPaths:管线预制的Shader名称列表。

GetShaderPath:根据枚举获取管线预制的Shader名称。

LightweightShadowUtils.cs

提供阴影相关的杂项功能。

ExtractDirectionalLightMatrix:计算主光源的视口矩阵和透视矩阵,用于处理它产生的阴影。

ExtractSpotLightMatrix:计算聚光灯的视口矩阵和透视矩阵,用于处理它产生的阴影。

RenderShadowSlice:基于给定的视口、透视矩阵渲染阴影切片(级联阴影产生的多级切片)。

GetMaxTileResolutionInAtlas:计算阴影贴图的分辨率。

ApplySliceTransform:计算级联阴影的切片变换矩阵。

SetupShadowCasterConstants:设置产生阴影的光源的Shader参数(_ShadowBias和_LightDirection)。

GetShadowTransform:基于视口、透视矩阵,计算阴影变换矩阵。

MaterialHandles.cs

就一个枚举。

Error:Shader编译出错时默认使用的粉紫色Shader。

DepthCopy:深度拷贝用Shader。

Sampling:采样Shader。

Blit:渲染纹理拷贝Shader。

ScrenSpaceShadow:屏幕空间阴影Shader。(Scren不是我拼错的,源码里就这样)

Count:枚举总数。

RenderTargetHandle.cs

一个句柄及相关运算符重载。

Init:基于Shader属性的名字生成ID。

Identifier:基于ID创建识别对象。

Equals:判定ID是否相等。

GetHashCode:返回ID。

==:判定ID是否相等。

!=:判定ID是否不等。

RenderTargetHandles.cs

空文件,没有任何代码。

SampleCount.cs

就一个枚举。

One = 1:单次采样。

Two = 2:双重采样。

Four = 4:四重采样。

SceneViewDrawMode.cs

重定义Scene视图支持的绘制模式。

RejectDrawMode:在Scene视图中屏蔽掉那些不支持的绘制模式(TexturedWire、ShadowCascades、RenderPaths、AlphaChannel、Overdraw、Mipmaps、SpriteMask、DeferredDiffuse、DeferredSpecular、DeferredSmoothness、DeferredNormal、ValidateAlbedo、ValidateMetalSpecular、ShadowMasks、LightOverlap)

SetupDrawMode:启用管线重定义绘制模式。

ResetDrawMode:禁用管线重定义绘制模式。

Editor

lightweightPBRExtraPasses.template

ShaderGraph用轻量PBR的模板文件,定义了ShadowCaster、DepthOnly、Meta这几个Pass。(和非ShaderGraph用的Standard还是有较大区别的)

lightweightPBRForwardPass.template

ShaderGraph用轻量PBR的模板文件,定义了LightweightForward这个Pass。(和非ShaderGraph用的Standard还是有较大区别的)

LightWeightPBRSubShader.cs

继承自IPBRSubShader,负责生成ShaderGraph的PBRMasterNode的源码。

m_ForwardPassMetallic:定义基于金属度的PBR参数列表:Albedo、Normal、Emission、Metallic、Smoothness、Occlusion、Alpha、AlphaThreshold。

m_ForwardPassSpecular:定义基于高光度的PBR参数列表:Albedo、Normal、Emission、Specular、Smoothness、Occlusion、Alpha、AlphaThreshold。

GetSubshader:根据IMasterNode中设置的参数,替换掉模板文件中的可替换部分代码,生成最终使用的SubShader源码字符串。

IsPipelineCompatible:检查当前设置的管线资源文件是否为轻量管线资源。

GetTemplatePath:从LightweightIncludePaths中指定的包路径中寻找LWRP/Editor/ShaderGraph目录中的指定模板文件,传入参数指定了模板文件的名字。

GetShaderPassFromTemplate:根据提供的参数替换主Pass模板中的可替换部分代码,生成主Pass源码。

GetExtraPassesFromTemplate:根据提供的参数替换额外Pass模板中的可替换部分代码,生成额外Pass源码。

lightweightSubshaderFastBlinn.template

ShaderGraph用FastBlinn这个SubShader的模板文件,定义了LightweightForward、ShadowCaster、DepthOnly、Meta这几个Pass(轻量管线支持的所有pass)。

lightweightUnlitExtraPasses.template

ShaderGraph用无光照Shader模板文件,定义了ShadowCaster、DepthOnly这几个Pass。

lightweightUnlitPass.template

ShaderGraph用无光照Shader模板文件,定义了LightweightForward这个Pass。

LightWeightUnlitSubShader.cs

继承自IUnlitSubShader,负责生成ShaderGraph的UnlitMasterNode的源码。

m_UnlitPass: 定义参数列表:Color、Alpha、AlphaThreshold。

GetSubshader: 根据IMasterNode中设置的参数,替换掉模板文件中的可替换部分代码,生成最终使用的SubShader源码字符串。

IsPipelineCompatible: 检查当前设置的管线资源文件是否为轻量管线资源。

GetTemplatePath:从指定的包路径中寻找LWRP/Editor/ShaderGraph目录中的指定模板文件,传入参数指定了模板文件的名字。(同一个包同一个目录下的两个类的相同用途、名称的函数(LightWeightPBRSubShader和LightWeightUnlitSubShader),实现居然还不一样。。。Unity的开发团队在管理上还是不够严谨。)

GetShaderPassFromTemplate:根据提供的参数替换主Pass模板中的可替换部分代码,生成主Pass源码。

GetExtraPassesFromTemplate:根据提供的参数替换额外Pass模板中的可替换部分代码,生成额外Pass源码。

LightweightShaderGUI.cs

继承自ShaderGUI,抽象基类。

MaterialChanged:材质发生变更,纯虚函数,需要实现。

FindProperties:获取需要用到的材质属性实例。本类仅处理_Surface、_Blend、_Cull、_AlphaClip、_Cutoff这几个属性。

ShaderPropertiesGUI:材质属性的界面显示。本类仅显示_Surface、_Blend、_Cull、_AlphaClip、_Cutoff这几个属性。

OnGUI:界面显示,分别调用FindProperties,MaterialChanged(仅首次),ShaderPropertiesGUI。

SetupMaterialBlendMode:设置材质的混合模式,根据_AlphaClip的值去指定_ALPHATEST_ON关键字是否启用,根据_Surface和_Blend的值去指定RenderType、_SrcBlend、_DstBlend、_ZWrite、renderQueue等属性以及_ALPHAPREMULTIPLY_ON关键字是否启用,并决定是否启用ShadowCaster这个Pass。

DoPopup:呼出一个下拉框风格的界面(子函数封装)。

DoMaterialRenderingOptions:显示材质面板最下方的RenderingOptions:

LightweightStandardGUI.cs

继承自LightweightShaderGUI,标准PBR Shader的设置界面逻辑。

FindProperties:获取需要用到的材质属性实例。本类仅处理_WorkflowMode、_Color、_MainTex、_Glossiness、_GlossMapScale、_SmoothnessTextureChannel、_Metallic、_SpecColor、_MetallicGlossMap、_SpecGlossMap、_SpecularHighlights、_GlossyReflections、_BumpScale、_BumpMap、_OcclusionStrength、_OcclusionMap、_EmissionColor、_EmissionMap这几个属性。

MaterialChanged:材质发生变更,原材质keywords置空,调用SetupMaterialBlendMode(基类中)和SetMaterialKeywords。

ShaderPropertiesGUI:材质属性的界面显示。本类仅显示_WorkflowMode然后调用基类、以及_Color、_MainTex、_Glossiness、_GlossMapScale、_SmoothnessTextureChannel、_Metallic、_SpecColor、_MetallicGlossMap、_SpecGlossMap、_SpecularHighlights、_GlossyReflections、_BumpScale、_BumpMap、_OcclusionStrength、_OcclusionMap、_EmissionColor、_EmissionMap这几个属性。

AssignNewShaderToMaterial:更换Shader时的处理,如果新的Shader包含能够识别的属性,则把值传递过去。能够识别的属性有_Emission、_AlphaClip 、_Surface、_Blend、_WorkflowMode、_SpecGlossMap、_MetallicGlossMap等。

DoAlbedoArea:显示Albedo界面。

DoNormalArea:显示Normal界面。

DoEmissionArea:显示Emission界面。

DoMetallicSpecularArea:显示Metallic或者Specular界面,取决于选择的Workflow Mode。

GetSmoothnessMapChannel:根据材质中_SmoothnessTextureChannel参数的值,返回AlbedoAlpha或者SpecularMetallicAlpha。

SetMaterialKeywords:根据界面操作后的值,设置_SPECULAR_SETUP、_METALLICSPECGLOSSMAP、_SPECGLOSSMAP、_METALLICGLOSSMAP、_NORMALMAP、_SPECULARHIGHLIGHTS_OFF、_GLOSSYREFLECTIONS_OFF、_OCCLUSIONMAP、_PARALLAXMAP、_DETAIL_MULX2、_EMISSION、_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A等关键字是否开启。

LightweightStandardParticlesShaderGUI.cs

继承自ShaderGUI,标准粒子Shader的设置界面逻辑。

FindProperties:获取需要用到的材质属性实例。本类仅处理_Mode、_ColorMode、_FlipbookMode、_Cull、_MainTex、_Color、_Cutoff、_MetallicGlossMap、_Metallic、_Glossiness、_BumpScale、_BumpMap、_EmissionEnabled、_EmissionColor、_EmissionMap、_SoftParticlesEnabled、_CameraFadingEnabled、_SoftParticlesNearFadeDistance、_SoftParticlesFarFadeDistance、_CameraNearFadeDistance、_CameraFarFadeDistance这几个属性。

OnGUI:为正确显示属性动画,通过FindProperties逐事件获取属性实例,通过调用ShaderPropertiesGUI完成Shader界面的绘制,并在首次启用时通过调用MaterialChanged和CacheRenderersUsingThisMaterial完成初始化操作。

ShaderPropertiesGUI:通过调用模块化的绘制函数,绘制本Shader所有的界面元素,由于enlighten(相比progressive更亮一些,无论亮部还是暗部)的缘故,在非翻书模式下还要把albedo纹理的缩放和偏移值赋给emission纹理。

AssignNewShaderToMaterial:更换Shader时的处理,如果新的Shader包含能够识别的属性,则把值传递过去。能够识别的属性有Unlit、_Emission等,针对Shader名称包含Legacy Shaders/、/Transparent/Cutout/、/Transparent/的情况也进行了处理。

ColorModePopup:选择色彩模式的下拉框。

FlipbookModePopup:选择翻书模式的下拉框,从源码上看,Blend模式仅仅是定义了_REQUIRE_UV2这个关键字,并且给Mesh指定了UV2和AnimBlend,用两套UV对同一纹理采样后混合。但是没有找到实际应用和把关键字关联起来的地方,可能是因为这个功能还未开发完毕。另外,这个功能的使用似乎需要在制作Mesh的时候提供符合要求的UV数据才行,但是并不知道这些数据是怎么做的,以及这个功能对于美术到底有何用途,顾名思义,似乎是用来制作翻书的效果,但这是粒子系统的功能么?

TwoSidedPopup:双面与否的勾选,勾上则为CullMode.Off,不勾用CullMode.Back。

FadingPopup:淡入参数设置,勾选后弹出近、远距离的输入框。

由近及远,出现淡入效果,越近的地方越虚,越远的地方越实。Fade因子由小变大。

DoAlbedoArea:显示反照率界面。

DoEmissionArea:显示自发光界面。

DoSpecularMetallicArea:显示金属度和光滑度界面。

DoNormalMapArea:显示法线贴图界面。

DoVertexStreamsArea:显示顶点流信息并填充实质顶点流信息列表。

SetupMaterialWithBlendMode:根据选定的混合模式,设置材质的相关属性和关键字。

ColorOverlay

ColorColor

ColorAddSubDiff

Vector4(CASD)

Multiply

0

0

0

/

Additive

0

0

1

/

Subtractive

0

0

1

/

Overlay

0

0

1

-1,1,0,0

Color

0

0

1

1,0,0,0

Difference

0

0

1

-1,0,0,0

SetMaterialKeywords:设置其它的一些材质参数和关键字,包括_NORMALMAP、_METALLICGLOSSMAP、_EMISSION、_REQUIRE_UV2、_FADING_ON、_SoftParticlesNearFadeDistance、_SoftParticlesFarFadeDistance、_CameraNearFadeDistance、_CameraFarFadeDistance、_SoftParticleFadeParams、_CameraFadeParams、Always,这里是根据材质面板上实际贴图是否存在及默认参数是否变更来决定材质中关键字特性宏的开关,以起到Shader变体的自动生成和裁剪的作用。具体怎样打AB包能够实现正确生成最少变体及不丢失Shader,还有待进一步研究确认。

MaterialChanged:材质发生变更,原材质keywords置空,调用SetupMaterialBlendMode(基类中)、SetupMaterialWithColorMode和SetMaterialKeywords。

CacheRenderersUsingThisMaterial:缓存当前内存中所有使用了本材质的Renderer实例。首次生效时执行,以加速后续的遍历。

SetKeyword:对材质开、关关键字特性的便利方法。

LightweightStandardSimpleLightingGUI.cs

继承自LightweightShaderGUI,简单光照Shader的设置界面逻辑

FindProperties:获取需要用到的材质属性实例。本类仅处理_MainTex、_Color、_SpecSource、_GlossinessSource、_SpecGlossMap、_SpecColor、_Shininess、_BumpMap、_EmissionMap、_EmissionColor这几个属性。

ShaderPropertiesGUI:通过调用模块化的绘制函数,绘制本Shader所有的界面元素,由于enlighten(相比progressive更亮一些,无论亮部还是暗部)的缘故,还要把albedo纹理的缩放和偏移值赋给emission纹理。

MaterialChanged:材质发生变更,原材质keywords置空,调用SetupMaterialBlendMode(基类中)和SetMaterialKeywords。

SetMaterialKeywords:这个函数是从MaterialChanged调过来的,但是重复执行了一样的代码,应该是写错了。

并且全局也只有这么一处调用:

通过调用UpdateMaterialSpecularSource设置其它的一些材质参数和关键字,包括_SPECGLOSSMAP、_SPECULAR_COLOR、_GLOSSINESS_FROM_BASE_ALPHA以及_NORMALMAP、_EMISSION关键字,这里是根据材质面板上实际贴图是否存在及默认参数是否变更来决定材质中关键字特性宏的开关,以起到Shader变体的自动生成和裁剪的作用。具体怎样打AB包能够实现正确生成最少变体及不丢失Shader,还有待进一步研究确认。

UpdateMaterialSpecularSource:根据面板的参数设置_SPECGLOSSMAP、_SPECULAR_COLOR、_GLOSSINESS_FROM_BASE_ALPHA关键字特性的开、关。

AssignNewShaderToMaterial:更换Shader时的处理,如果新的Shader包含能够识别的属性,则把值传递过去。能够识别的属性有_Shininess、Transp 、Cutout、Spec、_Surface、_Blend、_SpecSource、_GlossinessSource、Self-Illumin等。

RequiresAlpha:判断这个Shader设置了_AlphaClip非0或者_Surface被指定为trasparent类型。

DoSurfaceArea:显示Surface属性第一行界面信息。

DoSpecular:显示高光界面区域。

注意,如果上面勾选了Alpha Clip或者选定了Transparent类型的Surface,会导致下面的GlossinessSource置灰,无法选择。本函数负责绘制Specular勾选框及其下面的几行界面,如果未勾选Specular,则下面几行也不绘制。

DoEmissionArea:显示Emission界面区域。

ConvertFromLegacy:对Shader名称包含Transp 、Cutout、Spec,或包含_Surface、_Blend、_SpecSource、_GlossinessSource、Self-Illumin等属性的,将相应属性拷贝到新的材质中。

LightweightUnlitGUI.cs

FindProperties:获取需要用到的材质属性实例。本类仅处理_MainTex、_Color、_SampleGI、_BumpMap这几个属性。

ShaderPropertiesGUI:绘制本Shader所有的界面元素。勾选Sample GI会额外提供Normal Map设置,别的也没啥花头了。

MaterialChanged:材质发生变更,原材质keywords置空,调用SetupMaterialBlendMode(基类中)和SetMaterialKeywords。

SetMaterialKeywords:设置其它的一些材质参数和关键字,包括_SAMPLE_GI、_NORMAL_MAP关键字,这里是根据材质面板上实际贴图是否存在及默认参数是否变更来决定材质中关键字特性宏的开关,以起到Shader变体的自动生成和裁剪的作用。具体怎样打AB包能够实现正确生成最少变体及不丢失Shader,还有待进一步研究确认。

LightweightCameraEditor.cs

管线特化的摄像机Inspector面板。

SetAnimationTarget:设置AnimBool的便捷方法,当值发生变化的时候进行界面重绘。

UpdateAnimationValues:对ClearFlag、Ortho、TargetEye三个动画值监控其变化。

OnEnable:缓存管线资产,调用Settings类的同名方法,更新UpdateAnimationValues为true。

OnDisable:移除对ClearFlag、Ortho、TargetEye三个动画值的监控,缓存的管线资产置空。

OnInspectorGUI:相机参数的界面绘制,调用了DrawRenderingPath、DrawHDR、DrawTargetTexture、DrawMSAA这几个方法。

DrawRenderingPath:绘制渲染路径界面,但是不给设置。

DrawHDR:绘制HDR界面,但是轻量管线默认是禁用HDR的,可以手动开启。

DrawTargetTexture:绘制渲染纹理界面,如果渲染纹理要求的多重采样抗锯齿倍率高于轻量管线设置的允许倍率,会出现Fix now按钮,点击会用渲染纹理要求的倍率覆盖管线的设置值。

DrawMSAA:绘制多重采样抗锯齿界面,如果管线设置中关闭了抗锯齿支持,会出现Fix now按钮,点击后把管线的抗锯齿倍率值设置为4倍。

LightweightLightEditor.cs

管线特化的灯光Inspector面板。

OnEnable:调用Settings类的同名方法,调用UpdateShowOptions,传入true。

OnInspectorGUI:调用Settings类的Update方法,调用UpdateShowOptions,传入false。调用Settings类的DrawLightType,在两种光源类型间切换时,变化的参数播放滚动的效果。

SetOptions:设置AnimBool的便捷方法,当值发生变化的时候进行界面重绘。

UpdateShowOptions:对Spot、Point、Dir、Area、Shadow、Runtime、ShadowAngle、ShadowRadius、LightBounceIntensity九个动画值监控其变化(不同的光源类型用到不同的值)。

DrawSpotAngle:绘制聚光灯的角度参数。

DrawCookie:绘制Cookie,调用的地方被注释掉了,Tim写着在v1版本中禁用cookie以节省shader组合。

DrawCookieSize:绘制Cookie的Size,调用的地方被注释掉了,Tim写着在v1版本中禁用cookie以节省shader组合。

ShadowsGUI:绘制阴影参数,不同的光源类型,阴影参数也不一样。

LightweightMaterialUpgrader.cs

调用Core库中的MaterialUpgrader,完成材质转换。

UpgradeProjectMaterials:将管线自定义的转换类传入MaterialUpgrader类的UpgradeProjectFolder方法对整个工程进行转换。

UpgradeSelectedMaterials:将管线自定义的转换类传入MaterialUpgrader类的UpgradeSelection方法对选中的资源进行转换。

GetUpgraders:创建管线要用到的所有转换类的实例,供Core库转换时进行回调。

SupportedUpgradeParams:定义了轻量管线支持的参数类型。

StandardSimpleLightingUpgrader:简单光照材质的转换类,用于将BuiltIn的材质转换成轻量管线的格式,除了重命名材质的Shader外,主要是处理_Surface、_Blend、_AlphaClip、_SpecSource、_GlossinessSource、_EmissionColor这六个参数和_NORMALMAP、_EMISSION、_SPECGLOSSMAP、_SPECULAR_COLOR、_GLOSSINESS_FROM_BASE_ALPHA这五个关键字以及把_Illum纹理重命名为_EmissionMap。

TerrainUpgrader:重命名地形材质的Shader。

ParticleUpgrader :重命名粒子材质的Shader。

LightweightPipelineAssetEditor.cs

管线资产文件的Inspector面板。

OnInspectorGUI:绘制Inspector面板的界面,调用DrawCapabilitiesSettings和DrawGeneralSettings,并绘制XR区面板。

OnEnable:缓存属性值,添加SoftParticle和OpaqueTextureScale值变化的重绘回调。

OnDisable:移除SoftParticle和OpaqueTextureScale值变化的重绘回调。

UpdateAnimationValues:更新SoftParticle和OpaqueTextureScale的当前值。

DrawGeneralSettings:绘制基础区面板。

DrawCapabilitiesSettings:绘制容量区面板。

LightweghtShaderIncludePaths.cs

GetPaths:获取查找管线资源的根目录,默认是指向通过PackageManager下载的包在电脑上默认保存的地方,如果把包拷贝到Assets目录下使用,需要修改这个路径指向Assets目录下的管线包根目录。

ShaderPreprocessor.cs

StripUnusedShader:裁剪掉名字包含Debug、HDRenderPipeline字样的Shader,如果管线禁用了方向阴影(DirectionalShadows),那么也裁剪掉名字包含ScreenSpaceShadows的Shader。

StripUnusedPass:裁剪掉类型为Meta的Pass,因为是烘焙时用的,如果管线禁用了阴影(方向阴影和局部阴影,详见DirectionalShadowPass和LocalShadowPass),那么也裁剪掉类型为ShadowCaster的Pass。

StripUnusedVariant:裁剪掉和管线设置不相容(在管线设置中禁用)的变体,包括额外光源(AdditionalLights)、顶点光(VertexLights)、方形阴影(DirectionalShadows)、局部阴影(LocalShadows)、软阴影(SoftShadows)。

StripInvalidVariants:裁剪掉非法的变体,包括启用级联阴影但是禁用方向阴影、启用软阴影但是禁用方向阴影和局部阴影、启用顶点光但是禁用额外光源、启用方向光照贴图但是禁用光照贴图这几种情况,另外,轻量管线不支持动态光照贴图。

StripUnused:通过调用StripUnusedShader、StripUnusedPass、StripUnusedVariant、StripInvalidVariants进行Shader裁剪。

LogVariants:把轻量管线的变体打印出来(调试用),默认关闭,需要开启LOG_VARIANTS宏才可以使用。

OnProcessShader:Shader编译过程中的回调,通过调用StripUnused进行Shader裁剪。

UpgradeCommon.cs

UpgradeSurfaceType:表面类型枚举,Opaque、Transparent。

UpgradeBlendMode:混合模式枚举,Alpha、Premultiply、Additive、Multiply。

SpecularSource:高光来源枚举,SpecularTextureAndColor、NoSpecular。

GlossinessSource:光泽度来源枚举,BaseAlpha、SpecularAlpha。

ReflectionSource:反射来源枚举,NoReflection、Cubemap、ReflectionProbe。

UpgradeParams:包含上述枚举的结构体。

Shader

InputSurfaceGrass.hlsl

FastSinCos:使用泰勒级数一次性计算4个0-1区间值的正弦和余弦近似值,正弦取前4项,余弦取前5项。

TerrainWaveGrass:在顶点层计算草被吹动的动画效果,并根据波动融合顶点色和GrassTint颜色。这里可能是实现三个效果:

  1. 草的物理波动,顶点位移,这里实现的顶点位移,不同位置的草的位移有细微的不同的表现,不会有所有草很规律的波动问题。
  2. 草的颜色波动,波浪颜色(波浪起伏有明暗变化)和草顶点色融合。
  3. 草的alpha值由近及远变为0,和距离的平方成正比,非线性。

TerrainBillboardGrass:计算草顶点的位置,超出距离的草不做偏移处理。

InputSurfaceTerrain.hlsl

定义了地形使用的4组纹理(单次渲染最多使用4组纹理)及其所需的变量,并引用了其它库文件。这里有四组表面纹理和法线的贴图、采样器、纹理缩放和偏移参数;一组控制贴图的对应参数。对应四组表面贴图的平滑度和反光度参数;一组高光贴图和它的采样器,没有纹理缩放和偏移参数。这里定义的高光贴图实际并没有找到使用的地方,可能是作者复制黏贴的时候忘删了。

InputSurfaceTerrainBase.hlsl

SampleMetallicSpecGloss:从金属度纹理采样高光光泽度信息,但使用反照率的alpha。但是后续InitializeStandardLitSurfaceData中的调用却又完全不是这么回事,莫名其妙。从当前的逻辑看,这个函数完全没有存在价值,反而混淆了逻辑,导致更不容易理解。

InitializeStandardLitSurfaceData:填充SurfaceData结构,从主纹理采样反照率(rgb)和光滑度(a)信息,调用SampleMetallicSpecGloss获取高光光泽度和alpha(但是这实际上完全是混淆,其实就是直接从金属度纹理获取金属度),然后从凹凸纹理获取切空间法线。透明度和遮蔽设置为1,高光设置为0。

LightweightPassDepthOnlyGrass.hlsl

仅计算草的深度信息,所以只处理了坐标和颜色(透明度裁剪)。

DepthOnlyVertex:根据草的波动计算顶点色,草的顶点色中Alpha通道用于控制波动强度,不参与其他计算,在一个距离范围之内离摄像机越远Alpha趋近0,用作后续的透明裁剪,根据顶点坐标进行裁剪。

DepthOnlyFragment:根据顶点色中的透明度(a),执行裁剪,用于输出深度。根据做过波浪计算之后的顶点颜色的透明度(根据设置不同有可能又乘过了贴图的透明度)进行裁剪,裁剪的部分不会写深度缓存。

LightweightPassLitGrass.hlsl

InitializeInputData:计算世界空间的法线,计算世界空间的视向量,填充阴影坐标和雾效顶点光信息,计算预烘焙的全局光照信息,输出片元信息的结构体。

InitializeVertData:填充GrassVertexOutput结构体,计算世界空间的顶点坐标,用32填充闪耀度,基于世界空间坐标计算裁剪,计算世界空间的视向量,根据是否启用法线贴图计算所需的法线、切线和副法线信息,根据宏开关的设置来决定是从法线贴图还是从球谐函数采样全局光照信息,计算顶点光和雾的信息,计算阴影坐标。

WavingGrassVert:计算草顶点信息,处理INSTANCE信息,处理STEREO,计算顶点颜色,调用InitializeVertData填充其它数据。

WavingGrassBillboardVert:计算公告板样式草顶点信息,处理INSTANCE信息,处理STEREO,计算顶点颜色,调用InitializeVertData填充其它数据。

LitPassFragmentGrass:处理INSTANCE信息,采样主纹理并与顶点色进行乘法计算,处理透明度裁剪,反光度填充为0.1,计算闪耀度,调用InitializeInputData处理输入的插值后片元数据,基于BlinnPhong计算光照,计算雾。

LightweightPassLitTerrain.hlsl

InitializeInputData:计算世界空间的法线,计算世界空间的视向量,填充阴影坐标和雾效顶点光信息,计算预烘焙的全局光照信息,输出片元信息的结构体。

SplatmapMix:基于地形图示执行反照率和法线的混合。

(地形图示用R、G、B、A四个通道分别表示四组纹理的分布)

(针对多组纹理混合的情境,R、G、B、A通道均可有值,表征其混合权重)

SplatmapFinalColor:在最后计算雾效。

(不计算雾效的地形)         (计算了红色雾的地形)

SplatmapVert:执行地形的顶点计算,计算世界空间顶点坐标,并计算裁剪坐标,变换地形图示的UV,计算光照贴图的UV,根据宏定义计算法线,计算雾效顶点光信息,计算阴影坐标。

SpatmapFragment(这个Spat是源码中就存在的拼写错误):基于地形图示的片元计算,调用了SplatmapMix

、InitializeInputData和SplatmapFinalColor,各材质的反照率、金属度和光滑度被根据权重融合并用于计算PBR。

Core.hlsl

OUTPUT_NORMAL:如果启用了_NORMALMAP,则调用OutputTangentToWorld把切线空间的纹理采样得到的法线转换成世界空间的法线;如未启用,则调用TransformObjectToWorldNormal把模型空间的法线转换成世界空间法线。这里OUTPUT_NORMAL是在顶点着色器中调用,把法线从模型空间转换到世界空间,区别是,如果材质中添加了凹凸贴图就会启用_NROMALMAP,这时在片元着色器里需要当前顶点的切线空间的转换矩阵来进行法线贴图的采样,因此在顶点着色器阶段需要输出世界空间下的(切线、副切线、法线)来构成切线空间的三个正交轴,从而获取切线空间到世界空间的转换矩阵。否则只需要输出顶点法线即可,用来减少插值寄存器的使用。

UNITY_Z_0_FAR_FROM_CLIPSPACE:根据各种平台特性和底层关键字定义,把深度转换为从近切面到远切面线性增大的值,出于性能考虑,对于负值的情况未做处理,因为距离比较近。这会导致OPENGL下靠近相机近裁剪面特别近的物体的深度信息会被修复为0,可能会有一些问题。主要和UNITY_REVERSED_Z、UNITY_UV_STARTS_AT_TOP关键字的定义有关,也和API版本(SHADER_API_OPENGL、SHADER_API_GLES、SHADER_API_GLES3)有关,关键字在Core库中定义,如下表所示。

D3D11

GL

GLES

GLES2

GLES3

Metal

PSSL

Switch

Vulkan

XBoxOne

UNITY_UV_STARTS_AT_TOP

1

0

/

0

0

1

1

1

1

1

UNITY_REVERSED_Z

1

0

/

0

0

1

1

1

1

1

可以看出,两个关键字的定义是完全同步的。

SCREENSPACE_TEXTURE:为兼容XR而做的定义。

SCREENSPACE_TEXTURE_FLOAT:为兼容XR而做的定义。

SCREENSPACE_TEXTURE_HALF:为兼容XR而做的定义。

AlphaDiscard:如果定义了_ALPHATEST_ON,则调用clip函数进行裁剪,小于cutoff – offset的alpha值会被裁剪。

UnpackNormal:如果定义了UNITY_NO_DXT5nm,调用UnpackNormalRGBNoScale,把压缩为0~1值域的法线还原到-1~1值域;否则,调用UnpackNormalmapRGorAG,针对DXT5nm和BC5格式将存于xy通道的法线信息转换到wy通道统一处理后放回xy通道,同样把压缩为0~1值域的法线还原到-1~1值域,然后基于xy计算z的值。这里传入了常量1执行乘法,注释里写着编译器会把它优化掉的,那么为什么还要传呢?

UnpackNormalScale:与UnpackNormal功能完全相同,额外提供了Scale参数供缩放法线值至-scale~scale值域。

OutputTangentToWorld:通过调用TransformObjectToWorldNormal(额外处理了非统一缩放的情况,x、y、z方向的缩放率不同)和TransformObjectToWorldDir把法线和切线变换到世界空间,并计算出世界空间下的副法线,基于unity_WorldTransformParams.w和vertexTangent.w来判断副法线是否需要取反。

FragmentNormalWS:在片元中使用,如果定义了SHADER_HINT_NICE_QUALITY,则对法线执行逐片元归一化,否则就什么都不做,以换取更好的性能,四点不共面的情况下会存在一些可忍受的错误,插值不正确,显示上可能会有一些小错误。由于法线在从顶点阶段传入片元阶段经过了插值计算,所以片元阶段获取的结果可能不是归一化过的法线,需要再次归一化,这里在使用低质量的配置的情况下不进行归一化处理,对某些计算会增大误差,但是节省了计算单元处理器的开销。尤其对相对在世界空间下的体积只有较少的三角面模型在光照处理时会误差增大。

FragmentViewDirWS:在片元中使用,如果定义了SHADER_HINT_NICE_QUALITY,则对视向量执行安全的(额外处理了0向量的情况以免出错)逐片元归一化,否则就什么都不做,以换取更好的性能,但是由于物体表面未必处处可导,插值不正确,显示上可能会有一些小错误。

VertexViewDirWS:在顶点中使用,如果没有定义SHADER_HINT_NICE_QUALITY,则对视向量执行安全的(额外处理了0向量的情况以免出错)逐顶点归一化,因为片元中不会进行归一化,否则就什么都不做,因为在片元中会对其进行逐像素归一化,没有必要重复做。

TangentToWorldNormal:把切线空间的法线值,基于给定的切空间坐标轴变换为世界空间法线值,并调用FragmentNormalWS进行归一化处理。

ComputeScreenPos:计算屏幕空间坐标,说是后续版本的SRP库中有个功能相同的函数,到时候这个函数就不用了,目前主要用于计算阴影。

ComputeFogFactor:调用UNITY_Z_0_FAR_FROM_CLIPSPACE获取从近切面到远切面的距离,

ApplyFogColor:如果FOG_LINEAR、FOG_EXP、FOG_EXP2都没有定义,就什么都不做,就是不启用雾。分别针对三种不同的雾计算不同的雾度因子,并以此因子为系数在原始颜色和雾颜色之间插值。

雾度因子

LINEAR

雾强度 * 深度

EXP

e(-雾强度 * 深度)

EXP2

e(-雾强度 * 深度)2

ApplyFog:调用ApplyFogColor,并传入unity_FogColor(在InputBuiltin.hlsl中定义,real4类型,仍为cpp层默认传入,CSharp层未开源这部分代码;real4宏在Core库中定义,移动平台下使用half4,否则为float4)。

TransformStereoScreenSpaceTex:XR时计算左右眼偏移,并根据叠加因子w对zw通道进行缩放。

UnityStereoTransformScreenSpaceTex:调用TransformStereoScreenSpaceTex,叠加因子w传入1。

CoreFunctions.hlsl

齐次裁剪空间(Homogeneous Clip Space),这省略了逐顶点的3次除法运算(x/w, y/w, z/w),提升了性能。下文(函数名)中HClip就是指齐次裁剪空间(Homogeneous Clip)。注释中存在拼写错误,齐次空间应为homogeneous space,而Unity的源码中漏了一个e,写的是homogenous space,这是内置管线就有的历史遗留问题了。在轻量管线中,w分量恒为1。至于为什么要有这个w分量,就要去调查OpenGL最早时候的设计目的了。齐次空间W分量的意义,是处理向量(方向、法线等,w=0)和矢量(顶点w=1)时可以使用统一的4x4变换矩阵。Unity的内部的实现,没有遵照这一方式,在处理方向时都把4x4矩阵做了裁剪成为3x3矩阵忽略了W分量,或者把W分量用来存储其他数据来节省插值寄存器的使用。

GetCameraPositionWS:返回_WorldSpaceCameraPos(在InputBuiltin.hlsl中定义,主要对XR情况下进行重定义,其它情况下就是在SetupForwardRenderingPass.cs中通过context.SetupCameraProperties接口传入的摄像机的世界坐标)。

GetWorldToViewMatrix:返回UNITY_MATRIX_V(在Input.hlsl中定义为unity_MatrixV,后者在InputBuiltin.hlsl中定义,主要对XR情况下进行重定义,其它情况下就是cpp层传入的世界空间到观察空间的转换矩阵,这部分代码未在CSharp层开源)。

GetObjectToWorldMatrix:返回UNITY_MATRIX_M(在Input.hlsl中定义为unity_ObjectToWorld,后者在InputBuiltin.hlsl中定义,就是cpp层传入的模型空间到世界空间的转换矩阵,这部分代码未在CSharp层开源)。

GetWorldToObjectMatrix:返回UNITY_MATRIX_I_M(在Input.hlsl中定义为unity_WorldToObject,后者在InputBuiltin.hlsl中定义,就是cpp层传入的世界空间到模型空间的转换矩阵,这部分代码未在CSharp层开源)。

GetWorldToHClipMatrix:返回UNITY_MATRIX_VP(在Input.hlsl中定义为unity_MatrixVP,后者在InputBuiltin.hlsl中定义,主要对XR情况下进行重定义,其它情况下就是cpp层传入的世界空间到裁剪空间的转换矩阵,这部分代码未在CSharp层开源)。

在冯乐乐的《Unity Shader入门精要》一书的4.8章节有罗列部分内置矩阵的数学意义,轻量管线中的完整矩阵定义如下表所示。

变量名

描述

UNITY_MATRIX_M

即unity_ObjectToWorld,当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间

UNITY_MATRIX_I_M

即unity_WorldToObject ,UNITY_MATRIX_M的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间

UNITY_MATRIX_V

当前的观察矩阵,用于将顶点/方向矢量从世界空间变换到观察空间

UNITY_MATRIX_I_V

UNITY_MATRIX_V的逆矩阵,用于将顶点/方向矢量从观察空间变换到世界空间

UNITY_MATRIX_P

当前的投影矩阵,用户将顶点/方向矢量从观察空间变换到齐次裁剪空间

UNITY_MATRIX_I_P

没有实现,不可使用

UNITY_MATRIX_VP

当前的观察投影矩阵,用于将顶点/方向矢量从世界空间变换到齐次裁剪空间

UNITY_MATRIX_I_VP

没有实现,不可使用

UNITY_MATRIX_MV

当前的模型观察矩阵,用于将顶点/方向矢量从模型空间变换到观察空间

UNITY_MATRIX_T_MV

UNITY_MATRIX_MV的转置矩阵

UNITY_MATRIX_IT_MV

UNITY_MATRIX_MV的逆转置矩阵,用于将法线从模型空间变换到观察空间,也可用于得到UNITY_MATRIX_MV的逆矩阵

UNITY_MATRIX_MVP

当前的模型观察投影矩阵,用于将顶点/方向矢量从模型空间变换到齐次裁剪空间

unity_ObjectToWorld

当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间

unity_WorldToObject

unity_ObjectToWorld的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间

GetOddNegativeScale:返回unity_WorldTransformParams的w通道(1或-1),表示法线贴图是否需要取反,比如MAX和MAYA的法线贴图就是反的。

TransformWorldToView:通过左乘UNITY_MATRIX_V矩阵,把顶点从世界空间变换到观察空间。

TransformObjectToWorld:通过左乘UNITY_MATRIX_M矩阵,把顶点从模型空间变换到世界空间。

TransformWorldToObject:通过左乘UNITY_MATRIX_I_M矩阵,把顶点从世界空间变换到模型空间。

TransformObjectToWorldDir:通过左乘UNITY_MATRIX_M矩阵,把方向矢量从模型空间变换到世界空间,做了一次归一化以支持统一缩放。

TransformWorldToObjectDir:通过左乘UNITY_MATRIX_I_M矩阵,把方向矢量从世界空间变换到模型空间,做了一次归一化以支持统一缩放

TransformObjectToWorldNormal:如果定义了UNITY_ASSUME_UNIFORM_SCALING,则通过调用TransformObjectToWorldDir进行简单转换(模型空间到世界空间),因为假定模型为统一缩放;否则,需要处理非统一缩放的情况,通过右乘UNITY_MATRIX_I_M矩阵并归一化实现。这里使用右乘的数学意义是,非统一缩放的情况下,直接左乘变换矩阵法线方向会被改动,所以需要使用的是变换矩阵中缩放矩阵的逆矩阵,进行左乘。又有左乘矩阵和右乘矩阵的转置矩阵是等价的,而旋转矩阵是正交矩阵,其逆矩阵等价于转置矩阵。所以这里使用变换

矩阵的逆矩阵右乘法线,来抵消非统一缩放产生的影响。详见冯乐乐的《Unity Shader入门精要》一书4.7章。

TransformObjectToHClip:由于Unity对UNITY_MATRIX_MVP的定义是UNITY_MATRIX_VP和UNITY_MATRIX_M相乘,而不是从cpp层直接传入一个MVP的矩阵,因此,使用UNITY_MATRIX_MVP意味着逐顶点的两个4x4矩阵乘法运算(64次乘法、48次加法),所以,这里使用UNITY_MATRIX_M左乘模型空间的顶点坐标,再用UNITY_MATRIX_VP左乘变换后的世界空间的顶点坐标,以减少运算次数(16次乘法、12次加法)。最终把顶点坐标从模型空间变换到齐次裁剪空间。在内置管线中,存在UNITY_USE_PREMULTIPLIED_MATRICES定义的情况下,会从cpp层直接传入MVP矩阵,而轻量管线中没有这个功能。

TransformWorldToHClip:比TransformObjectToHClip少了一步,直接从世界空间变换到齐次裁剪空间。

CreateWorldToTangent:通过提供的法线、切线和翻转标记生成一个世界空间到切线空间的变换矩阵的便捷函数。

TransformTangentToWorld:通过右乘世界空间到切线空间的变换矩阵完成逆变换,把切线空间的方向矢量变换到世界空间。同样这里因为世界空间到切线空间的变换矩阵是正交矩阵,其逆矩阵等于其转置矩阵,所以这里右乘世界空间到切线空间的逆矩阵,等价于左乘切线空间到世界空间的矩阵。而切线空间到世界空间的变换矩阵需要额外计算所以这里使用了右乘。只要不涉及模型空间的变换,都不会有缩放矩阵,而不规则缩放矩阵不是正交矩阵,只要没有缩放矩阵,其他系统矩阵是正交矩阵。详见冯乐乐的《Unity Shader入门精要》一书4.7章。

TransformWorldToTangent:通过左乘世界空间到切线空间的变换矩阵完成变换,把世界空间的方向矢量变换到切线空间。

TransformTangentToObject:通过右乘世界空间到切线空间的变换矩阵完成逆变换,得到世界空间的方向矢量,再左乘世界空间到模型空间的变换矩阵,把切线空间的方向矢量变换到模型空间。

TransformObjectToTangent:通过调用TransformObjectToWorldDir把方向矢量从模型空间变换到世界空间,再左乘世界空间到切线空间的变换矩阵,把模型空间的方向矢量变换到切线空间。

DepthCopy.hlsl

VertexInput:VS的输入数据,包含模型空间坐标、纹理坐标UV0和instanceID(如果启用Instance的话)。

VertexOutput:VS的输出数据,包含齐次裁剪空间的坐标位置、纹理坐标UV0、instanceID(如果启用Instance的话)、以及XR相关的双眼数据。

vert:顶点着色主入口,设置instanceID(如果启用Instance的话),初始化双眼数据(如果启用XR的话),把模型空间的坐标变换到裁剪空间。

DEPTH_TEXTURE_MS:定义多重采样的深度纹理或者纹理阵列(XR模式)。

DEPTH_TEXTURE:定义全精度(32位)深度纹理或者纹理阵列(XR模式)。

LOAD:根据平台(GLES2不支持,直接返回全0)对多重采样纹理或纹理阵列(XR模式)执行从给定UV采样的操作。

SAMPLE:每个平台都是对深度纹理的 r通道执行采样。

MSAA_SAMPLES:设定2次或4次采样多重采样。

_CameraDepthAttachment:根据是否开启深度纹理多重采样定义摄像机的深度纹理和采样器。

sampler_CameraDepthAttachment:根据是否开启深度纹理多重采样定义摄像机的深度纹理和采样器。

DEPTH_DEFAULT_VALUE:根据UNITY_REVERSED_Z宏定义深度的默认值为0或者1。

DEPTH_OP:根据UNITY_REVERSED_Z宏定义深度操作函数用max还是min。

SampleDepth:根据是否启用深度多重采样对深度纹理执行循环多重采样或者简单采样,在GLES2上不展开循环,其它平台上均标记为展开循环。

如果UNITY_REVERSED_Z打开,深度默认值为1,循环多重采样时取最小值。

如果UNITY_REVERSED_Z未定义,深度默认值为0,循环多重采样时取最大值。

这里多重采样的在两种深度信息的形况下,无论是取最大值还是最小值,都是取出当前位置最靠近相机的一个深度值。

frag:片元着色主入口,设置instanceID(如果启用Instance的话),调用SampleDepth获取深度值并返回。

Input.hlsl

定义了InputData和前面已经提到过的那些个变换矩阵。

InputData中包含了世界空间坐标、世界空间法线、世界空间视线、阴影坐标、雾坐标、顶点光照、烘焙光照这些信息。

在这里定义了最多支持的额外光照数目为16个,加上主光源应该为17个。不包括烘焙光源。

InputBuiltin.hlsl

定义了绝大部分引擎cpp层会自动进行赋值的渲染用数据(变量),包括了时间、投影参数、屏幕参数、深度参数、正交相机参数、视锥体的六个面的信息 、相机坐标、光源信息、球谐系数、探针遮蔽、XR信息、环境色、间接光环境色、雾颜色、环境贴图、光照贴图、阴影遮罩、主光源Cookie、变换矩阵等等。

OptimizeProjectionMatrix:优化投影矩阵,把某些恒为0的位赋0来减少计算。

一些参数的意义,可以查看这里的代码注释。

这里的相机的投影矩阵永远保存的是场景中相机的投影矩阵,在阴影计算阶段,不能用这里的参数获取光源位置的投影矩阵。

InputSurfaceCommon.hlsl

SurfaceData:定义了表面数据,必须和轻量管线ShaderGraph的MasterNode相匹配。其中包含了反照率、高光、金属度、光滑度、切线空间法线、自发光、遮蔽率、透明度。

Alpha:根据是否叠加反照率的透明度计算透明度,并在启用AlphaTest的情况下执行透明阈值裁剪。

SampleAlbedoAlpha:调用SAMPLE_TEXTURE2D宏(GLES2调用tex2D,其它平台调用Texture2D的Sample函数,因为版本原因改名了)采样反照率贴图。

SampleNormal:调用SAMPLE_TEXTURE2D宏采样法线贴图,然后调用UnpackNormal或UnpackNormalScale(根据是否定义了BUMP_SCALE_NOT_SUPPORTED)把值域转换到-1~1。如果禁用法线贴图则直接返回(0,0,1)。

SampleEmission:如果启用自发光(_EMISSION), 调用SAMPLE_TEXTURE2D宏采样自发光贴图,并乘上自发光色。否则直接返回0。

InputSurfacePBR.hlsl

SAMPLE_METALLICSPECULAR:对于金属度模式和高光模式分别采样金属度纹理和高光纹理,以实现同一套代码兼容两种模式。

SampleMetallicSpecGloss:调用SAMPLE_METALLICSPECULAR采样金属度或高光纹理,返回高光光泽度;如果未提供该纹理,则通过颜色值进行计算,同样返回高光光泽度。

SampleOcclusion:调用SAMPLE_TEXTURE2D宏采样遮蔽纹理(G通道),若非GLES环境,基于遮蔽强度参数进行插值;如果未提供遮蔽纹理,直接返回1。

InitializeStandardLitSurfaceData:基于传入的uv值,采样计算反照率(并进行Alpha裁切)、金属度、高光、切线空间法线、遮蔽、自发光信息,返回SurfaceData结构体。

InputSurfaceSimple.hlsl

SampleSpecularGloss:根据是否启用光泽纹理(_SPECGLOSSMAP),从光泽纹理采样光泽色或者直接使用传入的高光色,如果启用基础透明度(_GLOSSINESS_FROM_BASE_ALPHA),则把传入的alpha用作a通道。这个接口用于简单光照模式。

InputSurfaceUnlit.hlsl

仅仅定义了一些变量,没有额外提供函数。

Lighting.hlsl

DECLARE_LIGHTMAP_OR_SH:如果定义了LIGHTMAP_ON,则为float2的UV值,通常为UV2;否则,为half3的值,用于存放顶点的球谐函数值。

OUTPUT_LIGHTMAP_UV:如果定义了LIGHTMAP_ON,则为一段计算UV缩放和偏移的表达式;否则为空行。

OUTPUT_SH:如果定义了LIGHTMAP_ON,则为空行;否则为调用SampleSHVertex函数的表达式。

SAMPLE_GI:如果定义了LIGHTMAP_ON,则为调用SampleLightmap函数的表达式;否则为调用SampleSHPixel函数的表达式。

kDieletricSpec:入射角下的标准介质反射率系数为4%。(详询BRDF双向反射分布函数的论文)。

LightInput:光输入常数结构体,用于在获取灯光参数时,保存根据灯光下标(从unity_4LightIndices0的4个通道依次获取)从CSharp层传入的光源数据(位置、颜色、距离衰减、聚光方向、聚光衰减)索引到的数据进行传参。

Light:着色数据结构体,用于存放获取到的灯光的数据(在所有灯光数组中的下标、相对当前片元的朝向、衰减、subtractive模式的衰减、颜色)进行传参。

BRDFData:BRDF数据结构体,用于PBR计算模块间进行参数打包传递。

CookieAttenuation:计算cookie衰减,cookie功能在轻量管线中已被禁用,说是产生了过多的变体。

DistanceAttenuation:计算距离衰减,并做平滑处理。主光源是不产生衰减的。只有平行光能成为主光源,即使没有主光源,其它类型的光源也不会充当主光源(详见SetupLightweightConstanstPass.cs的SetupMainLightConstants函数中对_MainLightPosition的设置逻辑)。

左图为无衰减,中图为衰减后,右图添加平滑处理。

SpotAttenuation:计算聚光灯的形态衰减。如果光源类型不是聚光灯,这个函数的计算结果恒为1,不起作用(但是计算仍要执行,似乎产生了平白的开销)。对于主光源不会调用这个函数,因此也不存在平白的开销。而一个场景只能有一个实时的平行光,另外一个会自动被设置为烘焙光。

左图为正常的聚光灯,右图去掉了聚光灯衰减,变成了一个点光源。

GetLightDirectionAndAttenuation:获取非主光源的方向和衰减,根据传入的灯光数据和片元的世界坐标,获取指向光源的向量并通过调用DistanceAttenuation和SpotAttenuation计算当前片元的衰减值。

从上图可以看到,平台上所有像素指向中间的光源的向量用RGB颜色表示,刚好与Unity使用的坐标轴的颜色是匹配的,即R(X轴)G(Y轴)B(Z轴),正上方向是G,所以当灯光被拉高时,整个平台变成绿色,如下图所示。

上图显示了点光源调用DistanceAttenuation得到的衰减系数。

上图显示了聚光灯调用DistanceAttenuation和SpotAttenuation得到的衰减系数。

GetMainLightDirectionAndAttenuation:获取主光源的方向和衰减,调用GetLightDirectionAndAttenuation,注释了cookie的处理,所以和非主光源没区别。这个方法不会被调用到。

GetMainLight:获取主光源的灯光数据(在所有灯光数组中的下标恒为0、方向光的朝向、衰减恒为1、subtractive模式的衰减、颜色)。对于主光源实际上使用的是这个方法。

GetLight:获取非主光源的灯光数据(在所有灯光数组中的下标、相对当前片元的朝向、衰减、subtractive模式的衰减、颜色)。引擎会自动设置unity_4LightIndices0和unity_4LightIndices1来指定当前片元用到的光源的索引,设置的代码在cpp层,没有开源。

上图用颜色表示两个Cube分别受到影响的光源的索引值,左边的Cube受到索引为0、1、2、3的光源影响,颜色接近黑色(1+2+3)*0.037=0.222,右边的Cube受到索引为4、5、6、7的光源影响,颜色接近白色(4+5+6+7)*0.037=0.777,而影响范围没有覆盖到这两个Cube的大量实时光源不会影响这两个Cube,影响范围覆盖Cube的光源数量超过管线允许的最大像素级光源数量(轻量管线默认为4)时,会按重要与否和亮度高低排序,仅使用最靠前的光源(这在Unity早年关于光源的文档中有说明)。

GetPixelLightCount:获取像素级灯光的数量。注意,在编辑器模式下,CullResults类不会随时更新可见光列表信息,会缓存上一次运行的筛选结果,所以如果是变更了实时光,需要运行起来查看。

ReflectivitySpecular:获取高光3通道的最大值,如果是GLES1,则只取R通道。

OneMinusReflectivityMetallic:标准介质反射率系数叠加金属度计算。96% - M * 96%,所以金属度越高,返回值越小(最小值为0),金属度越低,返回值越大(最大值为96%)。

InitializeBRDFData:初始化BRDF(双向反射分布函数)数据。对于基于高光的方式,调用ReflectivitySpecular计算反射率;对于基于金属度的方式,调用OneMinusReflectivityMetallic计算反射率。并计算漫反射、高光、粗糙度等相关信息,存于BRDFData结构体中。这里面存有一些预计算的数据,作用是给到后续执行的计算进行直接调用,节省一些重复计算,例如粗糙度的平方、粗糙度的四倍加二、粗糙度的平方减一。

EnvironmentBRDF:间接漫反射和BRDF漫反射的乘积加上受粗糙度(1粗糙度+1)减弱的间接高光反射和BRDF高光反射(计算菲涅尔)的乘积。

DirectBDRF(又拼错):进行BRDF计算。

BRDF高光 = D * V * F4.0

其中D = 粗糙度2法线与光视线2 * 粗糙度2 - 1 + 12

V * F = 1.0光线与光视线2* (粗糙度+0.5)

注光视线是指入射光矢量和视线矢量相加,夹在两者中间的矢量H。

参见Siggraph 2015上关于移动平台PBR优化的资料https://community.arm.com/c/e/40

把计算结果截取到0 – 100的范围内,以防移动平台上使用half会造成数据溢出。

SampleSH:获取cpp层传入的辐照度环境贴图球谐系数Ar、Ag、Ab、Br、Bg、Bb、C,调用SampleSH9(在Core库中定义)计算L0、L1、L2的结果。从CubeMap映射到SH的原理参见微软皮特派克石罗恩的《愚蠢的球谐函数技巧(Stupid Spherical Harmonics(SH) Tricks》,附录10提供了SampleSH9的完整shader实现以及cpp层计算这7个系数的完整代码实现。

SampleSHVertex:顶点级的球谐系数,根据EVALUATE_SH_VERTEX和EVALUATE_SH_MIXED宏的定义与否,区分是计算全部L0、L1、L2还是只计算L2,或者全部留给像素级计算。是对SampleSH及其子过程的条件封装。

SampleSHPixel:像素级的球谐系数,根据EVALUATE_SH_VERTEX和EVALUATE_SH_MIXED宏的定义与否,区分是全都用顶点级计算的结果还是只计算L0、L1,或者全部进行逐像素计算。是对SampleSH及其子过程的条件封装。

SampleLightmap:调用SampleDirectionalLightmap或SampleSingleLightmap(均在Core库中定义)进行光照贴图的采样。

GlossyEnvironmentReflection:计算间接高光,首先通过粗糙度算出非线性的MipMap级别,然后从环境六面体贴图(在cpp层指定,未开源,在Light Settings面板中指定)采样相应MipMap级别的预编码辐照度,如果开启了HDR则再从HDR解码辐照度,最后乘上遮蔽系数得到最终结果。如果未开启光滑环境反射功能,则直接用常量颜色代替。

在Light Settings面板中指定该环境六面体贴图。

左图直接返回0,右图为正常计算了光滑环境反射,可以看到,在0粗糙度的情况下,两面侧墙明显地反射出了天空球的天际线,而遮蔽纹理可以在既定的粗糙度下按需减弱这种反射。

SubtractDirectMainLightFromLightmap:基于兰伯特光照模型和光源的衰减程度计算混合型(Mixed即Baked和Realtime共存)光源产生的实时阴影,用烘焙结果减去(Subtractive由此而来)实时阴影的亮度,并通过Light Settings中Realtime Shadow Color设置的颜色提供一个保底亮度(即用户可手动指定阴影的最低暗度,防止阴影过于暗)。要想让这个函数工作,需要把光源设置为Mixed,并且设置好光源的Shadow Type(如果选No Shadows,内存中读取到的Lighting Mode恒为Indirect Only,这个函数不会被调用),Light Settings中设置Lighting Mode为Subtractive。

Realtime Shadow Color设置为0,阴影较暗。

Realtime Shadow Color设置为114,阴影较亮。

GlobalIllumination:调用reflect计算反射向量,计算菲涅尔系数,烘焙的GI乘上遮蔽系数计算间接漫反射,调用GlossyEnvironmentReflection计算环境反射,最后调用EnvironmentBRDF传入这些参数计算静态物体受到的GI间接光照的值。

菲涅尔系数如上图所示,中间的小人为动态物体,不受本函数影响。

两侧光滑墙面产生的完全反射。

通过EnvironmentBRDF混合漫反射和镜面反射后的结果,左侧墙为灰色,右侧墙为红色,底座为绿色。

MixRealtimeAndBakedGI:调用SubtractDirectMainLightFromLightmap计算混合光照情况下的阴影,然后把光源进一步衰减(和减法模式的烘焙衰减相乘)。

两面墙壁和底座为静态物体,实时光源和烘焙GI的混合结果如上图所示。左侧墙为灰色,右侧墙为红色,底座为绿色。

LightingLambert:兰伯特光照模型计算漫反射,法线点积光线。

LightingSpecular:布林冯光照模型计算镜面反射,法线点积光视线(光线和视线的夹线)。

LightingPhysicallyBased:6参数,调用DirectBDRF进行BRDF计算,里面反正都是嵌套调用前面的那些方法。

LightingPhysicallyBased:4参数,调用前面这个6参数的重载。

VertexLighting:进行逐顶点的光照计算,必须是可线性插值的特性才能在顶点中进行计算,由于顶点的数量通常少于像素的数量(假定每个三角面至少占据3个屏幕像素,否则这模型规格就定得有问题了),这会取得更好的性能。这里主要是逐灯光处理了光色的衰减,并调用LightingLambert计算漫反射。当场景内额外实时光源数目多于4个时,多出的部分光源会进行顶点级别的着色处理。如果没有需要做顶点处理的光源,这里返回黑色。

LightweightFragmentPBR:PBR片元着色的主入口,作为外部模块调用的API,对于任何想要在PBR之上叠加的效果,可以在这个函数调用之前/后进行处理。类似ShaderGraph的PBRMasterNode结点的使用思路。

LightweightFragmentBlinnPhong:布林冯光照模型片元着色的主入口,作为外部模块调用的API,对于任何想要在其之上叠加的效果,可以在这个函数调用之前/后进行处理。类似ShaderGraph的PBRMasterNode结点的使用思路。

LightweightPassDepthOnly.hlsl

顾名思义,是只进行坐标计算并写入深度用的,需要注意的是,如果存在局部全透明像素,不能写入深度。只有在开启透明度测试的情况下,才会把没通过测试的片元裁剪掉,从而不写入深度。

VertexInput:VS的输入数据,包含模型空间坐标、纹理坐标UV0和instanceID(如果启用Instance的话)。

VertexOutput:VS的输出数据,包含纹理坐标UV0、齐次裁剪空间的坐标位置、instanceID(如果启用Instance的话)、以及XR相关的双眼数据。

DepthOnlyVertex:设置instanceID(如果启用Instance的话),初始化双眼数据(如果启用XR的话),执行纹理坐标的平移、缩放,把模型空间的坐标变换到齐次裁剪空间。

DepthOnlyFragment:调用SampleAlbedoAlpha获取纹理中的Alpha值,调用Alpha进行全透明像素裁剪(需要开启透明度测试,对小于等于给定裁剪值_Cutoff的像素直接返回,不再执行后续的渲染步骤——不写入深度)。

LightweightPassLit.hlsl

LightweightVertexInput:VS的输入数据,包含模型空间、模型空间的法线、模型空间的切线、纹理坐标UV0、光照贴图坐标UV1和instanceID(如果启用Instance的话)。

LightweightVertexOutput:VS的输出数据,包含纹理坐标UV0、光照贴图(或顶点球谐)坐标UV1、额外光源的世界空间坐标、法线(启用法线贴图时额外提供切线、副法线)、视线、雾强度、顶点光、阴影坐标、裁剪空间的坐标、instanceID(如果启用Instance的话)、以及XR相关的双眼数据。

InitializeInputData:根据LightweightVertexOutput结构体的输入数据,逐片元将法线、视线转换到世界空间,填充InputData结构体,以供后续使用。

LitPassVertex:顶点着色主入口,根据LightweightVertexInput结构体的输入数据,逐顶点计算裁剪空间坐标、视线、法线(切线、副法线)、光照纹理坐标、球谐坐标、兰伯特漫反射、雾强度、阴影坐标,输出到LightweightVertexOutput结构体中。

LitPassFragment:片元着色主入口,调用InitializeStandardLitSurfaceData(在InputSurfacePBR.hlsl中定义)填充SurfaceData结构体,调用InitializeInputData填充InputData结构体,调用LightweghtFragmentPBR计算PBR光照,调用ApplyFog计算雾。

LightweightPassLitSimple.hlsl

LightweightVertexInput:顶点着色输入信息,包含模型空间顶点坐标、模型空间法线、模型空间切线、纹理坐标UV0、光照贴图坐标UV1和instanceID(如果启用Instance的话)。

LightweightVertexOutput:顶点着色输出信息,包含纹理坐标UV0、光照贴图(或顶点球谐)坐标UV1、世界空间的坐标、亮度、世界空间法线、视线、世界空间切线(如果启用法线贴图)、世界空间副法线(如果启用法线贴图)、雾系数、顶点光照信息、阴影坐标、齐次裁剪空间坐标、instanceID(如果启用Instance的话)、以及XR相关的双眼数据。

InitializeInputData:基于传入的LightweightVertexOutput结构体,计算世界空间法线、世界空间视线、采样烘焙光照、并填充世界空间坐标、雾系数、顶点光照信息,填充InputData结构体。

LitPassVertexSimple:用于Standard (Simple Lighting)的顶点着色主入口,根据传入的LightweightVertexInput结构体,设置instanceID(如果启用Instance的话),初始化双眼数据(如果启用XR的话),执行纹理坐标的平移、缩放,把模型空间的坐标变换到齐次裁剪空间,填充世界空间坐标、亮度、视线,根据是否启用法线贴图,输出必要的法线信息,执行光照贴图坐标的平移、缩放,计算球谐光照、雾效、顶点光照、阴影坐标,填充LightweightVertexOutput结构体输出。

LitPassFragmentSimple:用于Standard (Simple Lighting)的片元着色主入口,根据传入的LightweightVertexOutput结构体,设置instanceID(如果启用Instance的话),采样漫反射颜色和透明度,执行Alpha裁切,从法线贴图采样切线空间的法线,采样自发光颜色,采样高光光泽度,调用InitializeInputData填充InputData结构体,调用LightweightFragmentBlinnPhong根据布林冯光照模型计算光照结果,计算雾效。

LightweightPassMetaCommon.hlsl

烘焙专用的Pass,计算光照贴图。

MetaInput:烘焙用片元数据输入结构体,包括反照率、自发光、高光颜色。

MetaVertexInput:烘焙用顶点着色输入结构体 ,包含模型空间坐标、模型空间法线、纹理坐标UV0、UV1、UV2,模型空间切线。

MetaVertexOuput:烘焙用顶点着色输出结构体 ,齐次坐标空间顶点坐标、纹理坐标UV0。

MetaVertexPosition:根据光照贴图坐标UV1执行纹理缩放、平移,并从世界空间转换到齐次裁剪空间。

MetaFragment:根据unity_MetaFragmentControl的x、y值是否非0,返回反照率的计算值或自发光色,将unity_OneOverOutputBoost归到0~1,调用PositivePow(在Core中定义)计算反照率颜色的unity_OneOverOutputBoost次方,并截断到0~unity_MaxOutputValue之间。或者直接用自发光色。

LightweightVertexMeta:调用MetaVertexPosition根据纹理坐标计算齐次裁剪空间坐标,执行纹理坐标的平移、缩放。这个函数十分简单,仅仅用于烘焙时对纯静态物体的空间位置和纹理坐标进行处理,以定位到光照贴图上正确的位置。

LightweightPassMetaPBR.hlsl

LightweightFragmentMeta:调用InitializeStandardLitSurfaceData填充SurfaceData结构,调用InitializeBRDFData初始化BRDF(双向反射分布函数)数据,基于计算结果填充MetaInput结构体的反照率、高光颜色、自发光,最后调用MetaFragment计算最终颜色。

LightweightFragmentMeta的正常计算结果如上左图光照贴图第一行第三格所示,提供了来自其它物体反射的间接光照,如果这个函数直接返回0,如上右图第一行第三格所示,间接光照丢失。在场景中的实际表现如下两图所示:

这个函数仅对烘焙光照贴图产生影响,运行时不使用。从这段代码看,这只是一个比较简化的计算GI的方式,显然不是基于光线追踪的。

LightweightPassMetaSimple.hlsl

LightweightFragmentMetaSimple:从主纹理采样反照率,调用SampleSpecularGloss采样高光、调用SampleEmission采样自发光,最后调用MetaFragment计算最终颜色。

LightweightPassShadow.hlsl

VertexInput:阴影计算的顶点着色输入结构体,包含模型空间坐标、模型空间法线、纹理坐标UV0、instanceID(如果启用Instance的话)。

VertexOutput:阴影计算的顶点着色输出结构体,纹理坐标UV0、齐次裁剪空间坐标。

GetShadowPositionHClip:计算世界空间坐标、世界空间法线、变换到齐次裁剪空间坐标,利用_ShadowBias的x分量处理平台差异,并填充深度。

ShadowPassVertex:阴影计算的顶点着色主入口,设置instanceID(如果启用Instance的话),执行纹理坐标的平移、缩放,调用GetShadowPositionHClip计算齐次裁剪空间的坐标,填充到VertexOutput结构体中。

ShadowPassFragment:阴影计算的片元着色主入口,调用SampleAlbedoAlpha采样反照率中的透明度,并调用Alpha进行透明度裁切。

片元阶段不输出颜色,只输出深度信息,因为该阶段是阴影投射操作,目的是生成阴影贴图。此时投影矩阵是相机在光源位置把当前深度信息填入阴影贴图。

GetShadowPositionHClip中参与计算的_ShadowBias来自于灯光信息面板阴影的参数设置,Bias和Normal Bias。Bias是朝着光源的方向偏移阴影,使得光源好像更远离投影体,物理上并不正确,但有助美术进行调整;NormalBias是延物体的法线反方向向内进行偏移。

Bias的作用是为了解决阴影贴图分辨率满足不了采样时纹素数和片元一 一对应(往往远少于实际片元数量),造成多个片元在阴影贴图的同一个纹素位置采样,而产生的阶梯状现象,如左图所示,尤其当光线方向和阴影接收面的夹角很小时这个现象会尤其明显,需要通过对阴影贴图中存储的深度值做一定的偏移(这里不是对阴影贴图中的深度值做偏移,而是对当前模型的齐次裁剪空间下的深度值做偏移-最用判断该处是否有阴影是完全等价的)来避免该现象,但同时也会导致阴影位置产生错误的偏移,所以Bias值应该在满足需求的情况下尽可能小。

NormalBias原理和Bias一样,只是会动态根据当前平面法线和光照方向的夹角 调整偏移值。

建议只使用NormalBias 不使用Bias。

Particles.hlsl

SOFT_PARTICLE_NEAR_FADE:_SoftParticleFadeParams的x分量,重定义该名称便于代码阅读,顾名思义,是软粒子的近淡入距离。近淡入距离和远淡出距离如下图所示,在Shader面板上指定。

SOFT_PARTICLE_INV_FADE_DISTANCE:_SoftParticleFadeParams的y分量,重定义该名称便于代码阅读,顾名思义,是软粒子的远淡出距离和近淡入距离的差的倒数。

CAMERA_NEAR_FADE:_CameraFadeParams的x分量,重定义该名称便于代码阅读,顾名思义,是相机的近淡入距离。近淡入距离和远淡出距离如下图所示,在Shader面板上指定。

CAMERA_INV_FADE_DISTANCE:_CameraFadeParams的y分量,重定义该名称便于代码阅读,顾名思义,是相机的远淡出距离和近淡入距离的差的倒数。

vertColor:根据是否开启UNITY_PARTICLE_INSTANCING_ENABLED宏,定义为vertInstancingColor或者为空。

vertTexcoord:根据是否开启UNITY_PARTICLE_INSTANCING_ENABLED宏,定义为vertInstancingUVs或者直接使用传入的纹理坐标。并根据是否开启_FLIPBOOK_BLENDING宏,处理纹理坐标的混合。

vertFading:根据是否为Fading模式或软粒子,对projectedPosition进行计算和赋值的表达式,否则为空。

fragColorMode:对于6种不同的颜色混合模式,定义了4段不同的颜色计算表达式,仅Unlit用到。

fragSoftParticles:根据是否为Fading模式或软粒子,用软粒子淡入淡出参数计算淡出值并乘以反照率,否则为空。

fragCameraFading:根据是否为Fading模式,用相机淡入淡出参数计算淡出值并乘以反照率,否则为空。

appdata_particles:顶点着色输入结构体,包含模型空间顶点坐标、模型空间法线、颜色、纹理坐标UV0及混合UV1(如果是翻书模式)、模型空间切线(若启用法线贴图)。

VertexOutputLit:顶点着色输出结构体,包含颜色、纹理坐标UV0、法线UV1或UV3、切线UV1和副法线UV2(如果启用法线贴图)、翻书模式混合参数UV4(如果是翻书模式)、透视位置UV5(为Fading模式或软粒子)、世界空间坐标及雾强度UV6、视线及亮度UV7、裁剪空间坐标。

readTexture:兼容翻书模式的纹理采样方法,返回采样到的颜色。如果是翻书模式的话,在两套UV采样到的颜色间插值(基于UV4中Z通道保存的混合系数)。

SampleNormalTS:调用readTexture(以兼容翻书模式)采样法线贴图中的切线空间法线值,并重置到-1~1值域。

SampleEmission:调用readTexture(以兼容翻书模式)采样自发光纹理并乘以自发光颜色。

SampleAlbedo:调用readTexture(以兼容翻书模式)采样反照率纹理并乘以颜色值,调用fragColorMode处理颜色模式、调用fragSoftParticles处理软粒子、调用fragCameraFading处理相机淡入淡出。

SampleSpecularGloss:调用readTexture(以兼容翻书模式)采样高光光泽纹理。

AlphaBlendAndTest:根据透明混合模式调用AlphaDiscard处理半透明混合和透明度裁剪。这个方法在写法上有点奇怪,似乎没有必要定义局部变量result,一并放到return语句就可以了。

_ALPHABLEND_ON

_ALPHAPREMULTIPLY_ON

_ALPHAOVERLAY_ON

返回值

Opaque

Cutout

1

Fade

alpha

Transparent

alpha

Additive

alpha

Subtractive

alpha

Modulate

1

AlphaModulate:根据是否定义_ALPHAMODULATE_ON在1和反照率间根据透明度插值或者直接返回反照率。

_ALPHAMODULATE_ON

返回值

Opaque

反照率

Cutout

反照率

Fade

反照率

Transparent

反照率

Additive

反照率

Subtractive

反照率

Modulate

在1和反照率之间根据透明度插值

InitializeInputData:根据VertexOutputLit结构体的数据,计算世界空间法线、视线、雾强度,阴影坐标、顶点光照、烘焙光照置0,填充到InputData结构体中。

ParticlesPBR.hlsl

InitializeSurfaceData:根据传入的VertexOutputLit结构体数据,调用SampleAlbedo采样反照率,根据是否启用金属光泽度纹理调用readTexture(兼容翻书效果)采样该纹理或使用统一参数,调用SampleNormalTS采样法线贴图,调用SampleEmission采样自发光贴图,调用AlphaModulate计算最终的反照率,调用AlphaBlendAndTest计算透明度和执行透明度裁剪,填充整个SurfaceData结构体中的数据。

Shadows.hlsl

SHADOWS_SCREEN:对于非GLES环境的级联阴影,启用屏幕阴影模式。

ShadowSamplingData:阴影数据结构体,包含阴影半像素偏移0(负负)、1(正负)、2(负正)、3(正正)和阴影贴图尺寸(宽度的倒数、高度的倒数、宽度、高度)。

GetMainLightShadowSamplingData:获取主光源提供的从DirectionShadowsPass.cs传入的上述阴影数据,填充ShadowSamplingData结构体并返回。

GetLocalLightShadowSamplingData:获取局部光源(聚光灯)提供的从LocalShadowsPass.cs传入的上述阴影数据,填充ShadowSamplingData结构体并返回。

GetMainLightShadowStrength:返回从DirectionShadowsPass.cs传入的主光源阴影强度。

GetLocalLightShadowStrenth:返回从LocalShadowsPass.cs传入的第index个局部光源(聚光灯)阴影强度。

SampleScreenSpaceShadowMap:已兼容XR模式的方式采样屏幕空间的阴影贴图,获取阴影衰减值。

SampleShadowmap:调用SAMPLE_TEXTURE2D_SHADOW(在Core库中定义,主要是兼容GLES2的情况)采样阴影贴图,如果启用软阴影,则执行9向高斯模糊或简化的4向高斯模糊(移动平台)。

ComputeCascadeIndex:根据世界坐标计算级联索引,每级的半径从DirectionalShadowsPass.cs传入,是通过CullResults的未开源代码计算出来的。推测是根据配置的级联百分比划定大致区间,但防止物体跨级联区域,所以区间会视物体情况动态微调。

TransformWorldToShadowCoord:调用ComputeCascadeIndex计算级联索引,左乘对应索引的_WorldToShadow矩阵将世界空间坐标转换到阴影空间。

ComputeShadowCoord:调用ComputeScreenPos(在Core.hlsl中定义)计算屏幕空间坐标。

MainLightRealtimeShadowAttenuation:根据是否屏幕空间阴影,分别调用SampleScreenSpaceShadowMap或SampleShadowmap计算主光源的实时阴影衰减。该衰减值用于在Subtract模式下被烘焙光减掉以形成阴影(详见Lighting.hlsl的SubtractDirectMainLightFromLightmap函数)。该衰减值用于实时平行光的阴影生成,直接赋值给平行光的衰减值。平行光没有衰减,但是通过从阴影贴图采样的深度信息,作为衰减值来和无衰减(无阴影)区域形成明暗对比来模拟阴影。

LocalLightRealtimeShadowAttenuation:调用SampleShadowmap计算局部光源(聚光灯)的实时阴影衰减。该衰减值用于在Subtract模式下被烘焙光减掉以形成阴影(详见Lighting.hlsl的SubtractDirectMainLightFromLightmap函数)。同平行光的计算,只是结果和聚光灯的衰减值相乘,所以在聚光灯的中心和离光源近的地方阴影会非常明显,靠近边缘阴影会淡化。

LightweightStandardTerrain.shader

提供了前向、产生阴影、深度计算3个Pass,实现了标准的简化(可调参数较少,但底层实现也是调用标准PBR接口执行的计算)PBR地形渲染。标记为依赖Standard Terrain Add Pass(地形用到的贴图超过4套时,从引用的第5套贴图开始会被使用这个Shader来渲染)和Standard Terrain Base(据说是在不能支持4套贴图的设备上使用这个Shader来渲染,只会用到1套贴图,差不多和Fallback “Diffuse”一样没法看)。

LightweightStandardTerrainAddPass.shader

仅提供了前向1个Pass,地形用到的贴图超过4套时,从引用的第5套贴图开始会被使用这个Shader来渲染。

LightweightStandardTerrainBase.shader

提供了前向、产生阴影、深度计算、烘焙4个Pass,据说是在不能支持4套贴图的设备上使用这个Shader来渲染,只会用到1套贴图,差不多和Fallback “Diffuse”一样没法看。想来地形在烘焙时也是Fallback到这个Shader的。

LightweightWavingGrass.shader

提供了标准着色、深度计算2个Pass,通过InputSurfaceGrass.hlsl、LightweightPassLitGrass.hlsl、LightweightPassDepthOnlyGrass.hlsl三个文件中提供的函数,绘制随风摆动的草地。

在地形设置中指定风的参数来控制草的波动幅度。

默认是用下面那个公告板,不清楚这个是在什么地方用到。

LightweightWavingGrassBillboard.shader

提供了标准着色、深度计算2个Pass,通过InputSurfaceGrass.hlsl、LightweightPassLitGrass.hlsl、LightweightPassDepthOnlyGrass.hlsl三个文件中提供的函数,绘制随风摆动的草地。

在地形设置中指定风的参数来控制草的波动幅度。

和LightweightWavingGrass.shader唯一的区别在于进行顶点计算时,是否按公告板的算法来进行。地形系统默认是使用这个Shader来渲染草。

LightweightBlit.shader

顾名思义,进行渲染对象的拷贝,代码简单到令人发指。

LightweightCopyDepth.shader

利用DepthCopy.hlsl中的代码机型深度纹理的拷贝,由于多重采样抗锯齿的关系,和上面那个LightweightBlit.shader相比还是略复杂些。

LightweightSampling.shader

拷贝过程中进行降采样的Shader,和LightweightBlit.shader基本相同,只是多了一步降采样计算。

LightweightScreenSpaceShadows.shader

顾名思义,为屏幕空间阴影。

VertexInput:顶点着色的输入结构体,包含模型空间顶点坐标、纹理坐标UV0、instanceID(如果启用Instance的话)。

Interpolators:顶点着色的输出结构体,包含齐次裁剪空间坐标、纹理坐标UV0、以及XR相关的双眼数据。

Vertex:顶点着色主入口,将顶点坐标变换到屏幕空间,基于XR计算双眼纹理坐标。

Fragment:初始化双眼数据(如果启用XR的话)。片元着色主入口,将相机的坐标变换到视口空间和世界坐标。调用SampleShadowmap计算屏幕空间阴影。

对屏幕空间阴影,只能计算实时平行光也就是主光源的阴影。屏幕空间阴影的计算要比正常的计算做更多的空间变换,但是可以作为后处理使用,更加灵活,也可以通过降采样来节省开销。推荐使用。

LightweightStandard.shader

提供了前向、产生阴影、深度计算、烘焙4个Pass,最常用的PBR Shader。

LightweightStandardParticles.shader

提供了前向1个Pass,其它的Pass通过Fallback实现。标准的粒子Shader。

LightweightStandardParticlesSimpleLighting.shader

提供了前向1个Pass,其它的Pass通过Fallback实现。简化光照的粒子Shader。

LightweightStandardParticlesUnlit.shader

提供了1个默认Pass。无光照计算的粒子Shader。

LightweightStandardSimpleLighting.shader

提供了前向、产生阴影、深度计算、烘焙4个Pass,简化光照的标准Shader。

LightweightStandardUnlit.shader

提供了前向、产生阴影、深度计算、烘焙4个Pass,无光照的标准Shader。

Unity3d轻量渲染管线(LWRP)民间文档相关推荐

  1. 【Unity3D日常开发】解析Txt文档,赋值给Text案例

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 一.前言 有小伙伴跟我说,公司现在需要解析JSON,但是百度的解析JSON的 ...

  2. 广点通-优量汇广告接入文档

    总是忘记广点通广告的接入链接:在这里标记一下:https://developers.adnet.qq.com/doc/android/access_doc, 大家有广告接入的问题可以一起交流.

  3. 几款接口文档管理工具

    在项目开发测试中,接口文档是贯穿始终的.前后端开发需要在开发前期进行接口定义并形成文档,QA在功能测试和接口测试的环节也需要依赖于这些接口文档进行测试.接口文档往往以最简单的静态文档的形态存在.然而在 ...

  4. 【markdown】【xSliders | slidev | mindshow】markdown文档生成PPT/视频

    文章目录 一.xSlides 自讲解幻灯片.生成视频 在线会议/课堂 基于Markdown 纯文本写作 支撑xSlides的设计逻辑 惯例优于配置 将重复操作交给"系统1",让&q ...

  5. 百度地图开放平台轻量路径规划

    百度地图开放平台轻量路径规划 背景 思路 完整代码 代码解读 结果截图 注意事项 背景 由于要做等时圈研究,手头上有一些poi数据,如小区地理位置信息,地铁站点的地理位置信息,想通过等时圈把两者连接起 ...

  6. Docute超简单的文档工具

    1.前言 在我们日常的开发和学习中,我们可能会进行在线文档的写作,为了能够让我们的文档公开给大家查阅,我们可能需要将其发布到公网.这个时候,我们会有很多的文档软件选项,比如GitBook.Docsif ...

  7. 接口文档管理工具-Postman、Swagger、RAP、DOClever

    在项目开发测试中,接口文档是贯穿始终的.前后端开发需要在开发前期进行接口定义并形成文档,QA在功能测试和接口测试的环节也需要依赖于这些接口文档进行测试.接口文档往往以最简单的静态文档的形态存在.然而在 ...

  8. 接口文档管理工具-Postman、Swagger、RAP

    在项目开发测试中,接口文档是贯穿始终的.前后端开发需要在开发前期进行接口定义并形成文档,QA在功能测试和接口测试的环节也需要依赖于这些接口文档进行测试.接口文档往往以最简单的静态文档的形态存在.然而在 ...

  9. 什么是金山文档轻维表?如何根据日期自动提醒发送表格中的内容?

    什么是金山文档轻维表? 金山文档作为老牌文档应用,推出了新的功能轻维表,是一款新式在线协作表格,具有传统表格强大的内核发动机,是专为多人协作场景设计的增强版表格软件,可以支持快速搭建轻量应用. 由于金 ...

最新文章

  1. opencv中imgproc库中函数详解(1)
  2. The path is not a valid path to the xx-generic kernel headers
  3. axure web组件_AXURE原型设计:移动端选择器的应用
  4. Keba常用硬件模块
  5. Python中的传值和引用
  6. 机器人当上“央视主播”,主持人感慨未来要下岗
  7. python numpy安装步骤-NumPy 安装
  8. Flocker 做为后端存储代理 docker volume-driver 支持
  9. 【机器学习】萌新必学的 Top10 算法
  10. rose服务器系统,ROSE HA 服务器集群软件
  11. php sns 源码,ThinkSNS V2.3源码下载(SNS系统)
  12. SQL Server 2012 数据库可疑问题解决
  13. Create user中采用externally操作系统认证方式详解
  14. 【离散】画哈斯图--最好理解绝不会出错
  15. 【读书笔记】Haskell函数式编程入门 I 基础篇 2. 类型系统和函数
  16. 用 Python 爬取分析每日票房数据
  17. EAUML日拱一卒-微信小程序实战:位置闹铃 (14)-分析监控点状态
  18. html传递汉字参数转换,url中如何传递中文信息呢?
  19. 2022-2028全球与中国磁屏蔽功率电感器市场现状及未来发展趋势
  20. 【Arduino实验02 LED流水灯】

热门文章

  1. android计时器秒,倒数计时器android秒不准确
  2. 多重条件结构(知识就是力量!)
  3. PAT 乙级 1100 校庆
  4. Java 操作数据库插入失败原因
  5. 自主创新持续领航,麒麟信安荣获“网信自主创新尖峰企业”称号
  6. AI人工智能技术可以应用在网站seo优化推广上吗?
  7. 功能安全-26262-理论到实践-基础知识-标准机构与认可、认证
  8. 【项目管理】项目中的角色
  9. ubuntu 小新pro14 网卡驱动、键盘、触摸板
  10. excel matlab日期,Excel日期格式在matlab中的转换