当前位置:网站首页>(UE4 4.27) customize primitivecomponent
(UE4 4.27) customize primitivecomponent
2022-06-12 06:04:00 【Senior brother Dai Dai】
Preface
stay UE4 In development , Usually used Mesh Yes StaticMesh,SkeletalMesh,ProceduralMesh wait , They all have corresponding rendering components, such as UStaticMeshComponent, UProceduralMeshComponent, In essence, these Mesh Components are inherited UPrimitiveComponent, UPrimitiveComponent adopt FPrimitiveSceneProxy The rendering agent is responsible for translating a particular Mesh Rendering data for (VertexBuffer, IndexBuffer, Material) From the game thread to the rendering thread . Sometimes to customize something special Mesh Rendering , We have to customize the new PrimitiveComponent.( Of course UProceduralMeshComponent Often meets the need to customize new Mesh demand , But sometimes for further performance or special MeshPass Have to customize PrimitiveComponent).

Custom pyramids PrimitiveComponent
Now I will take an octahedron as an example to introduce customization PrimitiveComponent
Create a render proxy
The created render proxy contains VertexBuffer, IndexBuffer, Material related data
class FPyramidSceneProxy final : public FPrimitiveSceneProxy
{
public:
SIZE_T GetTypeHash() const override
{
static size_t UniquePointer;
return reinterpret_cast<size_t>(&UniquePointer);
}
FPyramidSceneProxy(const UPyramidComponent* InComponent);
virtual ~FPyramidSceneProxy()
{
VertexBuffers.PositionVertexBuffer.ReleaseResource();
VertexBuffers.StaticMeshVertexBuffer.ReleaseResource();
VertexBuffers.ColorVertexBuffer.ReleaseResource();
VertexFactory.ReleaseResource();
IndexBuffer.ReleaseResource();
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView *>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, class FMeshElementCollector& Collector) const override;
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const;
void BuildPyramidMesh(const FPyramidMesh* PyramidMeshData, TArray<FDynamicMeshVertex>& OutVertices, TArray<uint32>& OutIndices);
void SetMeshData_RenderThread(FPyramidMesh* PyramidMeshData);
uint32 GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
private:
/** Vertex buffer for this section */
FStaticMeshVertexBuffers VertexBuffers;
/** Index buffer for this section */
FDynamicMeshIndexBuffer32 IndexBuffer;
/** Vertex factory for this section */
FLocalVertexFactory VertexFactory;
FColor PyramidColor;
UMaterialInterface* Material;
FMaterialRelevance MaterialRelevance;
};initialization VertexBuffer, IndexBuffer, Material
FPyramidSceneProxy::FPyramidSceneProxy(const UPyramidComponent* InComponent)
: FPrimitiveSceneProxy(InComponent)
, VertexFactory(GetScene().GetFeatureLevel(), "FPyramidSceneProxy")
, PyramidColor(InComponent->ShapeColor)
, MaterialRelevance(InComponent->GetMaterialRelevance(GetScene().GetFeatureLevel()))
{
// Setup VertexData And IndexData
TArray<FDynamicMeshVertex> Vertices;
TArray<uint32> Indices;
BuildPyramidMesh(&InComponent->PyramidMesh, Vertices, Indices);
VertexBuffers.InitFromDynamicVertex(&VertexFactory, Vertices, 1);
IndexBuffer.Indices = Indices;
// Enqueue initialization of render resource
BeginInitResource(&VertexBuffers.PositionVertexBuffer);
BeginInitResource(&VertexBuffers.StaticMeshVertexBuffer);
BeginInitResource(&VertexBuffers.ColorVertexBuffer);
BeginInitResource(&IndexBuffer);
BeginInitResource(&VertexFactory);
Material = nullptr == InComponent->Material ? UMaterial::GetDefaultMaterial(MD_Surface) : InComponent->Material;
}Cover GetDynamicMeshElements, Conduct MeshBatch Submission of
Mesh The vertex data of , Index data , Apex factory , texture of material Proxy etc.
void FPyramidSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView *>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, class FMeshElementCollector& Collector) const
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_PyramidSceneProxy_GetDynamicMeshElements);
// Set up wireframe material (if needed)
const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe;
FColoredMaterialRenderProxy* WireframeMaterialInstance = NULL;
if (bWireframe)
{
WireframeMaterialInstance = new FColoredMaterialRenderProxy(
GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy() : NULL,
FLinearColor(0, 0.5f, 1.f)
);
Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
}
FMaterialRenderProxy* MaterialProxy = bWireframe ? WireframeMaterialInstance : Material->GetRenderProxy();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
//Draw Mesh
FMeshBatch& Mesh = Collector.AllocateMesh();
FMeshBatchElement& BatchElement = Mesh.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
Mesh.bWireframe = bWireframe;
Mesh.VertexFactory = &VertexFactory;
Mesh.MaterialRenderProxy = MaterialProxy;
bool bHasPrecomputedVolumetricLightmap;
FMatrix PreviousLocalToWorld;
int32 SingleCaptureIndex;
bool bOutputVelocity;
GetScene().GetPrimitiveUniformShaderParameters_RenderThread(GetPrimitiveSceneInfo(), bHasPrecomputedVolumetricLightmap, PreviousLocalToWorld, SingleCaptureIndex, bOutputVelocity);
FDynamicPrimitiveUniformBuffer& DynamicPrimitiveUniformBuffer = Collector.AllocateOneFrameResource<FDynamicPrimitiveUniformBuffer>();
DynamicPrimitiveUniformBuffer.Set(GetLocalToWorld(), PreviousLocalToWorld, GetBounds(), GetLocalBounds(), true, bHasPrecomputedVolumetricLightmap, DrawsVelocity(), bOutputVelocity);
BatchElement.PrimitiveUniformBufferResource = &DynamicPrimitiveUniformBuffer.UniformBuffer;
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = VertexBuffers.PositionVertexBuffer.GetNumVertices() - 1;
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
Mesh.Type = PT_TriangleList;
Mesh.DepthPriorityGroup = SDPG_World;
Mesh.bCanApplyViewModeOverrides = false;
Collector.AddMesh(ViewIndex, Mesh);
}
}
}Cover GetViewRelevance, Set up Mesh Render state information
FPrimitiveViewRelevance FPyramidSceneProxy::GetViewRelevance(const FSceneView* View) const
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View);
Result.bShadowRelevance = IsShadowCast(View);
Result.bDynamicRelevance = true;
Result.bRenderInMainPass = ShouldRenderInMainPass();
Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask();
Result.bRenderCustomDepth = ShouldRenderCustomDepth();
Result.bTranslucentSelfShadow = bCastVolumetricTranslucentShadow;
MaterialRelevance.SetPrimitiveViewRelevance(Result);
Result.bVelocityRelevance = IsMovable() && Result.bOpaque && Result.bRenderInMainPass;
return Result;
}Create an octahedral PrimitiveComponent
UCLASS(meta = (BlueprintSpawnableComponent), ClassGroup = Rendering)
class CUSTOMRENDERCOMPONENT_API UPyramidComponent : public UPrimitiveComponent
{
GENERATED_BODY()
public:
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
//~ Begin UActorComponent Interface.
virtual void OnRegister() override;
private:
//~ Begin USceneComponent Interface.
virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
//~ Begin USceneComponent Interface.
#if WITH_EDITOR
void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
void RecreateMeshData();
void UpdateLocalBounds();
public:
void RecreateMesh();
void SendMeshDataToRenderThread();
UFUNCTION(BlueprintCallable)
void SetCustomMaterial(UMaterialInterface* InMaterial);
/** Accesses the scene relevance information for the materials applied to the mesh. Valid from game thread only. */
FMaterialRelevance GetMaterialRelevance(ERHIFeatureLevel::Type InFeatureLevel) const;
virtual void GetUsedMaterials(TArray<UMaterialInterface*>& OutMaterials, bool bGetDebugMaterials = false) const override;
UFUNCTION(BlueprintCallable)
void SetPyramidHeight(float NewPyramidHeight);
UFUNCTION(BlueprintCallable)
void SetPyramidBottomSize(FVector2D NewSize);
private:
/** Local space bounds of mesh */
UPROPERTY()
FBoxSphereBounds LocalBounds;
UPROPERTY(EditAnywhere)
float PyramidHeight = 4.0f;
UPROPERTY(EditAnywhere)
FVector2D PyramidBottomSize = FVector2D(4.0f, 4.0f);
public:
/** Color used to draw the shape. */
UPROPERTY(EditAnywhere)
FColor ShapeColor;
UPROPERTY()
FPyramidMesh PyramidMesh;
UPROPERTY(EditAnywhere)
UMaterialInterface* Material;
};Octahedral construction Mesh Data functions
void UPyramidComponent::RecreateMeshData()
{
PyramidMesh.Reset();
//Vertex Pos
FVector HeightPos = FVector(0.0f, 0.0f, PyramidHeight);
FVector LowPos = FVector(0.0f, 0.0f, -PyramidHeight);
float BoxSizeX = PyramidBottomSize.X / 2.0f;
float BoxSizeY = PyramidBottomSize.Y / 2.0f;
FVector RightTopPos = FVector(BoxSizeX, BoxSizeY, 0.0f);
FVector RightDownPos = FVector(-BoxSizeX, BoxSizeY, 0.0f);
FVector LeftTopPos = FVector(BoxSizeX, -BoxSizeY, 0.0f);
FVector LeftDownPos = FVector(-BoxSizeX, -BoxSizeY, 0.0f);
FVector FaceNormal1 = FVector::CrossProduct(RightTopPos - HeightPos, HeightPos - LeftTopPos);
FaceNormal1.Normalize();
FVector FaceNormal2 = FVector::CrossProduct(LeftTopPos - HeightPos, HeightPos - LeftDownPos);
FaceNormal2.Normalize();
FVector FaceNormal3 = FVector::CrossProduct(LeftDownPos - HeightPos, HeightPos - RightDownPos);
FaceNormal3.Normalize();
FVector FaceNormal4 = FVector::CrossProduct(RightDownPos - HeightPos, HeightPos - RightTopPos);
FaceNormal4.Normalize();
FVector FaceNormal5 = FVector::CrossProduct(LeftTopPos - LowPos, LowPos - RightTopPos);
FaceNormal5.Normalize();
FVector FaceNormal6 = FVector::CrossProduct(LeftDownPos - LowPos, LowPos - LeftTopPos);
FaceNormal6.Normalize();
FVector FaceNormal7 = FVector::CrossProduct(RightDownPos - LowPos, LowPos - LeftDownPos);
FaceNormal7.Normalize();
FVector FaceNormal8 = FVector::CrossProduct(RightTopPos - LowPos, LowPos - RightDownPos);
FaceNormal8.Normalize();
//Add Vertex
//Up
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftTopPos, FaceNormal1));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(HeightPos, FaceNormal1));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightTopPos, FaceNormal1));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftDownPos, FaceNormal2));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(HeightPos, FaceNormal2));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftTopPos, FaceNormal2));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightDownPos, FaceNormal3));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(HeightPos, FaceNormal3));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftDownPos, FaceNormal3));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightTopPos, FaceNormal4));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(HeightPos, FaceNormal4));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightDownPos, FaceNormal4));
//Down
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightTopPos, FaceNormal5));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LowPos, FaceNormal5));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftTopPos, FaceNormal5));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftTopPos, FaceNormal6));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LowPos, FaceNormal6));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftDownPos, FaceNormal6));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LeftDownPos, FaceNormal7));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LowPos, FaceNormal7));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightDownPos, FaceNormal7));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightDownPos, FaceNormal8));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(LowPos, FaceNormal8));
PyramidMesh.VertexArray.Add(FCustomMeshVertex(RightTopPos, FaceNormal8));
for (int32 Index = 0; Index < 24; ++Index)
{
PyramidMesh.IndexArray.Add(Index);
}
UpdateLocalBounds();
}Override create render proxy function
FPrimitiveSceneProxy* UPyramidComponent::CreateSceneProxy()
{
return new FPyramidSceneProxy(this);
}Override build bounding volume function
Mesh The bounding box of visual vertebra elimination is through CalcBounds Function transitive , Every time you build a new Mesh Remember to call when the data UpdateLocalBounds and MarkRenderTransformDirty Update the size of the weeding bounding box
FBoxSphereBounds UPyramidComponent::CalcBounds(const FTransform& LocalToWorld) const
{
FBoxSphereBounds Ret(LocalBounds.TransformBy(LocalToWorld));
Ret.BoxExtent *= BoundsScale;
Ret.SphereRadius *= BoundsScale;
return Ret;
}
void UPyramidComponent::UpdateLocalBounds()
{
FBox LocalBox(ForceInit);
for(auto& MeshVertex : PyramidMesh.VertexArray)
{
LocalBox += MeshVertex.Position;
}
LocalBounds = LocalBox.IsValid ? FBoxSphereBounds(LocalBox) : FBoxSphereBounds(FVector(0, 0, 0), FVector(0, 0, 0), 0); // fallback to reset box sphere bounds
// Update global bounds
UpdateBounds();
// Need to send to render thread
MarkRenderTransformDirty();
}
UPyramidComponent Update data to FPrimitiveSceneProxy
When you set the new octahedral height , The corresponding vertex data will change , There are two ways to update data :
(1) Render dirty marks , cause Rendering Component Re Commission CreateSceneProxy, Create a new FPrimitiveSceneProxy, According to again Component Data to build new VertexBuffer, IndexBuffer wait . However, it is not good to destroy and create resources repeatedly . When you set a new material, it is marked as dirty .
(2) Another way is Component Update the vertex cache to be performed by rendering instructions . Specifically in GameThread Directly through ENQUEUE_RENDER_COMMAND Pass the built data changes to the rendering thread FPrimitiveSceneProxy, This is suitable for frequent changes VertexBuffer, IndexBuffer Parametric Mesh.
void UPyramidComponent::SendMeshDataToRenderThread()
{
FPyramidMesh* NewPyramidMesh = new FPyramidMesh;
*NewPyramidMesh = PyramidMesh;
// Enqueue command to set vertex data to renderthread
FPyramidSceneProxy* PyramidSceneProxy = (FPyramidSceneProxy*)SceneProxy;
if (PyramidSceneProxy)
{
ENQUEUE_RENDER_COMMAND(FPyramidMeshData)(
[PyramidSceneProxy, NewPyramidMesh](FRHICommandListImmediate& RHICmdList)
{
PyramidSceneProxy->SetMeshData_RenderThread(NewPyramidMesh);
}
);
}
}
void FPyramidSceneProxy::SetMeshData_RenderThread(FPyramidMesh* NewPyramidMeshData)
{
check(IsInRenderingThread());
//FCustomVertex To FDynMeshVertexildPyramidMesh(NewPyramidMeshData, Vertices, Indices);
TArray<FDynamicMeshVertex> Vertices;
TArray<uint32> Indices;
BuildPyramidMesh(NewPyramidMeshData, Vertices, Indices);
for (int32 Index = 0; Index < Vertices.Num(); Index++)
{
const FDynamicMeshVertex& Vertex = Vertices[Index];
VertexBuffers.PositionVertexBuffer.VertexPosition(Index) = Vertex.Position;
VertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(Index, Vertex.TangentX.ToFVector(), Vertex.GetTangentY(), Vertex.TangentZ.ToFVector());
VertexBuffers.StaticMeshVertexBuffer.SetVertexUV(Index, 0, Vertex.TextureCoordinate[0]);
VertexBuffers.ColorVertexBuffer.VertexColor(Index) = Vertex.Color;
}
{
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);
}
{
auto& VertexBuffer = VertexBuffers.ColorVertexBuffer;
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetNumVertices() * VertexBuffer.GetStride(), RLM_WriteOnly);
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetVertexData(), VertexBuffer.GetNumVertices() * VertexBuffer.GetStride());
RHIUnlockVertexBuffer(VertexBuffer.VertexBufferRHI);
}
{
auto& VertexBuffer = VertexBuffers.StaticMeshVertexBuffer;
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.TangentsVertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetTangentSize(), RLM_WriteOnly);
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetTangentData(), VertexBuffer.GetTangentSize());
RHIUnlockVertexBuffer(VertexBuffer.TangentsVertexBuffer.VertexBufferRHI);
}
{
auto& VertexBuffer = VertexBuffers.StaticMeshVertexBuffer;
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.TexCoordVertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetTexCoordSize(), RLM_WriteOnly);
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetTexCoordData(), VertexBuffer.GetTexCoordSize());
RHIUnlockVertexBuffer(VertexBuffer.TexCoordVertexBuffer.VertexBufferRHI);
}
if (NewPyramidMeshData)
{
delete NewPyramidMeshData;
NewPyramidMeshData = nullptr;
}
}Results show

