vault backup: 2024-02-06 21:26:28

This commit is contained in:
BlueRose 2024-02-06 21:26:28 +08:00
parent cb4b81260a
commit 942794934a
4 changed files with 884 additions and 6 deletions

View File

@ -29,10 +29,10 @@
"obsidian-hover-editor", "obsidian-hover-editor",
"obsidian-admonition", "obsidian-admonition",
"workspaces-plus", "workspaces-plus",
"Enhanced-editing",
"obsidian42-brat", "obsidian42-brat",
"remotely-save", "remotely-save",
"table-editor-obsidian",
"dataview", "dataview",
"obsidian-view-mode-by-frontmatter" "obsidian-view-mode-by-frontmatter",
"Enhanced-editing",
"table-editor-obsidian"
] ]

View File

@ -126,6 +126,14 @@
"lastUpdated": 1707140527615 "lastUpdated": 1707140527615
} }
} }
},
"VertexBufferSRV": {
"VertexBufferSRV": {
"currentFile": {
"count": 1,
"lastUpdated": 1707223827335
}
}
} }
} }
} }

View File

@ -8,7 +8,6 @@ rating: ⭐
# 前言 # 前言
原文地址:https://www.cnblogs.com/timlly/p/15092257.html 原文地址:https://www.cnblogs.com/timlly/p/15092257.html
# FShader # FShader
```c++ ```c++
class FShader class FShader
@ -893,4 +892,873 @@ IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLocalVertexFactory, SF_Vertex, FLocalVe
// 实现FLocalVertexFactory. // 实现FLocalVertexFactory.
IMPLEMENT_VERTEX_FACTORY_TYPE_EX(FLocalVertexFactory,"/Engine/Private/LocalVertexFactory.ush",true,true,true,true,true,true,true); IMPLEMENT_VERTEX_FACTORY_TYPE_EX(FLocalVertexFactory,"/Engine/Private/LocalVertexFactory.ush",true,true,true,true,true,true,true);
``` ```
下面进入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<FVector>& InPoints, TArray<FDynamicMeshVertex>& OutVertices, TArray<int32>& OutIndices)
{
(......)
}
// 设置动态数据(渲染线程调用)
void SetDynamicData_RenderThread(FCableDynamicData* NewDynamicData)
{
// 释放旧数据.
if(DynamicData)
{
delete DynamicData;
DynamicData = NULL;
}
DynamicData = NewDynamicData;
// 从Cable点构建顶点.
TArray<FDynamicMeshVertex> Vertices;
TArray<int32> 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<const FSceneView*>& 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三种类型。
- FGlobalShaderMapFGlobalShaderMap保存并管理着所有编译好的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<uint32> 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<uint8> 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<FMyVertexFactoryParameters> FMyVertexFactoryBufferRef;
// 索引缓冲.
class FMyMeshIndexBuffer : public FIndexBuffer
{
public:
FMyMeshIndexBuffer(int32 InNumQuadsPerSide) : NumQuadsPerSide(InNumQuadsPerSide) {}
void InitRHI() override
{
if (NumQuadsPerSide < 256)
{
IndexBufferRHI = CreateIndexBuffer<uint16>();
}
else
{
IndexBufferRHI = CreateIndexBuffer<uint32>();
}
}
int32 GetIndexCount() const { return NumIndices; };
private:
template <typename IndexType>
FIndexBufferRHIRef CreateIndexBuffer()
{
TResourceArray<IndexType, INDEXBUFFER_ALIGNMENT> 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<FString>& 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<FMyVertexFactoryShaderParameters>(), 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<FString>& 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结构体。

View File

@ -6,4 +6,6 @@ tags:
rating: ⭐ rating: ⭐
--- ---
# 前言 # 前言
https://www.cnblogs.com/timlly/p/15109132.html https://www.cnblogs.com/timlly/p/15109132.html
# UMaterial