UE4繪製自定義模型

      UE4中的UProceduralMeshComponent組件可以用於繪製自定義形狀的模型,該組件位於ProceduralMeshComponent插件中,如果要引用該插件中的功能就要在XXXX.build.cs文件中加入對ProceduralMeshComponent模塊的引用,如下所示

PublicDependencyModuleNames.AddRange(new string[] {......, "ProceduralMeshComponent"});

      UProceduralMeshComponent類中CreateMeshSection_LinearColor函數用於根據頂點數組等數據創建模型,UpdateMeshSection_LinearColor用於更新模型,ClearMeshSection和ClearAllMeshSections用於刪除模型。
      本文用CreateMeshSection_LinearColor演示如何創建三角形面片,四邊形面片以及如何根據一組折線上的點畫出指定寬度的折線面片。
      首先創建一個CustomMeshActor.h和CustomMeshActor.cpp文件,定義一個ACustomMeshActor類,類中有一個UProceduralMeshComponent組件成員變量,代碼如下

//CustomMeshActor.h
#pragma once
#include"CoreMinimal.h"
#include"GameFramework/Actor.h"
#include"ProceduralMeshComponent.h"
#include"CustomMeshActor.generated.h"
UCLASS()
class THIRDPERSON_API ACustomMeshActor :public AActor
{
    GENERATED_UCLASS_BODY()
public:
    ACustomMeshActor();
    UPROPERTY()
        UProceduralMeshComponent * DrawMeshComponent;
};

//CustomMeshActor.cpp
#include "CustomMeshActor.h"
ACustomMeshActor::ACustomMeshActor(const FObjectInitializer& ObjectInitializer)
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
    DrawMeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("DrawMeshComponent"));
    DrawMeshComponent->SetupAttachment(RootComponent);
}

1、三角形面片
      定義DrawTriangle函數用於繪製三角形面片

void ACustomMeshActor::DrawTriangle(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0,0,0));
    vertices.Add(FVector(0,100,0));
    vertices.Add(FVector(100,0,0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

上面的代碼畫一個由(0,0,0),(0,100,0)和(100,0,0)三個頂點圍成的三角形,該三角形的材質以函數的參數傳入。
       畫一個三角形需要三個頂點和三個頂點索引,也就是上面的vertices和triangles,其中triangles是頂點的索引,必須是3的倍數,3個索引表示一個三角形,而且三個索引對應的頂點必須逆時針排列才能看到該三角面(逆時針是根據視角決定的,如從上往下看和從下往上看的逆時針方向不一樣。此外如果需要從兩個相反的方向都能看到三角面,可以將材質設置爲雙面材質)。
      在編輯中創建一個基類爲ACustomMeshActor的藍圖類BPCustomMeshActor,在ConstructionScript函數中使用DrawTriangle函數就可以看到繪製出來的三角形面片,材質使用了純紅色的自定義材質RedMaterial,如下圖所示


2、四邊形片面
      定義DrawPlane函數用於繪製四邊形面片,四邊形可以由兩個三角形組成,因此有6個索引,而頂點只有4個

void ACustomMeshActor::DrawPlane(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0, 0, 0));
    vertices.Add(FVector(0, 100, 0));
    vertices.Add(FVector(100, 100, 0));
    vertices.Add(FVector(100, 0, 0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);
    triangles.Add(2);
    triangles.Add(3);
    triangles.Add(0);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

      效果如下圖

3、折線面片
      定義DrawLine用於繪製折線面片,InPoints是折線上的點,InWidth是線的寬度,爲了簡單起見這裏只畫XY平面的折線,即要確保InPoints中點的Z軸值爲0或全都相等,

void ACustomMeshActor::DrawLine(UMaterialInterface * InMaterial,const TArray<FVector>& InPoints, float InWidth)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    int32 num = InPoints.Num();
    if (num < 2)
    {
        return;
    }
    for (int32 i = 0; i < num; i++)
    {
        FVector right(0, -1, 0);
        if (i == num - 1)
        {
            right = FVector::CrossProduct(InPoints[i] - InPoints[i-1], FVector(0, 0, 1));
        }
        else
        {
            right = FVector::CrossProduct(InPoints[i+1] - InPoints[i], FVector(0, 0, 1));
        }
        right.Normalize();
        right = -right;//右手座標系轉左手座標系
        FVector pt1 = InPoints[i] - right * InWidth / 2;
        FVector pt2 = InPoints[i] + right * InWidth / 2;
        vertices.Add(pt1);
        vertices.Add(pt2);
        int vNum = vertices.Num();
        if (i > 0)
        {
            triangles.Add(vNum - 4);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 1);
        }
    }
    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

      right是該線段的右向單位向量,也就是在XY二維平面上與該線段垂直的向量,加減right向量就是將點左右平移,triangles添加索引與vertices加入頂點的順序有關。繪製出來後如下圖所示

      上面幾個例子中normals;tangents;vertexColors;uvs;這些變量都沒有添加值,具體使用時可根據需要添加,尤其是uvs一般都會有值的。
附上完整的代碼:
CustomMeshActor.h

#pragma once
#include"CoreMinimal.h"
#include"GameFramework/Actor.h"
#include"ProceduralMeshComponent.h"
#include"CustomMeshActor.generated.h"
UCLASS()
class THIRDPERSON_API ACustomMeshActor :public AActor
{
    GENERATED_UCLASS_BODY()
public:
    ACustomMeshActor();
    UFUNCTION(BlueprintCallable, Category = "CustomMeshActor")
    void DrawTriangle(UMaterialInterface* InMaterial);
    UFUNCTION(BlueprintCallable, Category = "CustomMeshActor")
    void DrawPlane(UMaterialInterface* InMaterial);
    UFUNCTION(BlueprintCallable, Category = "CustomMeshActor")
    void DrawLine(UMaterialInterface * InMaterial, const TArray<FVector>& InPoints, float InWidth);

    UPROPERTY()
        UProceduralMeshComponent * DrawMeshComponent;
};

CustomMeshActor.cpp

#include "CustomMeshActor.h"
ACustomMeshActor::ACustomMeshActor(const FObjectInitializer& ObjectInitializer)
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
    DrawMeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("DrawMeshComponent"));
    DrawMeshComponent->SetupAttachment(RootComponent);
}

