unreal4 使用的着色器

2018-10-06 17:44:00 u012614151 阅读数 52

本文为()Unreal 调试着色器编译过程

   

调试着色器编译过程

Rolando Caloca 在 April 19, 2016 | 学习编程

   

启用 CVAR 以允许转储中间着色器

在 ConsoleVariables.ini 文件(通常位于 Engine/Config/ConsoleVariables.ini)中,启用下列 Cvar:

   

在调试时构建 SHADERCOMPILEWORKER

默认情况下,UnrealBuildTool (UBT) 将为工具生成项目,以便始终在开发时进行编译。要在调试时构建项目,请将 ShaderCompileWorker 的解决方案属性(Visual Studio:生成 -> 配置管理器)更改为 Debug_Program:

   

生成中间文件

此刻,您想要生成可调试的文件;启用 Cvar 将允许后续编译转储所生成的文件;要强制重建所有着色器,请在Engine/Shaders/Common.usf 中添加一个空格或进行更改,然后重新运行编辑器。这将重新编译着色器并转储Project/Saved/ShaderDebugInfo 文件夹中的所有中间文件。

   

转储的着色器的文件夹结构

让我们分析转储的文件的完整路径:

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf

项目的根路径:

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf

转储着色器的根路径:

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf

现在,对于每种着色器格式/平台,您可找到一个子文件夹,在本例中,这是 PC D3D Shader Model 5:

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf

现在,对于每个材质名称都有一个对应的文件夹,并且有一个名为 Global 的特殊文件夹。在本例中,我们在 M_Egg 材质内:

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf

着色器分组在贴图内并按顶点工厂排序,而这些顶点工厂通常最终对应于网格/元件类型;在本例中,有一个"本地顶点工厂":

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf

最后,下一路径是特性的排列;因为我们早先已启用 r.DumpShaderDebugShortNames=1,所以名称已压缩(以减小路径长度)。如果将其设置为 0,那么完整路径将是:

D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\FLocalVertexFactory\TBasePassPSFNoLightMapPolicy\BasePassPixelShader.usf

在该文件夹中,有一个批处理文件和一个 usf 文件。这个 usf 文件就是要提供给平台编译器的最终着色器代码。您可通过这个批处理文件来调用平台编译器,以查看中间代码。

   

使用 SHADERCOMPILEWORKER 进行调试

从 4.11 版开始,我们为 ShaderCompileWorker (SCW) 添加了一项功能,使其能够调试平台编译器调用;命令行如下所示:

PathToGeneratedUsfFile -directcompile -format=ShaderFormat -ShaderType -entry=EntryPoint

  • PathToGeneratedUsfFile 是 ShaderDebugInfo 文件夹中的最终 usf 文件
  • ShaderFormat 是您想要调试的着色器平台格式(在本例中,这是 PCD3D_SM5)
  • ShaderType 是 vs/ps/gs/hs/ds/cs 中的一项,分别对应于"顶点"、"像素"、"几何体"、"物体外壳"、"域"和"计算"着色器类型
  • EntryPoint 是 usf 文件中此着色器的入口点的函数名称

例如,D:\UE4\Samples\Games\TappyChicken\Saved\ShaderDebugInfo\PCD3D_SM5\M_Egg\LocalVF\BPPSFNoLMPolicy\BasePassPixelShader.usf-format=PCD3D_SM5 -ps -entry=Main

现在,您可以对 D3D11ShaderCompiler.cpp 中的 CompileD3D11Shader() 函数设置断点,通过命令行运行 SCW,并且应该能够开始了解如何调用平台编译器。

有关此主题的更多信息,请参阅虚幻引擎文档的以下两个页面:

2018-06-09 17:31:22 wangyars 阅读数 2061

https://medium.com/@lordned/unreal-engine-4-rendering-part-2-shaders-and-vertex-data-80317e1ae5f3

翻译:yarswang 转载请保留

 

着色器与顶点工厂

 

    本章我们来关注着色器与顶点工厂。Unreal使用一些魔法来绑定C++的着色器表现与等价HLSL类,使用顶点工厂(Vertex Factory)来将顶点着色器的控制数据上传到GPU。从本章起,我们将使用Unreal的类名来讨论问题,让它们更容易被你自己查找。

    我们将专注于核心的着色器/顶点工厂涉及类。有很多处理内务的结构/方法如胶水一般地黏合了整个系统。我们不太可能去修改这些胶水,所以我们不会去谈他们来让事情变得更复杂。

 

FLocalVertexFactory的顶点输出

 

着色器

    Unreal中所有着色器都派生自一个基类 FShader.  Unreal有两个主要的着色器分类,FGlobalShader用于在只应存在一个实例的情况,以及FMaterialShader使用在关联材质的着色器上。FShader和FShaderResouce配对,后者跟踪与特定着色器相关联的GPU上的资源。如果FShader的已编译输出与已存在的输出相匹配,则可以在多个FShaders之间共享FShaderResource。

