專欄目錄:
前言:
VertexFactory主要負責將特定網格型別的頂點從CPU傳遞到GPU,然後在VertexShader中使用,它描述了UE的各個渲染Pass輸入給Shader的頂點資料都是什麼,參考原始碼4。26
無處不在的VertexFactory頂點工廠
FLidarPointCloudVertexFactory
FVirtualHeightfieldMeshVertexFactory
TWaterVertexFactory
FNiagaraVertexFactoryBase
FHairCardsVertexFactory
FHairStrandsVertexFactory
FVectorFieldVisualizationVertexFactory
FGeometryCacheVertexVertexFactory
FGPUBaseSkinVertexFactory
FParticleVertexFactoryBase
FLandscapeVertexFactory
FLocalVertexFactory
FPaperSpriteVertexFactory
FLidarPointCloudCollisionVertexFactory
FInstancedStaticMeshVertexFactory
FSplineMeshVertexFactory
FTileVertexFactory
FTriangleVertexFactory
FGeometryCollectionVertexFactory
FGPUSkinPassthroughVertexFactory
FSingleTriangleMeshVertexFactory
FQuadMeshVertexFactory
FRenderResource和FRHIResource
這兩個類是UE4渲染程式碼的重要組成部分,因為他們是大多數資源實現的介面,意味著你在UE4渲染程式碼中找到的任何一個Resources不是RenderResource就是RHIResource。
FRHIResource
:
是任何RHI資源的基本型別,可以是頂點緩衝區,索引緩衝區,混合狀態等。基本上在圖形API中熟悉的任何資源都具有等效的RHI。不過,我們通常不直接與這些資源直接進行互動,它們由渲染器及其元件使用。
FRenderResou
rce:
是一個介面,用於定義渲染資源的常規行為模式。這些在渲染器模組中定義,它們可以建立和封裝FRHIResources
。
正是這些資源,我們可以與之互動並直接建立,並且在後臺,它們將為我們建立所需的FRHIResources。這就是為什麼此介面包括用於初始化和釋放渲染資源擁有的RHI資源(InitRHI(),ReleaseRHI() 等)的不同方法的原因。
SRV & UAV
Shader resource view (SRV):著色器資源檢視通常以著色器可以訪問它們的格式包裝紋理。
Unordered Access view (UAV):無序訪問檢視提供了類似的功能,但可以按任意順序讀取和寫入紋理(或其他資源)。
基本型別說明
FStaticMeshDataType:該類包含了頂點工廠初始化RHIResource所需的資源,在LocalVertexFactory類裡面命名為FDataType繼承自FStaticMeshDataType,如果我們是直接派生自FVertexFactory那麼我們通常自己定義所需的FDataType,無需繼承FStaticMeshDataType類。
struct
FStaticMeshDataType
{
/** The stream to read the vertex position from。 */
FVertexStreamComponent
PositionComponent
;
/** The streams to read the tangent basis from。 */
FVertexStreamComponent
TangentBasisComponents
[
2
];
/** The streams to read the texture coordinates from。 */
TArray
<
FVertexStreamComponent
,
TFixedAllocator
<
MAX_STATIC_TEXCOORDS
/
2
>
>
TextureCoordinates
;
/** The stream to read the shadow map texture coordinates from。 */
FVertexStreamComponent
LightMapCoordinateComponent
;
/** The stream to read the vertex color from。 */
FVertexStreamComponent
ColorComponent
;
FRHIShaderResourceView
*
PositionComponentSRV
=
nullptr
;
FRHIShaderResourceView
*
TangentsSRV
=
nullptr
;
/** A SRV to manually bind and load TextureCoordinates in the Vertexshader。 */
FRHIShaderResourceView
*
TextureCoordinatesSRV
=
nullptr
;
/** A SRV to manually bind and load Colors in the Vertexshader。 */
FRHIShaderResourceView
*
ColorComponentsSRV
=
nullptr
;
int
LightMapCoordinateIndex
=
-
1
;
int
NumTexCoords
=
-
1
;
uint32
ColorIndexMask
=
~
0u
;
uint32
LODLightmapDataIndex
=
0
;
};
FVertexStreamComponent:作為FStaticMeshDataType類最重要的成員,你可能已經注意到了,每個頂點流元件都儲存一個同類型的資料,可以是一個屬性,如:位置、顏色、紋理座標等的頂點快取區。FVertexStreamComponent是VertexBuffer資源和有關流的其他元素的包裝,用於建立頂點宣告元素和頂點流,程式碼如下:
/**
* A typed data source for a vertex factory which streams data from a vertex buffer。
*/
struct FVertexStreamComponent
{
/** The vertex buffer to stream data from。 If null, no data can be read from this stream。 */
const FVertexBuffer* VertexBuffer = nullptr;
/** The offset to the start of the vertex buffer fetch。 */
uint32 StreamOffset = 0;
/** The offset of the data, relative to the beginning of each element in the vertex buffer。 */
uint8 Offset = 0;
/** The stride of the data。 */
uint8 Stride = 0;
/** The type of the data read from this stream。 */
TEnumAsByte
EVertexStreamUsage VertexStreamUsage = EVertexStreamUsage::Default;
/**
* Initializes the data stream to null。
*/
FVertexStreamComponent()
{}
/**
* Minimal initialization constructor。
*/
FVertexStreamComponent(const FVertexBuffer* InVertexBuffer, uint32 InOffset, uint32 InStride, EVertexElementType InType, EVertexStreamUsage Usage = EVertexStreamUsage::Default) :
VertexBuffer(InVertexBuffer),
StreamOffset(0),
Offset(InOffset),
Stride(InStride),
Type(InType),
VertexStreamUsage(Usage)
{
check(InStride <= 0xFF);
check(InOffset <= 0xFF);
}
FVertexStreamComponent(const FVertexBuffer* InVertexBuffer, uint32 InStreamOffset, uint32 InOffset, uint32 InStride, EVertexElementType InType, EVertexStreamUsage Usage = EVertexStreamUsage::Default) :
VertexBuffer(InVertexBuffer),
StreamOffset(InStreamOffset),
Offset(InOffset),
Stride(InStride),
Type(InType),
VertexStreamUsage(Usage)
{
check(InStride <= 0xFF);
check(InOffset <= 0xFF);
}
};
FVertexElement:該結構僅包含有關流的一些資料(但不包含流的“頂點緩衝區”),該資料會透過FVertexStream和FVertexStreamComponent來初始化,並且在InitDeclaration()方法中建立頂點宣告。
struct FVertexElement
{
uint8 StreamIndex;
uint8 Offset;
TEnumAsByte
uint8 AttributeIndex;
uint16 Stride;
/**
* Whether to use instance index or vertex index to consume the element。
* eg if bUseInstanceIndex is 0, the element will be repeated for every instance。
*/
uint16 bUseInstanceIndex;
FVertexElement() {}
FVertexElement(uint8 InStreamIndex,uint8 InOffset,EVertexElementType InType,uint8 InAttributeIndex,uint16 InStride,bool bInUseInstanceIndex = false):
StreamIndex(InStreamIndex),
Offset(InOffset),
Type(InType),
AttributeIndex(InAttributeIndex),
Stride(InStride),
bUseInstanceIndex(bInUseInstanceIndex)
{}
/**
* Suppress the compiler generated assignment operator so that padding won‘t be copied。
* This is necessary to get expected results for code that zeros, assigns and then CRC’s the whole struct。
*/
void operator=(const FVertexElement& Other)
{
StreamIndex = Other。StreamIndex;
Offset = Other。Offset;
Type = Other。Type;
AttributeIndex = Other。AttributeIndex;
Stride = Other。Stride;
bUseInstanceIndex = Other。bUseInstanceIndex;
}
friend FArchive& operator<<(FArchive& Ar,FVertexElement& Element)
{
Ar << Element。StreamIndex;
Ar << Element。Offset;
Ar << Element。Type;
Ar << Element。AttributeIndex;
Ar << Element。Stride;
Ar << Element。bUseInstanceIndex;
return Ar;
}
RHI_API FString ToString() const;
RHI_API void FromString(const FString& Src);
RHI_API void FromString(const FStringView& Src);
};
FVertexDeclarationElementList:是FVertexElement的陣列,一般在頂點工廠的InitRHI()中建立頂點宣告時使用。
typedef TArray
FRHIVertexDeclaration:這個頂點宣告相當於一組Input Layout輸入佈局對應的RHIResource,它描述了包括不同屬性的頂點資料,例如位置、顏色、發線等,可以簡單理解就是一組FStaticMeshDataType資料。
FVertexFactory類具有一個輔助方法InitDeclaration(),該方法透過提供FVertexDeclarationElementList來建立FVertexDeclaration。InitDeclaration有兩件事情要做。
一是:會根據我們指定的流型別建立不同的頂點宣告,預設頂點宣告、僅位置頂點宣告、僅位置和法線頂點宣告,後面兩種會用於特定通道,例如深度通道,頂點工廠對應的usf shader檔案裡也會有不同處理,比如VertexFactoryGetWorldPosition方法就會有對應的三種過載;
二是:宣告被快取在PipelineStateCache中。因為具有相同元素和型別的頂點宣告可以由不同型別的頂點工廠重用,而不必每次都重新建立新的RHI資源。
typedef TRefCountPtr
///////////////////////////////////////////////////////////////////////////////////////
/** The RHI vertex declaration used to render the factory normally。 */
FVertexDeclarationRHIRef Declaration;
/** The RHI vertex declaration used to render the factory during depth only passes。 */
FVertexDeclarationRHIRef PositionDeclaration;
FVertexDeclarationRHIRef PositionAndNormalDeclaration;
///////////////////////////////////////////////////////////////////////////////////////
void FVertexFactory::InitDeclaration(const FVertexDeclarationElementList& Elements, EVertexInputStreamType StreamType)
{
if (StreamType == EVertexInputStreamType::PositionOnly)
{
PositionDeclaration = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
else if (StreamType == EVertexInputStreamType::PositionAndNormalOnly)
{
PositionAndNormalDeclaration = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
else // (StreamType == EVertexInputStreamType::Default)
{
// Create the vertex declaration for rendering the factory normally。
Declaration = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
}
FVertexStream:用來設定頂點流所需的資訊,這與FVertexStreamComponent非常相似,這個結構唯一的附加資料是這個頂點流將用於的流型別(PositionOnly,PositionAndNormalOnly或Default)。
類似於三種FVertexDeclarationRHIRef ,頂點工廠對於每種流型別也具有3個FVertexStream陣列。
/**
* Information needed to set a vertex stream。
*/
struct
FVertexStream
{
const
FVertexBuffer
*
VertexBuffer
=
nullptr
;
uint32
Offset
=
0
;
uint16
Stride
=
0
;
EVertexStreamUsage
VertexStreamUsage
=
EVertexStreamUsage
::
Default
;
uint8
Padding
=
0
;
friend
bool
operator
==
(
const
FVertexStream
&
A
,
const
FVertexStream
&
B
)
{
return
A
。
VertexBuffer
==
B
。
VertexBuffer
&&
A
。
Stride
==
B
。
Stride
&&
A
。
Offset
==
B
。
Offset
&&
A
。
VertexStreamUsage
==
B
。
VertexStreamUsage
;
}
FVertexStream
()
{
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
/** The vertex streams used to render the factory。 */
TArray
<
FVertexStream
,
TInlineAllocator
<
8
>
>
Streams
;
/** The position only vertex stream used to render the factory during depth only passes。 */
TArray
<
FVertexStream
,
TInlineAllocator
<
2
>
>
PositionStream
;
TArray
<
FVertexStream
,
TInlineAllocator
<
3
>
>
PositionAndNormalStream
;
/** The RHI vertex declaration used to render the factory normally。 */
FVertexDeclarationRHIRef
Declaration
;
/** The RHI vertex declaration used to render the factory during depth only passes。 */
FVertexDeclarationRHIRef
PositionDeclaration
;
FVertexDeclarationRHIRef
PositionAndNormalDeclaration
;
FVertexFactory類
FVertexFactory是FRenderResource的一種,負責從資產或其他來源獲取網格資料,並使用其建立所需的FRHIResources。它封裝了這些資源,當需要渲染網格時,渲染器將需要它的VertexFactory(間接地說,就是它封裝的底層RHI資源)。
宣告頂點工廠型別
class ENGINE_API FLocalVertexFactory : public FVertexFactory
{
DECLARE_VERTEX_FACTORY_TYPE(FLocalVertexFactory);
。。。
}
實現頂點工廠型別
IMPLEMENT_VERTEX_FACTORY_TYPE_EX
(
FLocalVertexFactory
,
“/Engine/Private/LocalVertexFactory。ush”
,
true
,
true
,
true
,
true
,
true
,
true
,
true
);
頂點工廠著色器引數:根據頂點工廠及其實現的邏輯,我們需要提供一組不同的著色器引數。為此,我們需要建立一個繼承自FVertexFactoryShaderParameters的類並實現其介面。
//宣告
DECLARE_TYPE_LAYOUT(FLocalVertexFactoryShaderParametersBase, NonVirtual);
////////////////////////////////////////////////////////////////////////
//實現
IMPLEMENT_TYPE_LAYOUT(FLocalVertexFactoryShaderParametersBase);
////////////////////////////////////////////////////////////////////////
//指定哪個頂點工廠將此類用作其著色器引數型別
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLocalVertexFactory, SF_Vertex, FLocalVertexFactoryShaderParameters);
預處理指令控制:透過頂點工廠方法ModifyCompilationEnvironment可以設定預處理器指令,來控制預設流Input Layout中應包含的內容,即宏開啟與否。
void FLocalVertexFactory::ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment。SetDefine(TEXT(“VF_SUPPORTS_SPEEDTREE_WIND”),TEXT(“1”));
const bool ContainsManualVertexFetch = OutEnvironment。GetDefinitions()。Contains(“MANUAL_VERTEX_FETCH”);
if (!ContainsManualVertexFetch && RHISupportsManualVertexFetch(Parameters。Platform))
{
OutEnvironment。SetDefine(TEXT(“MANUAL_VERTEX_FETCH”), TEXT(“1”));
}
OutEnvironment。SetDefine(TEXT(“VF_SUPPORTS_PRIMITIVE_SCENE_DATA”), Parameters。VertexFactoryType->SupportsPrimitiveIdStream() && UseGPUScene(Parameters。Platform, GetMaxSupportedFeatureLevel(Parameters。Platform)));
OutEnvironment。SetDefine(TEXT(“VF_GPU_SCENE_TEXTURE”), Parameters。VertexFactoryType->SupportsPrimitiveIdStream() && UseGPUScene(Parameters。Platform, GetMaxSupportedFeatureLevel(Parameters。Platform)) && GPUSceneUseTexture2D(Parameters。Platform));
}
FLocalVertexFactory類:
一個簡單地將顯式的頂點屬性從區域性空間轉換到世界空間的頂點工廠,派生自FVertexFactory。
從FLocalVertexFactory派生的頂點工廠可以使用LocalVertexFactory。ush也可以使用自己的著色器標頭檔案
\Engine\Source\Runtime\Engine\Public\LocalVertexFactory。h
VertexFactory。ush
頂點工廠著色器程式碼,是基於模板的方法,僅需要定義介面功能和結構。
輸入佈局,用於描述每種流型別的頂點資料佈局,FVertexFactoryInput中的結構需要與頂點工廠中初始化頂點宣告的資料型別保持一直,不然會編譯報錯哦。
/**
* Per-vertex inputs from bound vertex buffers
*/
struct FVertexFactoryInput
{
float4 Position : ATTRIBUTE0;
float4 Color : ATTRIBUTE1;
};
/**
* Per-vertex inputs from bound vertex buffers。 Used by passes with a trimmed down position-only shader。
*/
struct FPositionOnlyVertexFactoryInput
{
float4 Position : ATTRIBUTE0;
};
/**
* Per-vertex inputs from bound vertex buffers。 Used by passes with a trimmed down position-and-normal-only shader。
*/
struct FPositionAndNormalOnlyVertexFactoryInput
{
float4 Position : ATTRIBUTE0;
float4 Normal : ATTRIBUTE2;
};
用於僅需執行一次的快取計算,並使用插值將資料從頂點著色器傳遞到畫素著色器的結構。
/**
* Get the 3x3 tangent basis vectors for this vertex factory
* this vertex factory will calculate the binormal on-the-fly
*
* @param Input - vertex input stream structure
* @return 3x3 matrix
*/
float3x3 VertexFactoryGetTangentToLocal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
float3x3 Result = CalcTangentBasisFromWorldNormal(Intermediates。WorldNormal);
return Result;
}
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;
return Interpolants;
}
例如,VertexFactoryGetWorldPosition,我們可以為每種輸入型別過載,可以為僅位置流和預設流定義不同的邏輯,因為它將使用不同的引數型別進行呼叫。
/** for depth-only pass */
float4 VertexFactoryGetWorldPosition(FPositionOnlyVertexFactoryInput Input)
{
return TransformLocalToTranslatedWorld(Input。Position。xyz, 0);
}
// @return translated world position
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
//return GetLocalPosition(Input。Position);
return TransformLocalToTranslatedWorld(Input。Position。xyz, 0);
}
/** for depth-only pass (slope depth bias) */
float4 VertexFactoryGetWorldPosition(FPositionAndNormalOnlyVertexFactoryInput Input)
{
return TransformLocalToTranslatedWorld(Input。Position。xyz, 0);
}
其他結構
FVertexFactoryIntermediates:用於儲存快取記憶體的中間資料,該資料將在多個頂點工廠函式中使用。一個常用的示例是 TangentToLocal 矩陣,該矩陣必須根據未打包的頂點輸入進行計算。
FVertexFactoryInterpolantsVSToPS:從頂點著色器傳遞到畫素著色器的頂點工廠資料。
GetMaterialPixelParameters:此函式在畫素著色器中呼叫,並將特定於頂點工廠的插值 (FVertexFactoryInterpolants) 轉換為 FMaterialPixelParameters 結構,該結構由過程畫素著色器使用。
注意:ShaderCompile編譯報錯的介面函式同樣都需要一一實現,只要著色器標頭檔案正確實現了介面,並且符合C ++程式碼中提供的資料和引數,便可以正常執行啦。