Here, you should pay attention to setting the material MarkRenderStateDirty, MarkRenderStateDirty Will make UPyramidComponent Create a new FPrimitiveSceneProxy.
Octahedral rendering component source code link
https://download.csdn.net/download/qq_29523119/35627759
Reference material
【1】CableComponent.h and ProceduralMeshComponent.h
【2】https://medium.com/@lordned/unreal-engine-4-rendering-part-2-shaders-and-vertex-data-80317e1ae5f3
边栏推荐
- Types, functions and applications of intelligent sensors
- 数据库为什么不使用hash表?
- Directx11 advanced tutorial PBR (1) summary of physical phenomena of light
- Leetcode dynamic programming
- Leetcode-1705. Maximum number of apples to eat
- EBook editing and deleting
- Review notes of naturallanguageprocessing based on deep learning
- How do I get the date and time from the Internet- How to get DateTime from the internet?
- 数据库实验二:数据更新
- Error the main class com xxx. yyy. Application
猜你喜欢

肝了一個月的 DDD,一文帶你掌握

Login authentication filter

Why don't databases use hash tables?

User login 【 I 】

姿态估计之2D人体姿态估计 - PifPaf:Composite Fields for Human Pose Estimation

Houdini script vex learning

Understanding of distributed transactions

Execute sh script to prompt "[[: not found" solution. The difference between Bash and sh

Leetcode-1260. 2D mesh migration

IBL of directx11 advanced tutorial PBR (3)
随机推荐
Guns框架多数据源配置,不修改配置文件
Research Report on truffle fungus industry - market status analysis and development prospect forecast
[PowerShell] command line output and adding system environment variables
Database Experiment 3: data query
Un mois de DDD hépatique.
Research Report on water sports shoes industry - market status analysis and development prospect forecast
Es6-es11 learning
Leetcode-646. Longest number pair chain
R语言大作业(四):上海市、东京 1997-2018 年GDP值分析
China's elastic belt market trend report, technical dynamic innovation and market forecast
Leetcode-1663. Minimum string with given value
项目管理与统筹
How do I get the date and time from the Internet- How to get DateTime from the internet?
Directx11 advanced tutorial tiled based deffered shading
EBook editing and deleting
Leetcode-553. Optimal division
MySQL notes
Makefile文件编写快速掌握
sqlite交叉編譯動態庫
为什么数据库不使用二叉树、红黑树、B树、Hash表? 而是使用了B+树