FGlobalShader

    这是个很简单,并且有限(但是有效!)的用法。 当着色器类从FGlobalShader派生时,它将它们标记为Global重新编译组的一部分(这似乎意味着它们在引擎打开时不会重新编译!)。 只有一个全局着色器实例存在,这意味着你不能拥有每个实例的参数。 但是,您可以拥有全局参数。 示例:FLensDistortionUVGenerationShader,FBaseGPUSkinCacheCS(用于计算网格蒙皮的计算着色器)和FSimpleElementVS/ FSimpleElementPS。

  

FMaterialShader和FMeshMaterialShader

    FMaterialShader和FMeshMaterialShader更加复杂。这两个类都允许多个实例,每个实例都与其自己的GPU资源副本相关联。 FMaterialShader添加了一个SetParameters函数,它允许着色器的C ++代码更改绑定HLSL参数的值。参数绑定是通过FShaderParameter / FShaderResourceParameter类完成的,并且可以在着色器的构造函数中完成,比如说FSimpleElementPS。在使用该着色器渲染某些内容之前调用SetParameters函数,并传递相当一部分信息(包括材质),这会为您提供大量信息来作为您计算要更改的参数的一部分。

    现在我们知道如何设置着色器范围的参数,我们可以看看FMeshMaterialShader。这增加了我们在绘制每个网格之前在着色器中设置参数的功能。大量着色器从该类派生,因为它是需要材质和顶点工厂参数的所有着色器的基类(根据文件中留下的注释)。这只需添加一个SetMesh函数,在绘制每个网格之前调用该函数,从而允许您修改GPU上的参数以适应特定的网格。示例:TDepthOnlyVS,TBasePassVS,TBasePassPS。

 

将C++绑定到HLSL

        因此,现在我们知道FShaders是CPU上着色器的C++表示,我们需要知道如何将给定的FShader与其相应的HLSL代码相关联。这是我们第一个C++宏的用途:IMPLEMENT_MATERIAL_SHADER_TYPE(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency)。在解释每个参数之前,让我们看一下DepthRendering.cpp中的一个例子:

 

IMPLEMENT_MATERIAL_SHADER_TYPE(,FDepthOnlyPS,TEXT("/Engine/Private/DepthOnlyPixelShader.usf"),TEXT("Main"),SF_Pixel);

 

    该宏将C++类FDepthOnlyPS绑定到位于/Engine/Private/DepthOnlyPixelShader.usf中的HLSL代码。具体而言,它与入口点“Main”以及SF_Pixel的频率相关联。我们现在拥有一个存在于(DepthOnlyPixelShader.usf)中HLSL文件的C++代码(FDepthOnlyPS)和该HLSL代码中要调用(Main)的函数之间的关联。Unreal使用术语“频率”来指定它的着色器类型 - 顶点,船体,域,几何,像素或计算。

    你会注意到这个实现忽略了第一个参数。因为这个特定的例子不是模板化的函数。在某些情况下,宏专门化了一个模板类,其中的模板类由另一个宏实例化,以创建特定的实现。例如为每种可能的照明类型创建一个变体。如果您好奇的看到了BasePassRendering.cpp的顶部的宏IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE......我们将在Base Pass文章中深入介绍它!

 

综述

FShader的实现是着色管道中的特定阶段,可以在使用之前修改其HLSL代码中的参数。Unreal使用宏将C++代码绑定到HLSL代码。从头开始实现着色非常简单,但将其集成到现有的延迟基本通道/阴影中将会复杂的多。

 

缓存和编译环境

在我们继续之前,有两个重要概念要介绍。在你修改材质后,Unreal会自动编译许多可能的着色器队列。这是一件好事,但会产生过多的无用着色器。这里介绍一下ShouldCache函数。

    如果“着色器”,“材质”和“顶点工厂”都同意应该缓存某队列,Unreal才会会创建着色器的这个特定队列。如果其中任何一个不同意,那么Unreal就会跳过创建该队列,这意味着在此队列可被绑定的情况下,你永远等不到结束。比如说,你不想去缓存一个需要SM5支持的着色器。如果您正在构建不支持它的平台,则没有理由编译和缓存它。

    ShouldCache函数是一个静态函数,可以在FShader,FMaterial或FVertexFactory类中实现。检查现有的使用情况将为您提供一个关于如何以及何时可以实现它的想法。

    第二个重要概念是在编译之前更改HLSL代码中的预处理器定义的能力。 FShader使用ModifyCompilationEnvironment(通过宏实现的静态函数),FMaterial使用SetupMaterialEnvironment,而FVertexFactory使用ModifyCompilationEnvironment。这些函数在着色器编译之前调用,并允许您修改HLSL预处理器定义。 FMaterial广泛使用该设置来根据材料内的设置设置着色模型相关的定义,以优化掉任何不必要的代码。

 

FVertexFactory

    现在我们知道如何在渲染着色器之前修改着色器,首先需要知道如何将数据提取到GPU中!顶点工厂封装顶点数据源并可以链接到顶点着色器。如果你已经写出了自己的渲染代码,那么你可能会试图创建一个包含顶点可能需要的所有可能数据的类。 Unreal使用顶点工厂,它只允许您将实际需要的数据上传到顶点缓冲区。

    要了解顶点工厂,我们应该了解两个具体的例子。 FLocalVertexFactory和FGPUBaseSkinVertexFactory。FLocalVertexFactory用于许多地方,因为它提供了一种简单的方法来将显式顶点属性从本地变换到世界空间。静态网格使用这个,缆绳和过程网格也是如此。骨骼网格(需要更多数据)另一方面使用FGPUBaseSkinVertexFactory。再下来,我们看看与这两个顶点工厂匹配的着色器数据如何具有不同的数据。

 

