UsageFlag文檔:https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/MaterialProperties/

ShadingModel文檔: https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/MaterialProperties/LightingModels/

本文章是基於4.27

之前有空想要比對一下mobile pass跟SM5 pass到底差在哪裡。有發現在windows editor中,只有SM5跟ES3二種,就算是選了蘋果icon也不是真的metal,真正的metal shader code只能在mac環境下編譯出來。

在追code的過程中相關些名詞一直出現,引起了我的好奇,因此決定仔細研究一下。想要追shader相關的程式碼,這幾個ini參數最好設一下會比較好debug:

`DefaultEngine.ini: [DevOptions.Shaders] bAllowCompilingThroughWorkers=false bAllowAsynchronousShaderCompiling=false

[ConsoleVariables] r.DumpShaderDebugInfo=1


在material上有usage跟shading model的選項,usage主要的用途是用來決定哪些shader組合是不是要compile permutation;ShadingModel主要是用來決定計算光源貢獻時要走哪條IntegrateBxDF的路。

在Unreal中shader code的generation主要是靠C++ Template的機制來大量產生各種組合(permutation),其中可以看到引擎大量的使用[Policy-Based Design](https://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315)。我們知道template的機制是compile time產code,所以我們只要有n個base shader,再加上m種policy的話,我們就可以產生n*m種組合。

每個Policy都會實作ShouldCompilePermutation跟ModifyCompilationEnvironment這二個function;前者搭配Usage使用,替BaseShaderType決定要搭配哪些Policy組合的編譯,例如,NoLightMapPolicy,接著實作會呼叫ModifyCompilationEnvironment,他會替Shader加macro,決定某段shader code要不要留下來。

以bIsUsedWithStaticLighting為例,若該usage有勾選的話,在跑IMPLEMENT_SHADER_TYPE時,其相關連到一組的BasePassVertexShader.usf、BasePassTessellationShaders.usf以及BasePassPixelShader.usf(Vertex Shader, Hull and Domain Shader, Pixel Shader)就會被用搭配到的TLightMapPolicy建立出來。

例如在${PROJECT_ROOT}/Saved/ShaderDebugInfo/PCD3D_SM5/AmbientOcclusion/FLocalVertexFactory可以看到類似於下面中的資料夾。其中產生的permutation shader code,針對BasePassVertexShader.usf,產生NoLightMapPolicy以及NoLightMapPolicyAtmosphericFog這二組;針對BasePassPixelShader.usf,則產生了FNoLightMapPolicy跟FNoLightMapPolicySkylight。

![](http://dorgon.horizon-studio.net/wp-content/uploads/2022/04/image.png)

這些Policy主要的功能是用來ModifyCompilationEnvironment,替shader code加入需要的macro define,之後每個permutation就會利用mcpp這個preprocess來幫我們濾掉不需要code,並把所有include到的shader結合成一個大檔之後,產生特製的shader code出來。

另外Usage除了決定permutation要不要產生出來外,有一些還會用來附加一些macro進shader中,例如OutEnvironment.SetDefine(TEXT("USES_DISTORTION"), Material->IsDistorted());

有發現Usage雖然有八成左右的功能都只是用來決定編譯組合;ShadingModel主要是用來決定計算光源貢獻的做法。但有看到有少部份會直接去加shader macro,例如Usage中的UsesDistortion以及ShadingModel中的MSM_SingleLayerWater,不確定這樣的做法是不是原設計者的意圖,比較像是hack。

ShadingModel主要是用來決定計算光源貢獻時要走哪條IntegrateBxDF的路,以下是在ShadingModels.ush中的實作:

`FDirectLighting IntegrateBxDF( FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow )
{
        switch( GBuffer.ShadingModelID )
        {
                case SHADINGMODELID_DEFAULT_LIT:
                case SHADINGMODELID_SINGLELAYERWATER:
                case SHADINGMODELID_THIN_TRANSLUCENT:
                        return DefaultLitBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_SUBSURFACE:
                        return SubsurfaceBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_PREINTEGRATED_SKIN:
                        return PreintegratedSkinBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_CLEAR_COAT:
                        return ClearCoatBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_SUBSURFACE_PROFILE:
                        return SubsurfaceProfileBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_TWOSIDED_FOLIAGE:
                        return TwoSidedBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_HAIR:
                        return HairBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_CLOTH:
                        return ClothBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                case SHADINGMODELID_EYE:
                        return EyeBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
                default:
                        return (FDirectLighting)0;
        }
}`

## Related
<!-- auto-related:start -->
- [認識Editor環境與UE4檔案命名系統](/blog/uncategorized/2017/2017-07-09-editor-ue4)
- [UE4How to use Visual Studio Graphics Debugger to debug shader](/blog/uncategorized/2017/2017-10-28-how-to-use-visual-studio-graphics-debugger-to-debug-shader)
- [演講筆記UnrealEngine UE5 Deferred Technology Pipeline for Mobile Games](/blog/uncategorized/2022/2022-09-03-ue5-deferred-technology-pipeline-for-mobile-games)
- [UnrealEngine讀書心得 關於UnrealEngine中的ISPC與SIMD的一些雜談](/blog/uncategorized/2023/2023-01-04-unrealengine-ispc-simd)
<!-- auto-related:end -->