From 942794934ab1ea6fb59369d5daad95c6b1ac0f41 Mon Sep 17 00:00:00 2001 From: BlueRose <378100977@qq.com> Date: Tue, 6 Feb 2024 21:26:28 +0800 Subject: [PATCH] vault backup: 2024-02-06 21:26:28 --- .obsidian/community-plugins.json | 6 +- .../plugins/various-complements/data.json | 8 + .../剖析虚幻渲染体系(08)- Shader体系.md | 872 +++++++++++++++++- .../剖析虚幻渲染体系(09)- 材质体系.md | 4 +- 4 files changed, 884 insertions(+), 6 deletions(-) diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json index 656dfaa..e36c103 100644 --- a/.obsidian/community-plugins.json +++ b/.obsidian/community-plugins.json @@ -29,10 +29,10 @@ "obsidian-hover-editor", "obsidian-admonition", "workspaces-plus", - "Enhanced-editing", "obsidian42-brat", "remotely-save", - "table-editor-obsidian", "dataview", - "obsidian-view-mode-by-frontmatter" + "obsidian-view-mode-by-frontmatter", + "Enhanced-editing", + "table-editor-obsidian" ] \ No newline at end of file diff --git a/.obsidian/plugins/various-complements/data.json b/.obsidian/plugins/various-complements/data.json index 5373190..91de3a3 100644 --- a/.obsidian/plugins/various-complements/data.json +++ b/.obsidian/plugins/various-complements/data.json @@ -126,6 +126,14 @@ "lastUpdated": 1707140527615 } } + }, + "VertexBufferSRV": { + "VertexBufferSRV": { + "currentFile": { + "count": 1, + "lastUpdated": 1707223827335 + } + } } } } \ No newline at end of file diff --git a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md index bea883d..deff2c3 100644 --- a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md +++ b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md @@ -8,7 +8,6 @@ rating: ⭐ # 前言 原文地址:https://www.cnblogs.com/timlly/p/15092257.html - # FShader ```c++ class FShader @@ -893,4 +892,873 @@ IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLocalVertexFactory, SF_Vertex, FLocalVe // 实现FLocalVertexFactory. IMPLEMENT_VERTEX_FACTORY_TYPE_EX(FLocalVertexFactory,"/Engine/Private/LocalVertexFactory.ush",true,true,true,true,true,true,true); -``` \ No newline at end of file +``` +下面进入CableComponent相关类型关于FLocalVertexFactory的使用: +```c++ +// Engine\Plugins\Runtime\CableComponent\Source\CableComponent\Private\CableComponent.cpp + +class FCableSceneProxy final : public FPrimitiveSceneProxy +{ +public: + FCableSceneProxy(UCableComponent* Component) + : FPrimitiveSceneProxy(Component) + , Material(NULL) + // 构造顶点工厂. + , VertexFactory(GetScene().GetFeatureLevel(), "FCableSceneProxy") + (......) + { + // 利用顶点工厂初始化缓冲区. + VertexBuffers.InitWithDummyData(&VertexFactory, GetRequiredVertexCount()); + (......) + } + + virtual ~FCableSceneProxy() + { + // 释放顶点工厂. + VertexFactory.ReleaseResource(); + (......) + } + + // 构建Cable网格. + void BuildCableMesh(const TArray& InPoints, TArray& OutVertices, TArray& OutIndices) + { + (......) + } + + // 设置动态数据(渲染线程调用) + void SetDynamicData_RenderThread(FCableDynamicData* NewDynamicData) + { + // 释放旧数据. + if(DynamicData) + { + delete DynamicData; + DynamicData = NULL; + } + DynamicData = NewDynamicData; + + // 从Cable点构建顶点. + TArray Vertices; + TArray Indices; + BuildCableMesh(NewDynamicData->CablePoints, Vertices, Indices); + + // 填充顶点缓冲区数据. + for (int i = 0; i < Vertices.Num(); i++) + { + const FDynamicMeshVertex& Vertex = Vertices[i]; + + VertexBuffers.PositionVertexBuffer.VertexPosition(i) = Vertex.Position; + VertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(i, Vertex.TangentX.ToFVector(), Vertex.GetTangentY(), Vertex.TangentZ.ToFVector()); + VertexBuffers.StaticMeshVertexBuffer.SetVertexUV(i, 0, Vertex.TextureCoordinate[0]); + VertexBuffers.ColorVertexBuffer.VertexColor(i) = Vertex.Color; + } + + // 更新顶点缓冲区数据到RHI. + { + auto& VertexBuffer = VertexBuffers.PositionVertexBuffer; + void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetNumVertices() * VertexBuffer.GetStride(), RLM_WriteOnly); + FMemory::Memcpy(VertexBufferData, VertexBuffer.GetVertexData(), VertexBuffer.GetNumVertices() * VertexBuffer.GetStride()); + RHIUnlockVertexBuffer(VertexBuffer.VertexBufferRHI); + } + + (......) + } + + virtual void GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override + { + (......) + + for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) + { + if (VisibilityMap & (1 << ViewIndex)) + { + const FSceneView* View = Views[ViewIndex]; + + // 构造FMeshBatch实例. + FMeshBatch& Mesh = Collector.AllocateMesh(); + // 将顶点工厂实例传给FMeshBatch实例. + Mesh.VertexFactory = &VertexFactory; + + (......) + + Collector.AddMesh(ViewIndex, Mesh); + } + } + } + + (......) + +private: + // 材质 + UMaterialInterface* Material; + // 顶点和索引缓冲. + FStaticMeshVertexBuffers VertexBuffers; + FCableIndexBuffer IndexBuffer; + // 顶点工厂. + FLocalVertexFactory VertexFactory; + // 动态数据. + FCableDynamicData* DynamicData; + + (......) +}; +``` + +主要步骤: +1. 在构造函数中初始化LocalVertexFactory成员变量。 +2. SetDynamicData_RenderThread() + 1. 通过控制点构建CableMesh顶点数据。 + 2. 填充顶点缓冲区数据。FStaticMeshVertexBuffers VertexBuffers + 1. PositionVertexBuffer.VertexPosition + 2. StaticMeshVertexBuffer.SetVertexTangents + 3. StaticMeshVertexBuffer.SetVertexUV + 4. ColorVertexBuffer.VertexColor + 3. 更新IndexBufferData到RHI。 +3. GetDynamicMeshElements():将顶点工厂实例传给FMeshBatch实例 + 1. FMeshBatch& Mesh = Collector.AllocateMesh(); + 2. Mesh.VertexFactory = &VertexFactory; + 3. Collector.AddMesh(ViewIndex, Mesh); + +不过,无论是使用已有的还是自定义的顶点工厂,顶点工厂的顶点声明的顺序、类型、组件数量和插槽需要和HLSL层的FVertexFactoryInput保持一致。比如说FLocalVertexFactory::InitRHI的顶点声明顺序是位置、切线、颜色、纹理坐标、光照图,那么我们进入FLocalVertexFactory对应的HLSL文件(由IMPLEMENT_VERTEX_FACTORY_TYPE等宏指定)看看: +```c++ +// Engine\Shaders\Private\LocalVertexFactory.ush +// 局部顶点工厂对应的输入结构体. +struct FVertexFactoryInput +{ + // 位置 + float4 Position : ATTRIBUTE0; + + // 切线和颜色 +#if !MANUAL_VERTEX_FETCH + #if METAL_PROFILE + float3 TangentX : ATTRIBUTE1; + // TangentZ.w contains sign of tangent basis determinant + float4 TangentZ : ATTRIBUTE2; + + float4 Color : ATTRIBUTE3; + #else + half3 TangentX : ATTRIBUTE1; + // TangentZ.w contains sign of tangent basis determinant + half4 TangentZ : ATTRIBUTE2; + + half4 Color : ATTRIBUTE3; + #endif +#endif + + // 纹理坐标 +#if NUM_MATERIAL_TEXCOORDS_VERTEX + #if !MANUAL_VERTEX_FETCH + #if GPUSKIN_PASS_THROUGH + // These must match GPUSkinVertexFactory.usf + float2 TexCoords[NUM_MATERIAL_TEXCOORDS_VERTEX] : ATTRIBUTE4; + #if NUM_MATERIAL_TEXCOORDS_VERTEX > 4 + #error Too many texture coordinate sets defined on GPUSkin vertex input. Max: 4. + #endif + #else + #if NUM_MATERIAL_TEXCOORDS_VERTEX > 1 + float4 PackedTexCoords4[NUM_MATERIAL_TEXCOORDS_VERTEX/2] : ATTRIBUTE4; + #endif + #if NUM_MATERIAL_TEXCOORDS_VERTEX == 1 + float2 PackedTexCoords2 : ATTRIBUTE4; + #elif NUM_MATERIAL_TEXCOORDS_VERTEX == 3 + float2 PackedTexCoords2 : ATTRIBUTE5; + #elif NUM_MATERIAL_TEXCOORDS_VERTEX == 5 + float2 PackedTexCoords2 : ATTRIBUTE6; + #elif NUM_MATERIAL_TEXCOORDS_VERTEX == 7 + float2 PackedTexCoords2 : ATTRIBUTE7; + #endif + #endif + #endif +#elif USE_PARTICLE_SUBUVS && !MANUAL_VERTEX_FETCH + float2 TexCoords[1] : ATTRIBUTE4; +#endif + (......) +}; +``` +因此可知,FVertexFactoryInput结构体的数据顺序和FLocalVertexFactory的顶点声明是一一对应的。 + +# ShaderMap +ShaderMap的作用是**存储编译后的shader代码**,分为FGlobalShaderMap、FMaterialShaderMap、FMeshMaterialShaderMap三种类型。 + +- FGlobalShaderMap:FGlobalShaderMap保存并管理着所有编译好的FGlobalShader代码。(**没有材质和顶点工程的**) +- FMaterialShaderMap:存储和管理着一组FMaterialShader实例的对象。(**额外关联一个材质和一个顶点工厂**) + - 因此可以找到,每个FMaterial都有一个FMaterialShaderMap(游戏线程一个,渲染线程一个),如果要获取FMaterial的指定类型的Shader,就需要从该FMaterial的FMaterialShaderMap实例中获取,从而完成了它们之间的链接。 +- FMeshMaterialShaderMap:存储和管理FMeshMaterialShader。 + +### 编译相关代码 +如果需要了解编译过程可以查看`RecompileShaders`命令。 +```c++ +// Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp +// 引擎预初始化. +int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine) +{ + (......) + + // 是否开启shader编译, 一般情况下都会开启. + bool bEnableShaderCompile = !FParse::Param(FCommandLine::Get(), TEXT("NoShaderCompile")); + + (......) + + if (bEnableShaderCompile && !IsRunningDedicatedServer() && !bIsCook) + { + (......) + + // 编译GlobalShaderMap + CompileGlobalShaderMap(false); + + (......) + } + + (......) +} + +// Engine\Source\Runtime\Engine\Private\ShaderCompiler\ShaderCompiler.cpp +void CompileGlobalShaderMap(EShaderPlatform Platform, const ITargetPlatform* TargetPlatform, bool bRefreshShaderMap) +{ + (......) + + // 如果对应平台的GlobalShaderMap未创建, 则创建之. + if (!GGlobalShaderMap[Platform]) + { + (......) + + // 创建对应平台的FGlobalShaderMap. + GGlobalShaderMap[Platform] = new FGlobalShaderMap(Platform); + + // Cooked模式. + if (FPlatformProperties::RequiresCookedData()) + { + (......) + } + // Uncooked模式 + else + { + // FGlobalShaderMap的id. + FGlobalShaderMapId ShaderMapId(Platform); + + const int32 ShaderFilenameNum = ShaderMapId.GetShaderFilenameToDependeciesMap().Num(); + const float ProgressStep = 25.0f / ShaderFilenameNum; + + TArray AsyncDDCRequestHandles; + AsyncDDCRequestHandles.SetNum(ShaderFilenameNum); + + int32 HandleIndex = 0; + + // 提交DDC请求. + for (const auto& ShaderFilenameDependencies : ShaderMapId.GetShaderFilenameToDependeciesMap()) + { + SlowTask.EnterProgressFrame(ProgressStep); + + const FString DataKey = GetGlobalShaderMapKeyString(ShaderMapId, Platform, TargetPlatform, ShaderFilenameDependencies.Value); + + AsyncDDCRequestHandles[HandleIndex] = GetDerivedDataCacheRef().GetAsynchronous(*DataKey, TEXT("GlobalShaderMap"_SV)); + + ++HandleIndex; + } + + // 处理已经结束的DDC请求. + TArray CachedData; + HandleIndex = 0; + for (const auto& ShaderFilenameDependencies : ShaderMapId.GetShaderFilenameToDependeciesMap()) + { + SlowTask.EnterProgressFrame(ProgressStep); + CachedData.Reset(); + + GetDerivedDataCacheRef().WaitAsynchronousCompletion(AsyncDDCRequestHandles[HandleIndex]); + if (GetDerivedDataCacheRef().GetAsynchronousResults(AsyncDDCRequestHandles[HandleIndex], CachedData)) + { + FMemoryReader MemoryReader(CachedData); + GGlobalShaderMap[Platform]->AddSection(FGlobalShaderMapSection::CreateFromArchive(MemoryReader)); + } + else + { + // 没有在DDC中找到, 忽略之. + } + + ++HandleIndex; + } + } + + // 如果有shader没有被加载, 编译之. + VerifyGlobalShaders(Platform, bLoadedFromCacheFile); + + // 创建所有着色器. + if (GCreateShadersOnLoad && Platform == GMaxRHIShaderPlatform) + { + GGlobalShaderMap[Platform]->BeginCreateAllShaders(); + } + } +} +``` + +# Shader调试 +修改`Engine\Config\ConsoleVariables.ini`配置 +- r.ShaderDevelopmentMode=1 获得关于着色器编译的详细日志和错误重试的机会。 +- r.DumpShaderDebugInfo=1 将编译的所有着色器的文件保存到磁盘ProjectName/Saved/ShaderDebugInfo的目录。包含源文件、预处理后的版本、一个批处理文件(用于使用编译器等效的命令行选项来编译预处理版本)。 +- r.DumpShaderDebugShortNames=1 保存的Shader路径将被精简。 +- r.Shaders.Optimize=0 禁用着色器优化,使得shader的调试信息被保留。 +- r.Shaders.KeepDebugInfo=1 保留调试信息,配合RenderDoc等截帧工具时特别有用。 +- r.Shaders.SkipCompression=1 忽略shader压缩,可以节省调试shader的时间。 + +另外,如果修改了Shader的某些文件(如BasePassPixelShader.ush),不需要重启UE编辑器,可以在控制台输入`RecompileShaders`命令重新编译指定的shader文件。其中`RecompileShaders`的具体含义如下: +- RecompileShaders all 编译源码有修改的所有shader,包含global、material、meshmaterial。 +- RecompileShaders changed 编译源码有修改的shader。 +- RecompileShaders global 编译源码有修改的global shader。 +- RecompileShaders material 编译源码有修改的material shader。 +- RecompileShaders material 编译指定名称的材质。 +- RecompileShaders 编译指定路径的shader源文件。 + +# 案例 +新增加FVertexFactory子类的过程如下: +```c++ +// FMyVertexFactory.h + +// 声明顶点工厂着色器参数. +BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FMyVertexFactoryParameters, ) + SHADER_PARAMETER(FVector4, Color) +END_GLOBAL_SHADER_PARAMETER_STRUCT() + +// 声明类型. +typedef TUniformBufferRef FMyVertexFactoryBufferRef; + +// 索引缓冲. +class FMyMeshIndexBuffer : public FIndexBuffer +{ +public: + FMyMeshIndexBuffer(int32 InNumQuadsPerSide) : NumQuadsPerSide(InNumQuadsPerSide) {} + + void InitRHI() override + { + if (NumQuadsPerSide < 256) + { + IndexBufferRHI = CreateIndexBuffer(); + } + else + { + IndexBufferRHI = CreateIndexBuffer(); + } + } + + int32 GetIndexCount() const { return NumIndices; }; + +private: + template + FIndexBufferRHIRef CreateIndexBuffer() + { + TResourceArray Indices; + + // 分配顶点索引内存. + Indices.Reserve(NumQuadsPerSide * NumQuadsPerSide * 6); + + // 用Morton顺序构建索引缓冲, 以更好地重用顶点. + for (int32 Morton = 0; Morton < NumQuadsPerSide * NumQuadsPerSide; Morton++) + { + int32 SquareX = FMath::ReverseMortonCode2(Morton); + int32 SquareY = FMath::ReverseMortonCode2(Morton >> 1); + + bool ForwardDiagonal = false; + + if (SquareX % 2) + { + ForwardDiagonal = !ForwardDiagonal; + } + if (SquareY % 2) + { + ForwardDiagonal = !ForwardDiagonal; + } + + int32 Index0 = SquareX + SquareY * (NumQuadsPerSide + 1); + int32 Index1 = Index0 + 1; + int32 Index2 = Index0 + (NumQuadsPerSide + 1); + int32 Index3 = Index2 + 1; + + Indices.Add(Index3); + Indices.Add(Index1); + Indices.Add(ForwardDiagonal ? Index2 : Index0); + Indices.Add(Index0); + Indices.Add(Index2); + Indices.Add(ForwardDiagonal ? Index1 : Index3); + } + + NumIndices = Indices.Num(); + const uint32 Size = Indices.GetResourceDataSize(); + const uint32 Stride = sizeof(IndexType); + + // Create index buffer. Fill buffer with initial data upon creation + FRHIResourceCreateInfo CreateInfo(&Indices); + return RHICreateIndexBuffer(Stride, Size, BUF_Static, CreateInfo); + } + + int32 NumIndices = 0; + const int32 NumQuadsPerSide = 0; +}; + +// 顶点索引. +class FMyMeshVertexBuffer : public FVertexBuffer +{ +public: + FMyMeshVertexBuffer(int32 InNumQuadsPerSide) : NumQuadsPerSide(InNumQuadsPerSide) {} + + virtual void InitRHI() override + { + const uint32 NumVertsPerSide = NumQuadsPerSide + 1; + + NumVerts = NumVertsPerSide * NumVertsPerSide; + + FRHIResourceCreateInfo CreateInfo; + void* BufferData = nullptr; + VertexBufferRHI = RHICreateAndLockVertexBuffer(sizeof(FVector4) * NumVerts, BUF_Static, CreateInfo, BufferData); + FVector4* DummyContents = (FVector4*)BufferData; + + for (uint32 VertY = 0; VertY < NumVertsPerSide; VertY++) + { + FVector4 VertPos; + VertPos.Y = (float)VertY / NumQuadsPerSide - 0.5f; + + for (uint32 VertX = 0; VertX < NumVertsPerSide; VertX++) + { + VertPos.X = (float)VertX / NumQuadsPerSide - 0.5f; + + DummyContents[NumVertsPerSide * VertY + VertX] = VertPos; + } + } + + RHIUnlockVertexBuffer(VertexBufferRHI); + } + + int32 GetVertexCount() const { return NumVerts; } + +private: + int32 NumVerts = 0; + const int32 NumQuadsPerSide = 0; +}; + +// 顶点工厂. +class FMyVertexFactory : public FVertexFactory +{ + DECLARE_VERTEX_FACTORY_TYPE(FMyVertexFactory); + +public: + using Super = FVertexFactory; + + FMyVertexFactory(ERHIFeatureLevel::Type InFeatureLevel); + ~FMyVertexFactory(); + + virtual void InitRHI() override; + virtual void ReleaseRHI() override; + + static bool ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters); + static void ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment); + static void ValidateCompiledResult(const FVertexFactoryType* Type, EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray& OutErrors); + + inline const FUniformBufferRHIRef GetMyVertexFactoryUniformBuffer() const { return UniformBuffer; } + +private: + void SetupUniformData(); + + FMyMeshVertexBuffer* VertexBuffer = nullptr; + FMyMeshIndexBuffer* IndexBuffer = nullptr; + + FMyVertexFactoryBufferRef UniformBuffer; +}; + + +// FMyVertexFactory.cpp +#include "ShaderParameterUtils.h" + +// 实现FMyVertexFactoryParameters, 注意在shader的名字是MyVF. +IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FMyVertexFactoryParameters, "MyVF"); + +// 顶点工厂着色器参数. +class FMyVertexFactoryShaderParameters : public FVertexFactoryShaderParameters +{ + DECLARE_TYPE_LAYOUT(FMyVertexFactoryShaderParameters, NonVirtual); + +public: + + void Bind(const FShaderParameterMap& ParameterMap) + { + } + + void GetElementShaderBindings( + const class FSceneInterface* Scene, + const class FSceneView* View, + const class FMeshMaterialShader* Shader, + const EVertexInputStreamType InputStreamType, + ERHIFeatureLevel::Type FeatureLevel, + const class FVertexFactory* InVertexFactory, + const struct FMeshBatchElement& BatchElement, + class FMeshDrawSingleShaderBindings& ShaderBindings, + FVertexInputStreamArray& VertexStreams) const + { + // 强制转换成FMyVertexFactory. + FMyVertexFactory* VertexFactory = (FMyVertexFactory*)InVertexFactory; + + // 增加shader帮定到表格. + ShaderBindings.Add(Shader->GetUniformBufferParameter(), VertexFactory->GetMyVertexFactoryUniformBuffer()); + + // 填充顶点流. + if (VertexStreams.Num() > 0) + { + // 处理顶点流索引. + for (int32 i = 0; i < 2; ++i) + { + FVertexInputStream* InstanceInputStream = VertexStreams.FindByPredicate([i](const FVertexInputStream& InStream) { return InStream.StreamIndex == i+1; }); + // 绑定顶点流索引. + InstanceInputStream->VertexBuffer = InstanceDataBuffers->GetBuffer(i); + } + + // 处理偏移. + if (InstanceOffsetValue > 0) + { + VertexFactory->OffsetInstanceStreams(InstanceOffsetValue, InputStreamType, VertexStreams); + } + } + } +}; + +// ----------- 实现顶点工厂 ----------- +FMyVertexFactory::FMyVertexFactory(ERHIFeatureLevel::Type InFeatureLevel) +{ + VertexBuffer = new FMyMeshVertexBuffer(16); + IndexBuffer = new FMyMeshIndexBuffer(16); +} + +FMyVertexFactory::~FMyVertexFactory() +{ + delete VertexBuffer; + delete IndexBuffer; +} + +void FMyVertexFactory::InitRHI() +{ + Super::InitRHI(); + + // 设置Uniform数据. + SetupUniformData(); + + VertexBuffer->InitResource(); + IndexBuffer->InitResource(); + + // 顶点流: 位置 + FVertexStream PositionVertexStream; + PositionVertexStream.VertexBuffer = VertexBuffer; + PositionVertexStream.Stride = sizeof(FVector4); + PositionVertexStream.Offset = 0; + PositionVertexStream.VertexStreamUsage = EVertexStreamUsage::Default; + + // 简单的实例化顶点流数据 其中VertexBuffer在绑定时设置. + FVertexStream InstanceDataVertexStream; + InstanceDataVertexStream.VertexBuffer = nullptr; + InstanceDataVertexStream.Stride = sizeof(FVector4); + InstanceDataVertexStream.Offset = 0; + InstanceDataVertexStream.VertexStreamUsage = EVertexStreamUsage::Instancing; + + FVertexElement VertexPositionElement(Streams.Add(PositionVertexStream), 0, VET_Float4, 0, PositionVertexStream.Stride, false); + + // 顶点声明. + FVertexDeclarationElementList Elements; + Elements.Add(VertexPositionElement); + + // 添加索引顶点流. + for (int32 StreamIdx = 0; StreamIdx < NumAdditionalVertexStreams; ++StreamIdx) + { + FVertexElement InstanceElement(Streams.Add(InstanceDataVertexStream), 0, VET_Float4, 8 + StreamIdx, InstanceDataVertexStream.Stride, true); + Elements.Add(InstanceElement); + } + + // 初始化声明. + InitDeclaration(Elements); +} + +void FMyVertexFactory::ReleaseRHI() +{ + UniformBuffer.SafeRelease(); + + if (VertexBuffer) + { + VertexBuffer->ReleaseResource(); + } + + if (IndexBuffer) + { + IndexBuffer->ReleaseResource(); + } + + Super::ReleaseRHI(); +} + +void FMyVertexFactory::SetupUniformData() +{ + FMyVertexFactoryParameters UniformParams; + UniformParams.Color = FVector4(1,0,0,1); + + UniformBuffer = FMyVertexFactoryBufferRef::CreateUniformBufferImmediate(UniformParams, UniformBuffer_MultiFrame); +} + +void FMyVertexFactory::ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters) +{ + return true; +} + +void FMyVertexFactory::ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) +{ + OutEnvironment.SetDefine(TEXT("MY_MESH_FACTORY"), 1); +} + +void FMyVertexFactory::ValidateCompiledResult(const FVertexFactoryType* Type, EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray& OutErrors) +{ +} +``` +C++层的逻辑已经完成,但HLSL层也需要编写对应的代码: +```c++ +#include "/Engine/Private/VertexFactoryCommon.ush" + +// VS插值到PS的结构体。 +struct FVertexFactoryInterpolantsVSToPS +{ +#if NUM_TEX_COORD_INTERPOLATORS + float4 TexCoords[(NUM_TEX_COORD_INTERPOLATORS+1)/2] : TEXCOORD0; +#endif + +#if VF_USE_PRIMITIVE_SCENE_DATA + nointerpolation uint PrimitiveId : PRIMITIVE_ID; +#endif + +#if INSTANCED_STEREO + nointerpolation uint EyeIndex : PACKED_EYE_INDEX; +#endif +}; + +struct FVertexFactoryInput +{ + float4 Position : ATTRIBUTE0; + + float4 InstanceData0 : ATTRIBUTE8; + float4 InstanceData1 : ATTRIBUTE9; + +#if VF_USE_PRIMITIVE_SCENE_DATA + uint PrimitiveId : ATTRIBUTE13; +#endif +}; + +struct FPositionOnlyVertexFactoryInput +{ + float4 Position : ATTRIBUTE0; + + float4 InstanceData0 : ATTRIBUTE8; + float4 InstanceData1 : ATTRIBUTE9; + +#if VF_USE_PRIMITIVE_SCENE_DATA + uint PrimitiveId : ATTRIBUTE1; +#endif +}; + +struct FPositionAndNormalOnlyVertexFactoryInput +{ + float4 Position : ATTRIBUTE0; + float4 Normal : ATTRIBUTE2; + + float4 InstanceData0 : ATTRIBUTE8; + float4 InstanceData1 : ATTRIBUTE9; + +#if VF_USE_PRIMITIVE_SCENE_DATA + uint PrimitiveId : ATTRIBUTE1; +#endif +}; + +struct FVertexFactoryIntermediates +{ + float3 OriginalWorldPos; + + uint PrimitiveId; +}; + +uint GetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants) +{ +#if VF_USE_PRIMITIVE_SCENE_DATA + return Interpolants.PrimitiveId; +#else + return 0; +#endif +} + +void SetPrimitiveId(inout FVertexFactoryInterpolantsVSToPS Interpolants, uint PrimitiveId) +{ +#if VF_USE_PRIMITIVE_SCENE_DATA + Interpolants.PrimitiveId = PrimitiveId; +#endif +} + +#if NUM_TEX_COORD_INTERPOLATORS +float2 GetUV(FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex) +{ + float4 UVVector = Interpolants.TexCoords[UVIndex / 2]; + return UVIndex % 2 ? UVVector.zw : UVVector.xy; +} + +void SetUV(inout FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex, float2 InValue) +{ + FLATTEN + if (UVIndex % 2) + { + Interpolants.TexCoords[UVIndex / 2].zw = InValue; + } + else + { + Interpolants.TexCoords[UVIndex / 2].xy = InValue; + } +} +#endif + +FMaterialPixelParameters GetMaterialPixelParameters(FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition) +{ + // GetMaterialPixelParameters is responsible for fully initializing the result + FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters(); + +#if NUM_TEX_COORD_INTERPOLATORS + UNROLL + for (int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++) + { + Result.TexCoords[CoordinateIndex] = GetUV(Interpolants, CoordinateIndex); + } +#endif //NUM_MATERIAL_TEXCOORDS + + Result.TwoSidedSign = 1; + Result.PrimitiveId = GetPrimitiveId(Interpolants); + + return Result; +} + +FMaterialVertexParameters GetMaterialVertexParameters(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 WorldPosition, half3x3 TangentToLocal) +{ + FMaterialVertexParameters Result = (FMaterialVertexParameters)0; + + Result.WorldPosition = WorldPosition; + Result.TangentToWorld = float3x3(1,0,0,0,1,0,0,0,1); + Result.PreSkinnedPosition = Input.Position.xyz; + Result.PreSkinnedNormal = float3(0,0,1); + +#if NUM_MATERIAL_TEXCOORDS_VERTEX + UNROLL + for(int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS_VERTEX; CoordinateIndex++) + { + Result.TexCoords[CoordinateIndex] = Intermediates.MorphedWorldPosRaw.xy; + } +#endif //NUM_MATERIAL_TEXCOORDS_VERTEX + + return Result; +} + +FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input) +{ + FVertexFactoryIntermediates Intermediates; + + // Get the packed instance data + float4 Data0 = Input.InstanceData0; + float4 Data1 = Input.InstanceData1; + + const float3 Translation = Data0.xyz; + const float3 Scale = float3(Data1.zw, 1.0f); + const uint PackedDataChannel = asuint(Data1.x); + + // Lod level is in first 8 bits and ShouldMorph bit is in the 9th bit + const float LODLevel = (float)(PackedDataChannel & 0xFF); + const uint ShouldMorph = ((PackedDataChannel >> 8) & 0x1); + + // Calculate the world pos + Intermediates.OriginalWorldPos = float3(Input.Position.xy, 0.0f) * Scale + Translation; + +#if VF_USE_PRIMITIVE_SCENE_DATA + Intermediates.PrimitiveId = Input.PrimitiveId; +#else + Intermediates.PrimitiveId = 0; +#endif + + return Intermediates; +} + +half3x3 VertexFactoryGetTangentToLocal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) +{ + return half3x3(1,0,0,0,1,0,0,0,1); +} + +float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 InWorldPosition) +{ + return InWorldPosition; +} + +float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition) +{ + return TranslatedWorldPosition; +} + +FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters) +{ + FVertexFactoryInterpolantsVSToPS Interpolants; + + Interpolants = (FVertexFactoryInterpolantsVSToPS)0; + +#if NUM_TEX_COORD_INTERPOLATORS + float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS]; + GetMaterialCustomizedUVs(VertexParameters, CustomizedUVs); + GetCustomInterpolators(VertexParameters, CustomizedUVs); + + UNROLL + for (int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++) + { + SetUV(Interpolants, CoordinateIndex, CustomizedUVs[CoordinateIndex]); + } +#endif + +#if INSTANCED_STEREO + Interpolants.EyeIndex = 0; +#endif + + SetPrimitiveId(Interpolants, Intermediates.PrimitiveId); + + return Interpolants; +} + +float4 VertexFactoryGetWorldPosition(FPositionOnlyVertexFactoryInput Input) +{ + return Input.Position; +} + +float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) +{ + float4x4 PreviousLocalToWorldTranslated = GetPrimitiveData(Intermediates.PrimitiveId).PreviousLocalToWorld; + PreviousLocalToWorldTranslated[3][0] += ResolvedView.PrevPreViewTranslation.x; + PreviousLocalToWorldTranslated[3][1] += ResolvedView.PrevPreViewTranslation.y; + PreviousLocalToWorldTranslated[3][2] += ResolvedView.PrevPreViewTranslation.z; + + return mul(Input.Position, PreviousLocalToWorldTranslated); +} + +float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants) +{ + float4 ObjectWorldPositionAndRadius = GetPrimitiveData(GetPrimitiveId(Interpolants)).ObjectWorldPositionAndRadius; + return float4(ObjectWorldPositionAndRadius.xyz + ResolvedView.PreViewTranslation.xyz, ObjectWorldPositionAndRadius.w); +} + +uint VertexFactoryGetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants) +{ + return GetPrimitiveId(Interpolants); +} + +float3 VertexFactoryGetWorldNormal(FPositionAndNormalOnlyVertexFactoryInput Input) +{ + return Input.Normal.xyz; +} + +float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) +{ + return float3(0.0f, 0.0f, 1.0f); +} +``` + +由此可见,如果新增加了FVertexFactory的自定义类型,需要在HLSL实现以下接口: +- FVertexFactoryInput 定义输入到VS的数据布局,需要匹配c++侧的FVertexFactory的类型。 +- FVertexFactoryIntermediates 用于存储将在多个顶点工厂函数中使用的缓存中间数据,比如TangentToLocal。 +- FVertexFactoryInterpolantsVSToPS 从VS传递到PS的顶点工厂数据。 +- VertexFactoryGetWorldPosition 从顶点着色器调用来获得世界空间的顶点位置。 +- VertexFactoryGetInterpolantsVSToPS 转换FVertexFactoryInput到FVertexFactoryInterpolants,在硬件光栅化插值之前计算需要插值或传递到PS的数据。 +- GetMaterialPixelParameters 由PS调用,根据FVertexFactoryInterpolants计算并填充FMaterialPixelParameters结构体。 \ No newline at end of file diff --git a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(09)- 材质体系.md b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(09)- 材质体系.md index 6fd8956..7376699 100644 --- a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(09)- 材质体系.md +++ b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(09)- 材质体系.md @@ -6,4 +6,6 @@ tags: rating: ⭐ --- # 前言 -https://www.cnblogs.com/timlly/p/15109132.html \ No newline at end of file +https://www.cnblogs.com/timlly/p/15109132.html + +# UMaterial \ No newline at end of file