FPrimitiveSceneProxy

    那么Unreal如何知道哪个顶点工厂用于网格?通过FPrimitiveSceneProxy类! FPrimitiveSceneProxy是UPrimitiveComponent的呈现线程版本。它的目的是子类UPrimitiveComponent和FPrimitiveSceneProxy并创建特定的实现。

 

FCableSceneProxy aFPrimitiveSceneProxy for the dynamic cable component.

    后退一下 - 虚幻有一个游戏线程和一个渲染线程,两者不应该触及属于另一个线程的数据(除非通过几个特定的​​同步宏)。为了解决这个问题,Unreal使用UPrimitiveComponent作为游戏线程,并通过重写CreateSceneProxy()函数来决定创建哪个FPrimitiveSceneProxy类。然后,FPrimitiveSceneProxy可以在适当的时候查询游戏线程,从游戏线程获取数据到渲染线程中,以便将其处理并放置在GPU上。

    这两个类通常成对出现,这里有两个很好的例子:UCableComponent / FCableSceneProxyUImagePlateFrustrumComponent /FImagePlateFrustrumSceneProxy。在FCableSceneProxy中,渲染线程查看UCableComponent中的数据并构建一个新的网格(计算位置,颜色等),然后与之前的FLocalVertexFactory相关联。 UImagePlateFrustrumComponent是整洁的,因为它根本没有顶点工厂!它只是使用渲染线程的回调来计算一些数据,然后使用该数据来绘制线条。没有着色器或顶点工厂与它关联,它只是使用GPU回调来调用一些即时模式样式的渲染函数。

 

Binding C++ to HLSL

    目前为止,我们已经介绍了不同类型的顶点数据以及场景中的组件如何创建并存储这些数据(通过具有顶点工厂的场景代理)。现在我们需要知道如何在GPU上使用唯一的顶点数据,尤其是考虑到basepass只有一个顶点函数需要处理所有不同类型的传入数据!如果你猜“另一个C++宏”,答对了!

IMPLEMENT_VERTEX_FACTORY_TYPE(FactoryClass,ShaderFilename, bUsedWithmaterials, bSupportsStaticLighting,bSupportsDynamicLighting, bPrecisePrevWorldPos, bSupportsPositionOnly)

 

    这个宏允许我们将顶点工厂的C ++表示绑定到特定的HLSL文件。例:

IMPLEMENT_VERTEX_FACTORY_TYPE(FLocalVertexFactory,”/Engine/Private/LocalVertexFactory.ush”,true,true,true,true,true);

    

    现在您会注意到一些有趣的内容,没有指定入口点(根本没有该入口)!我认为实际工作的方式非常精彩(虽然会让人感到困惑):Unreal会根据所使用的顶点工厂改变数据结构和函数调用的内容,同时重用相同的名称,以便通用代码工作。

    我们将看一个例子:BasePass顶点着色器将FVertexFactoryInput作为输入。 这个数据结构在LocalVertexFactory.ush中定义,具有特定的含义。 但是,GpuSkinVertexFactory.ush也定义了这个结构! 然后,根据包含哪个头,提供给顶点着色器的数据将发生变化。这种模式在其他领域中重复使用,并将在“Shader Architecture 着色器体系结构”文章中进行更深入的介绍。

// Entry point for the base pass vertexshader. We can see that it takes a generic FVertexFactoryInput struct andoutputs a generic FBasePassVSOutput.

void Main(FVertexFactoryInput Input, outFBasePassVSOutput Output)

{

   // This is where the Vertex Shader would calculate things based on theInput and store them in the Output.

}

// LocalVertexFactory.ush implements theFVertexFactoryInput struct

struct FVertexFactoryInput

{

   float4 Position : ATTRIBUTE0;

   float3 TangentX : ATTRIBUTE1;

   float4 TangentZ : ATTRIBUTE2;

   

   float4 Color : ATTRIBUTE3;

   // etc…

}

// GpuSkinVertexFactory.ush alsoimplements the FVertexFactoryInput struct

struct FVertexFactoryInput

{

   float4 Position : ATTRIBUTE0;

   half3 TangentX : ATTRIBUTE1;

   half4 TangentZ : ATTRIBUTE2;

   uint4 BlendIndices : ATTRIBUTE3;

   uint4 BlendIndicesExtra : ATTRIBUTE14;

   // etc…

}

 

总结

    IMPLEMENT_MATERIAL_SHADER_TYPE宏定义了着色器的入口点,但顶点工厂确定了传入该顶点着色器的数据。着色器使用非特定变量名称(例如FVertexFactoryInput),这些名称对不同的顶点工厂有不同的含义。 UPrimitiveComponent / FPrimitiveSceneProxy一起工作,通过特定的数据布局从您的场景中获取数据并将其传输到GPU上。

