vault backup: 2024-02-06 21:26:28
This commit is contained in:
parent
cb4b81260a
commit
942794934a
6
.obsidian/community-plugins.json
vendored
6
.obsidian/community-plugins.json
vendored
@ -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"
|
||||
]
|
@ -126,6 +126,14 @@
|
||||
"lastUpdated": 1707140527615
|
||||
}
|
||||
}
|
||||
},
|
||||
"VertexBufferSRV": {
|
||||
"VertexBufferSRV": {
|
||||
"currentFile": {
|
||||
"count": 1,
|
||||
"lastUpdated": 1707223827335
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,6 @@ rating: ⭐
|
||||
# 前言
|
||||
原文地址:https://www.cnblogs.com/timlly/p/15092257.html
|
||||
|
||||
|
||||
# FShader
|
||||
```c++
|
||||
class FShader
|
||||
@ -894,3 +893,872 @@ 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);
|
||||
```
|
||||
下面进入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三种类型。
|
||||
|
||||
- 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<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结构体。
|
@ -7,3 +7,5 @@ rating: ⭐
|
||||
---
|
||||
# 前言
|
||||
https://www.cnblogs.com/timlly/p/15109132.html
|
||||
|
||||
# UMaterial
|
Loading…
x
Reference in New Issue
Block a user