前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UE4/5 usf、ush UniformBuffer的黏合过程

UE4/5 usf、ush UniformBuffer的黏合过程

作者头像
Bairuo
发布2023-03-13 13:16:17
9160
发布2023-03-13 13:16:17
举报
文章被收录于专栏:Bairuo的文章Bairuo的文章

已有文章中简单提到过,这里再以另外的视角更详细地描述整个过程

UE4 HLSL 和 Shader 开发指南和技巧

第一次写UE shader大概都会疑惑,usf、ush里面的那些“View“、“BasePass”Uniform变量到底是哪来的?好像没有include怎么直接写一个字符串就可以了,或者莫名其妙的include怎么也找不到的/Engine/Generated/UniformBuffers/xxx,还有为啥c++定义了一下它们就能绑定上?这些到底是怎么魔法般运行的?

我们知道每个FShader类会有一个static的ShaderType描述这个Shader类型

类似的,每个UniformBuffer也会有一个static的FShaderParametersMetadata来描述这个Parameter类型,通过BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT宏定义

代码语言:javascript
复制
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FMobileBasePassUniformParameters, )
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT_WITH_CONSTRUCTOR(FViewUniformShaderParameters, ENGINE_API)

然后通过

代码语言:javascript
复制
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FMobileBasePassUniformParameters, "MobileBasePass");

去初始化这个FShaderParametersMetadata,在FShaderParametersMetadata的构造函数中会将自己加入的一个全局的链表中

声明成员时通过宏前后串联

最终在构造时通过GetMembers进入FShaderParametersMetadata的Members中

引擎启动PreInitPreStartupScene阶段会收集所有的UniformBuffer信息

大概步骤是

1.BuildShaderFileToUniformBufferMap

搜集所有ShaderType(FVertexFactoryType和FShaderType和MaterialTemplate.ush等部分特殊文件)的Shader路径(包括Include文件),字符串搜索的方式查找每个文件中用到了哪些UniformBuffer名称,建立一个usf、ush路径名到UniformBuffer名的映射关系

2.ShaderType::Initialize

再次遍历,构造每个ShaderType所引用的UniformBuffer(包括Include文件),存储在Type->ReferencedUniformBufferStructsCache中

注意以上计算Include文件时会忽略Generated路径

FCachedUniformBufferDeclaration实际上是一个由UniformBuffer名称到其声明(Declaration成员)的TMap

代码语言:javascript
复制
TMap<const TCHAR*, FCachedUniformBufferDeclaration> ReferencedUniformBufferStructsCache;

启动阶段收集了每个ShaderType所使用的UniformBuffer(TMap的Key)名称,它们的声明会在必要时(一般是材质编译时)生成,同样也是字符串

这对应最终shader中的内容

至于为什么会是注释,这里后面还会再提

得到Declaration后,在编译前会将Declaration加入到Environment成员IncludeVirtualPathToExternalContentsMap中

这里是Path映射到Contents,虚拟路径直接映射到内容,所以找不到真实文件是正常的

这里不详细介绍Shader编译的其它内容,简而言之,组装好信息后传给Compile模块(Worker或Block单线程编译)的是一个Input,其中主要也就包括FShaderCompilerEnvironment

以D3DCompile为例,在进入CompileD3DShader函数之后主要工作是根据Input信息合成PreprocessedShaderSource文本

前面的VirtualPath映射会在这里查找转换合成文本内容(mcpp会遍历包含头文件)

最终传给编译器的是一个合并了所有内容的字符串,长度,文件名,入口点等信息

值得一提的是,在提交给编译器前会对合并后的文本进行一个RemoveUniformBuffersFromSource的操作(UE5 SM6除外)

如UE注释所述,它会将所有类似View.WorldToClip的写法替换成View_WorldToClip,并将原先的Struct声明注释掉

这个替换方法是先得到View的成员比如WorldToClip再去搜索使用到View.WorldToClip的地方

所以如果不小心使用了一个Uniform结构不存在的成员,那么它不会进行替换,最终的编译报错同样会是 undeclared identifier xxx(结构名),这在不留意的时候可能会非常令人迷惑

以上过程UE4和UE5大同小异,除了个别类成员的名称有些许变化外整体逻辑差不多是一致的

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-03-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档