着色器管线的一个脚注

    Unreal具有“着色器管线”的概念,它在一个管线中一起处理多个着色器(顶点,像素),以便查看输入/输出并优化它们。它们在引擎中的三个地方使用:DepthRendering,MobileTranslucentRendering和VelocityRendering。 我不能很好地理解它们,但是如果您正在使用这三种系统中的任何一种,并且在阶段之间优化语义的问题,请调查IMPLEMENT_SHADERPIPELINE_TYPE_ *。

啊,是的,不常用的VSHSDSGSPS 类型

 

 

下章

    在下一篇文章中,我们将着眼于绘图策略是什么,绘图策略工厂是什么,以及Unreal如何实际告诉GPU绘制网格。

 

 

2018-08-13 18:39:13 wangyars 阅读数 919

https://medium.com/@lordned/unreal-engine-4-rendering-part-5-shader-permutations-2b975e503dd4

翻译:yarswang 转载请保留

 

如前所述,Unreal将编译给定着色器/材质的多个排列以应对不同的用例。 修改材质时,Unreal将查找该着色器使用的 .ush/.usf 文件并重新加载它们。 然后,Unreal将你的材质图转换为HLSL代码,然后开始构建着色器的每个排列。

 

我们都熟悉每次在Unreal中使用材质编辑器的过程。不幸的是,这有一个问题:它只重新加载与材料本身相关的着色器。因为Unreal使用了延迟渲染,一些着色器是全局的(例如,对GBuffer进行采样并计算最终颜色的通道)。这意味着这些着色器实际上不是材质的一部分,因此修改该材质不会导致它们重新加载。

 

另外,一旦你更改了其中一个全局着色器并重新启动,Unreal就会重新编译每个使用该文件的着色器,所以如果你编辑一个共用着色器,你会看到一个空项目有约125个着色器/约10,000个的排列完全重新编译。对于修改渲染模块C++代码也是如此。这会对迭代时间产生可怕的影响,因为你需要重新编译所有内容并等待10分钟以查看你的更改是否有效。更糟糕的是,如果你在着色器内部修改#if X_Y区域内的某些内容,则会在重新编译的过程中遇到无法编译的排列。你可以去修复它,但修复它会使我们所已编译过的需要重新编译。

 

爽!

 

https://cdn-images-1.medium.com/max/800/1*sap7-qeesV_hrRGiN9B6eA.png
Unreal 4.19的新内容- Shader Permutation Reduction 设置

 

降低总排列数

我们的第一个目标是查看可以在引擎中或涉及材质的哪些设置可以更改,来减少需要编译的排列总数。我不一定建议你禁用这些设置,但暂时减少排列数会使你在开发着色器时加快迭代时间。

 

在基于材质的设定上,有些事项我们可以注意一下。第一个是Usage设置。

 

我们第一个目标是看改变什么设置可以降低排列的数量。这些设置看起来代表引擎中特殊的顶点工厂,如果 Automatically Set Usage in Editor 被勾选的话,它将生成该排列。禁用此功能可以让你获得更多控制权,并且可以让美术同学在分配材料方面三思而行,但可能不会为你节省太多的排列。

 

下一件基于材质的设定中,需要注意的是Quality and Static开关。因为开启静态开关,在编译时每种可能组合都需要一个新的排列,开启太多静态开关可能会导致相当多的排列。同样,这个可能不会节省你太多的排列。这是要记住,但是如果你正在尝试迭代并且你正处于一个近乎空的项目中,你可能不会有任何这些设置,并且运行时开启静态开关的额外性能增益值得根据需要使用它们。

 

现在我们来看看项目设置中哪些可以减少排列 —— 其中一些设置需要长期启用,但如果可能的话,我们可以暂时禁用它们。这需要Unreal 4.19或更高版本,编译本篇文章撰写日期之后的最新引擎源码就能够起作用。打开项目设置>渲染,有几个设置可以调整。根据你的特定项目/测试需求,你可能需要启用其中一些。如果你在此处禁用场景需要的着色器排列,你将在视窗中收到警告,比如:当尝试使用雾效却禁用Support Atmospheric Fog时,会有“PROJECT DOES NOT SUPPORT ATMOSPHERIC FOG”(工程不支持雾效)的警告出现。

 

https://cdn-images-1.medium.com/max/800/1*6gXKYpHVNigEh7mFxCyppg.png

 

光照

  1. Allow Static Lighting
    允许静态照明(注意:禁用此功能表示标记为静态的灯光不会影响你的材料。如果禁用此选项,请将它们设置为Movable进行测试)

 

减少着色器排列

  1. Support Stationary Skylight
    (注意:禁用此功能意味着标记为静态的天光对不会影响你的材质。如果禁用此功能,请将它们设置为Movable进行测试)
  2. Support Low Quality Lightmap Shader Permutations
  3. Support PointLight WholeSceneShadows
  4. Support Atmospheric Fog

 

减少移动着色器排列

  1. Support Combined Static and CSM Shadowing
  2. Support Distance Field Shadows
  3. Support Movable Directional Lights
  4. Max Movable Point Lights = 0

 

禁用所有这些后,打开ConsoleVariables.ini并添加r.ForceDebugViewModes = 2, 这会进一步减少排列的数量。这将在编辑器中禁用Buffer Visualization(用来显示GBuffer通道)但这是值得的。

 