void ACustomMeshActor::DrawTriangle(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0,0,0));
    vertices.Add(FVector(0,100,0));
    vertices.Add(FVector(100,0,0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

void ACustomMeshActor::DrawPlane(UMaterialInterface * InMaterial)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    vertices.Add(FVector(0, 0, 0));
    vertices.Add(FVector(0, 100, 0));
    vertices.Add(FVector(100, 100, 0));
    vertices.Add(FVector(100, 0, 0));
    triangles.Add(0);
    triangles.Add(1);
    triangles.Add(2);
    triangles.Add(2);
    triangles.Add(3);
    triangles.Add(0);

    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

void ACustomMeshActor::DrawLine(UMaterialInterface * InMaterial,const TArray<FVector>& InPoints, float InWidth)
{
    TArray<FVector> vertices;
    TArray<int32> triangles;
    int32 num = InPoints.Num();
    if (num < 2)
    {
        return;
    }
    for (int32 i = 0; i < num; i++)
    {
        FVector right(0, -1, 0);
        if (i == num - 1)
        {
            right = FVector::CrossProduct(InPoints[i] - InPoints[i-1], FVector(0, 0, 1));
        }
        else
        {
            right = FVector::CrossProduct(InPoints[i+1] - InPoints[i], FVector(0, 0, 1));
        }
        right.Normalize();
        right = -right;//右手座標系轉左手座標系
        FVector pt1 = InPoints[i] - right * InWidth / 2;
        FVector pt2 = InPoints[i] + right * InWidth / 2;
        vertices.Add(pt1);
        vertices.Add(pt2);
        int vNum = vertices.Num();
        if (i > 0)
        {
            triangles.Add(vNum - 4);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 2);
            triangles.Add(vNum - 3);
            triangles.Add(vNum - 1);
        }
    }
    TArray<FVector> normals;
    TArray<FProcMeshTangent> tangents;
    TArray<FLinearColor> vertexColors;
    TArray<FVector2D> uvs;
    DrawMeshComponent->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, true);
    DrawMeshComponent->SetMaterial(0, InMaterial);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章