所有这些操作使着色器排列数量从8809减少到3708,高达58%的降幅。

 

加快编译速度

你可以将CFLAG_StandardOptimization添加到编译环境中,以确保D3D编译器不会花时间优化着色器。 将设置应用到所有着色器最简单的方法是打开ShaderCompiler.cpp并向下滚动到GlobalBeginCompileShader函数。 然后只需添加一行代码:Input.Environment.CompilerFlags.Add(CFLAG_StandardOptimization);! 如果你尝试分析性能的话,那么请不要这样做,因为优化着色器很重要的。 注意:这与r.Shaders.Optimize = 0不同,后者是关于调试信息而不是代码优化。

 

仅重新编译已更改的着色器

最后,我们将研究重新编译特定着色器的技巧。 Unreal有一个不公开的控制台命令recompileshaders可以使用几个不同的参数。 请注意,这仅适用于.usf文件,因为.ush文件是仅包含在.usf文件中的标头。

 

recompileshaders changed查询FShaderTypeFShaderPipelineType以返回所有过时的着色器、工厂和着色器管线。 如果发现任何过时(由于源文件更改),则重新编译。 注释表明可以使用此命令可重新编译全局着色器。 在实践中我发现因为它最终会重新编译所有受影响的全局着色器,修改像ShadingModels.ush这样的文件最终会导致可观的数量,3-5分钟的编译。

 

recompileshaders global获取全局着色器表,清除它并强制重新编译所有全局着色器。修改像ShadingModels.ush这样的文件需要1-2分钟编译。如果你知道你正在修改的着色器是全局的而不是材质着色器,那么也许有用。

 

recompileshaders material<MaterialName>将重新编译找到该名称的第一个材质。不应包括路径信息。这是通过在材质上调用PreEditChange/PostEditChange来实现的,因此它与在材质编辑器中点击”Apply”按钮没有什么不同。

 

recompileshaders all都将重新编译所有内容。这就像修改所有文件然后重新启动编辑器一样。在更改结束时可能有用,以确保没有排列被破坏。可以与Project Settings > Rendering Overrides (Local) > Force All Shader Permutation Support,以确保在每个#if变体下确实编译了代码。

 

recompileshaders <path>将尝试通过shadertype或Shader Platform类型重新编译着色器。这允许我们指定特定的着色器文件路径,例如recompileshaders /Engine/Private/BasePassPixelShader.usf

 

重要说明:该命令大小写敏感,无论您使用哪个命令,都会运行一段时间;错误的使用情况(不正确的文件路径或文件名)会输出错误信息到控制台而不会重新编译着色器。

 

使用此命令后,打开新的地图和材质(“材质编辑器”预览窗口)可能会不稳定,因此如果要打开其他材质或场景,请确保您的测试环境已设置好,或者不介意崩溃。

 

它也只适用于场景中被显式使用的代码段。如果您修改仅适用于亮度半透明的部分但在场景中没有半透明对象,则热重载将不会检查该代码且不会有编译错误。

 

抛开这些警告,这些命令非常有用,因为它运行只需要3-5秒,而不是3-5分钟!我最常运行的两个命令是

recompileshaders /Engine/Private/BasePassPixelShaders.usf

recompileshaders /Engine/Private/DeferredLightPixelShaders.usf,这两个命令涵盖了你可以在基本通道中做的大多数更改。

常见的#if定义

下面是在延迟着色管道中找到的一些常见的预处理器定义。 我根据C++ 代码触发它们的方式列出了它们的预期含义。 这将有助于你确定延迟管线的哪些部分适用于你的代码,因为你可以根据此列表检查预处理器定义,以查看它被转换成UE4的哪些设置。

  1. #if NON_DIRECTIONAL_DIRECT_LIGHTING
    这可以在DeferredLightingCommon.ush中找到,但只在ForwardLightingCommon.ush中被定义为#define NON_DIRECTIONAL_DIRECT_LIGHTING(TRANSLUCENCY_LIGHTING_VOLUMETRIC_NONDIRECTIONAL || TRANSLUCENCY_LIGHTING_VOLUMETRIC_PERVERTEX_NONDIRECTIONAL)
  2. #if SUPPORT_CONTACT_SHADOWS
    支持Unreal的Contact Shadows功能。
  3. #if REFERENCE_QUALITY
    DeferredLightingCommon.ush的顶部定义为0 - 可能是用于电影渲染?
  4. #if ALLOW_STATIC_LIGHTING
    如果r.AllowStaticLighting控制台变量设置为1,则值为true。这与静态照明支持的 Project Settings > Rendering 匹配。
  5. #if USE_DEVELOPMENT_SHADERS
    如果COMPILE_SHADERS_FOR_DEVELOPMENT为真(且平台支持),则为真。如果设置了r.CompileShadersForDevelopment,则COMPILE_SHADERS_FOR_DEVELOPMENT为true。
  6. #if TRANSLUCENT_SELF_SHADOWING
    是为使用FSelfShadowedTranslucencyPolicy渲染的对象定义的。我相信这是针对Lit Translucency的支持。
  7. #if SIMPLE_FORWARD_DIRECTIONAL_LIGHT
    #if SIMPLE_FORWARD_SHADING
    似乎是在光照贴图渲染期间为静止定向光设置的。
  8. #if FORWARD_SHADING
    r.ForwardShading设置为1时,被设置。

 

综述

我们了解到,我们可以通过更改一些项目设置来减少着色器排列的总数(即使只是暂时的),这样可以更快地完全重新编译。 使用未列出的recompileshader控制台命令可以避免所有着色器重新编译,但下一个编辑器启动仍将需要完全重新编译。 我们还了解了一些常见预处理器定义的条件,使我们能够更好地了解哪些代码与我们的兴趣相关。

 

下章

终于我们要进入教程章节了! 下一篇文章将介绍修改现有着色器管道以添加新的着色模型,该模型在遵循PBR规则的同时提供类似卡通的效果。 这篇文章的技术内容主要来自FelixK的开发博客,但更新至4.18+版本,并对我们正在改变的内容进行了更多解释 - 特别是现在我们知道延时通道的工作原理了!

2018-10-24 21:48:00 u012614151 阅读数 47

@author: 白袍小道

转载悄悄说明下

随缘查看,施主开心就好

   

说明:

本篇继续Unreal搬山部分的渲染模块的Shader部分,

主要牵扯模块RenderCore, ShaderCore, RHI, Materia.

可能分成N篇。

(这里放入的UE的模块框)

(下一篇主要是UE灯光和着色简要[ush以及对应结构,和UE代码和DX部分],然后是巴拉巴拉)

   

前言:

部分算法和流程的实现原理,和细节(往往这部分会成为优化的处理口)。

梳理UEShader的结构,底层的接入,分层。

UE着色使用和细节部分。

对比反观差距和原因

   

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

第一部分、渲染管线和着色

Shading Models

描述物体的颜色如何根据表面等因素而变化 (例如方向,视图方向,和照明)

1、通常具有控制外观变化的属性。

2、控制这些变化的属性而决定了变化。

   

评估

设计着色实现时,需要根据计算结果进行划分他们的评估频率。

首先,确定一个给定的结果在整个绘制调用中,计算总是不变的。在这种情况下,计算应用程序可以通过GPU计算(通常在CPU上)执行吗。着色器可以用于特别昂贵的计算。结果被传递给图形API通过统一着色器输入。

   

说明

1、虚拟机

a\ 目前的着色语言都是 C-likelike 的着色语言,比如 HLSL,CG 和 GLSL GLSL,其被编译成独立于机器的汇语言 语言,也称为中间( IL )。

这些汇编语言在单独的阶段,通常是驱动中被转化成实际机器 )。

这些汇编语言在单独的阶段,通常是驱动中被转化成实际机器 语言。这样的安排可以兼容不同硬件实现些汇编被看做是定义一个作为着色译器.

b\ 着色语言虚拟机可以理解为一个处多种类型寄存器 和数据源、预编了一系列指令的处理器。

2、单指令多数据( SIMD), 寄存器.

a\ 一次DrawCall会通过API做一次(一系列)的图元绘制,从而驱使图形管线运行。[如Draw,DrawInstance, …..]

b\ 输入,输出和交接。

c\ Shader的语句、加载和编译。

DX10 虚拟机

  

   

语法树

划分

1、顶点着色

发生在图元装配后

顶点着色器(VS)阶段处理来自输入汇编器的顶点,执行每个顶点的操作,如转换、蒙皮、变形和每个顶点的光照,并编制到输出结构交付给下一个管道。

(顶点着色器总是操作单个输入顶点并产生单个输出顶点。顶点着色器阶段必须始终处于活动状态,以便管道执行。

如果不需要顶点修改或转换,则必须创建一个直通顶点着色器并将其设置为管道。)

   

a\ 输入和输出:

每个顶点着色器输入顶点可以由16个32位向量组成(每个顶点最多4个分量),每个输出顶点可以由16个32位4分量向量组成。

所有顶点着色器都必须至少有一个输入和一个输出,可以是一个标量值。

 

b\ vertexshader阶段可以使用输入汇编程序生成的两个系统值:VertexID和InstanceID(参见系统值和语义)。

由于VertexID和InstanceID在顶点级别都是有意义的,并且硬件生成的ID只能被输入到理解它们的第一阶段,因此这些ID值只能被输入到vertexshader阶段。

 

c\ 顶点着色器总是在所有顶点上运行,包括带有邻接的输入基元拓扑中的相邻顶点。

顶点着色器执行的次数可以使用VSInvocations管道统计从CPU查询。

   

   

2、可选镶嵌

描述:

Direct3D 11运行时支持三个实现镶嵌的新阶段,它将低细节细分曲面转换为GPU上的高细节原语。

镶嵌瓷砖(或分解)高阶表面成适合渲染的结构。

通过在硬件中实现镶嵌,图形管道可以评估较低的细节(较低的多边形计数)模型和渲染较高的细节。

虽然可以进行软件镶嵌,但是由硬件实现的镶嵌可以生成大量的视觉细节(包括对位移映射的支持),而不需要向模型大小添加视觉细节和刷新率。

   

概括:镶嵌使用GPU计算更详细的表面从一个由四边形斑块,三角形斑块或等值线构成的表面。

   

HullShader:

一个可编程的着色器阶段,它产生一个几何贴片(和贴片常量),对应于每个输入贴片(四边形、三角形或直线).

外壳着色器(每个补加点调用一次)将定义低阶表面的输入控制点转换为构成补加的控制点。

ID3D11Device::

CreateHullShader

   

ID3D11DeviceContext::HSSetShader

  

  

Tessellator

一个固定的函数管道阶段,它创建表示几何patch的域的抽样模式,并生成一组较小的对象(三角形、点或线)来连接这些样本

将一个域(四边形、三边形或直线)细分为许多较小的对象(三角形、点或直线)。将uv(和可选的w)坐标和表面拓扑(归一化的坐标域)输出到域着色器阶段。

域着色器在每个tessellator阶段点调用一次,并计算表面位置。

  

Domain

一个可编程着色器阶段,计算对应于每个域样本的顶点位置

域着色器计算输出补丁中细分点的顶点位置,其中每个tesselator阶段输出点运行一次,并且对tesselators阶段输出UV坐标、船体着色器输出补丁和船体着色器输出补删常量具有只读访问权限。

ID3D11Device::

CreateDomainShader

  

  

 

   

   

3Geometry Shader

几何着色器(GS)阶段运行应用程序指定的着色器代码,将顶点作为输入,并能够在输出上生成顶点。

(不像顶点着色器操作单个顶点,几何着色器的输入是一个完整语义的顶点(两个顶点表示线,三个顶点表示三角形,或者单个顶点表示点)。

几何体着色器还可以将边缘相邻基元的顶点数据作为输入(对于一条直线,额外的两个顶点,对于三角形,额外的三个顶点)。

下图显示了一个三角形和一条有相邻顶点的直线。)

   

a\ 可以使用由IA自动生成的SV_PrimitiveID系统生成的值。

b\ 能够输出多个顶点,形成单个选定的拓扑(可用的GS阶段输出拓扑有:tristrip、linestrip和pointlist)。

c\ 输出可以通过流输出阶段反馈到光栅化阶段和/或内存中的顶点缓冲区。提供给内存的输出被扩展到单独的点/线/三角形列表(它们将被传递给光栅化器)。

   

4Pixel Shader

像素着色器阶段(PS)支持丰富的着色技术,例如逐像素光照和后期处理。

像素着色器是一个程序,它结合了常量变量、纹理数据、每个顶点值的插值和其他数据来产生每个像素的输出

对于一个原语覆盖的每个像素,rasterizer阶段只调用一次像素着色器,但是,可以指定一个空着色器来避免运行一个着色器。

   

a\ 当对纹理进行多采样时,每个覆盖的像素只调用一次像素着色器,同时对每个覆盖的多采样进行深度/模板测试。通过深度/模版测试的样本将使用像素着色器输出颜色进行更新。(所以适当减少)

   

b\ 像素着色器固有函数产生或使用屏幕空间x和y的数量导数,导数最常用的用途是计算纹理采样的详细程度计算,在各向异性滤波的情况下,选择沿各向异性轴的样本。

通常,硬件实现同时在多个像素(例如2x2网格)上运行一个像素着色器,以便在像素着色器中计算的量的导数可以合理地近似为相邻像素中同一执行点上的值的增量。

   

c\ 输入:

输入将被限制(超过会截取)在16(无配置Geo着色)or 32、 32位、4个组件。

关键词:顶点属性,插值,覆盖,抽样,

   

d\ 输出

像素着色器最多可以输出8 32位4分量的颜色,如果像素被丢弃,则不输出颜色。像素着色器输出寄存器组件在使用之前必须声明;每个寄存器都允许有一个不同的输出-写入掩码。

使用depth-write-enable状态(在输出合并阶段)来控制是否将深度数据写入深度缓冲区(或使用丢弃指令丢弃该像素的数据)。

像素着色器还可以输出可选的32位、1个组件、浮点数、深度值来进行深度测试(使用SV_Depth语义)。

深度值在深度寄存器中输出,并替换深度测试的插值深度值(假设启用深度测试)。无法在使用固定函数深度和着色器深度之间动态变化。

像素着色器不能输出模板值。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

第二部分、Unreal 着色器相关

DX

1、编译,读写

D3DCompileFromFile(_In_ LPCWSTR pFileName,

_In_reads_opt_(_Inexpressible_(pDefines->Name != NULL)) CONST D3D_SHADER_MACRO* pDefines,

_In_opt_ ID3DInclude* pInclude,

_In_ LPCSTR pEntrypoint,

_In_ LPCSTR pTarget,

_In_ UINT Flags1,

_In_ UINT Flags2,

_Out_ ID3DBlob** ppCode,

_Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorMsgs);

D3DCompile(_In_reads_bytes_(SrcDataSize) LPCVOID pSrcData,

_In_ SIZE_T SrcDataSize,

_In_opt_ LPCSTR pSourceName,

_In_reads_opt_(_Inexpressible_(pDefines->Name != NULL)) CONST D3D_SHADER_MACRO* pDefines,

_In_opt_ ID3DInclude* pInclude,

_In_opt_ LPCSTR pEntrypoint,

_In_ LPCSTR pTarget,

_In_ UINT Flags1,

_In_ UINT Flags2,

_Out_ ID3DBlob** ppCode,

_Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorMsgs);

   

D3DCompressShaders(_In_ UINT uNumShaders,

_In_reads_(uNumShaders) D3D_SHADER_DATA* pShaderData,

_In_ UINT uFlags,

_Out_ ID3DBlob** ppCompressedData)

   

D3DReadFileToBlob(_In_ LPCWSTR pFileName,_Out_ ID3DBlob** ppContents)

D3DWriteBlobToFile(_In_ ID3DBlob* pBlob,_In_ LPCWSTR pFileName, _In_ BOOL bOverwrite)

   

2、创建

CreateVertexShader(

_In_reads_(BytecodeLength) const void *pShaderBytecode,

_In_ SIZE_T BytecodeLength,

_In_opt_ ID3D11ClassLinkage *pClassLinkage,

_COM_Outptr_opt_ ID3D11VertexShader **ppVertexShader)

CreateXXXShader类似

   

Unreal

UnrealShader处理包括了:ush的包含,编译得到转储的着色器usf,平台编译器编译,创建和使用(绑定)。

1、Global Shaders

全局shader是用于操作固定几何体(如:full screen quad)的shader,并且它不需要与materials产生交互,例如:阴影过滤、后处理。

这种类型的shader在内存中只有一份实例.

   

2Materia

材质由一些控制材质如何渲染的状态和一些控制材质如何与渲染Pass shader交互的材质输入(material inputs)组成。

   

3、Vertex Factories 和 MeshTypes

Material必须能够赋给各种类型的Mesh Types, 这个是通过和Vertex Factories 配合完成的。 一个FVertexFactoryType表示唯一的Mesh Type。

一个FVertexFactory的实例存有每个几何体的数据,以便支持特定的Mesh Type。

   

引擎内Shader位于:Engine\Shaders\Private(Public)

(详细分析和说明在下一篇)

使用部分

   

   

ShaderCore

FShader、FGlobalShader、

FShaderType, FVertexFactory

FShaderPipeline, FShaderTarget

   

   

   

   

   

材质的编译

   

过程说明:

1、生成材质的Shader代码【只是Shader文件】

2、构建编译环境,且添加相关文件路径

3、编译[可以异步],成功就缓存起来

3.1 创建了一个临时引用计数指针[主要是确保操作一个引用的着色器映射[或查找]将导致这个着色器映射被删除]

3.2 往ShaderMapsBeingCompiled添加[占个位置]

3.3 分配唯一编译ID,配置编译环境

3.4 迭代处理所有顶点工厂的类型,编译这个材质和顶点工厂类型组合的所有网格材质着色器。---------FVertexFactoryType

3.5 迭代处理所有材质Shader的类型 ------FShaderType

3.6 迭代处理所有材质Shader管线的类型 ------FShaderPipelineType

3.7 进入映射编译着色器

   

类关系:

   

   

2017-05-09 17:25:11 u013131744 阅读数 4415

内容:

       *概述

       *材料特性

       *基本颜色

       *不透明度

       *法线的设置

       *波浪高度和频率

       *折射

       *常数

       *材质实例

 

概述

本教程由52VR翻译自unrealengine官方,本教程将教您如何创建半透明的水,举例讲解着色器的使用,并已更新为能兼容4.13.1版本的引擎,在开始之前,先使用水着色器下载测试项目,然后就可以将相关的基本数据快速添加到项目中,或者你也可以重新自己下载所需的材料。

水着色器示例项目http://pan.baidu.com/s/1pL9Erbl

 

材料特性

以下是用于定义材料属性的操作,根据相关的说明及图片做好这些设置。

 

基本颜色

使用两个Vector3节点来定义深水色和浅水色。然后使用可由指数参数控制的菲涅尔,去控制暗色和浅色之间切换。为了增加颜色的效果,您可以添加“漫反射”参数。

 

不透明度

关于不透明度,我们使用Lerp渐变节点下的相应接口来驱动整体不透明度,使用场景深度来驱动褪色的距离。可以用它来创建浅水颜色变化的效果。

 

 法线的设置

法线的设置主要是定义波浪的大小和速度。小型波形的panner坐标——速度X设置为0.03,速度Y设置为-0.02。大波浪的panner坐标——速度X设置为-0.1,速度Y设置为0.1。设置好这些值后,法线就会产生平移,形成一种波纹般的效果。

 

波浪高度和频率

您可自己选择是否设置这个功能,这部分主要是使用世界位置偏移节点来设置水流的上下波动。这可以为水添加更加动感的效果,比如波浪拍打岸边时的上升和下降。

 

折射

在这里,我们使用折射率(折光指数)来设置折射效果。折射率光在真空中的传播速度与在某介质中传播速度之比。

 

常数

要完成这个效果,还需连接好以下几项。这些简单的常量将被转换为用于编辑材质实体的参数。

 

材质实例

以下是本教程开头的链接中提供的项目的示例。主材料已被实质化并应用于平面,然后使用标量参数使效果更好。

 

  以上就是相关教程!(本文由52VR开发网编辑白犀牛翻译,由UE技术工程师姜春雨审核校对)

   ( 转载请注明出处和明确的回链地址,否则保留追究责任)