NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 - 3D道路快速建模
我不会深入程序化建模(Procedural Mesh)背后的硬核理论,因为已经有很多文章介绍这些理论。相反,我将侧重于程序化建模的实际用途,例如简单景观的生成、在程序化生成中使用样条线、生成洞穴系统等。我也想做一些更多的实验性的事情, 比如生成式艺术。
现在不能保证本系列教程会涵盖上述内容, 因为我自己都不知道这个系列最终会写到什么程度,但可以保证的是教程会涵盖UE4程序化生成的基本知识。我将使用C++来实现所有示例,因为蓝图的可读性不高,不过可能会用蓝图实现一部分程序化生成。
好吧,我们开始。
1、创建三角形
首先进入Unreal编辑器,创建一个空的C++项目并添加C++类,父类选择Actor:
在"Build.cs"文件中的模块中添加ProceduralMeshComponent。
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine", "InputCore", "ProceduralMeshComponent" });
在新类的头文件中,包括ProceduralMeshComponent. h。此外,在类上添加ProceduralMeshComponent。接下来,添加创建程序化网格的所有必要数组。现在,你只需要顶点和三角形。不幸的是,创建程序化网格的函数还需要其他数组,因此也一并创建。接下来,将"OnConstruction"函数添加到头文件。这基本上是构建脚本,这样就创建了网格。同时创建ClearMeshData函数,用来清理数组。
#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "MyProceduralMesh.generated.h"
UCLASS()
class TUTORIALS_API AMyProceduralMesh : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere, Category = "MyProceduralMesh")
UProceduralMeshComponent* pm;
public:
AMyProceduralMesh();
UPROPERTY()
TArray<FVector> vertices;
UPROPERTY()
TArray<FVector> normals;
UPROPERTY()
TArray<int32> triangles;
UPROPERTY()
TArray<FVector2D> uvs;
UPROPERTY()
TArray<FLinearColor> vertexColors;
UPROPERTY()
TArray<FProcMeshTangent> tangents;
virtual void OnConstruction(const FTransform& Transform) override;
void ClearMeshData();
};
在 cpp 文件的构造器中,你需要做的第一件事是创建ProceduralMeshComponent。
AMyProceduralMesh::AMyProceduralMesh()
{
PrimaryActorTick.bCanEverTick = true;
pm = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("ProceduralMesh"));
pm->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
然后实现OnConstruction函数并添加顶点。现在在 X 轴和 Y 轴上创建它们。下一步将创建三角形。你可以通过将顶点序号添加到三角形数组中来做到这一点。在这里你需要小心,因为顶点的顺序将决定三角形的正面。你需要逆时针插入顶点。因此下面的三角形将是0-2-1。
接下来,初始化其余数组,其长度均与顶点数组相同。现在,您有了创建三角形所需的所有数据,可以创建它了:
void AMyProceduralMesh::OnConstruction(const FTransform& Transform)
{
vertices.Add(FVector(0.0f, 0.0f, 0.0f));
vertices.Add(FVector(100.0f, 0.0f, 0.0f));
vertices.Add(FVector(0.0f, 100.0f, 0.0f));
triangles.Add(0);
triangles.Add(2);
triangles.Add(1);
uvs.Init(FVector2D(0.0f, 0.0f), 3);
normals.Init(FVector(0.0f, 0.0f, 1.0f), 3);
vertexColors.Init(FLinearColor(0.0f, 0.0f, 0.0f, 1.0f), 3);
tangents.Init(FProcMeshTangent(1.0f, 0.0f, 0.0f), 3);
//Function that creates mesh section
pm->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, false);
}
在编译之前的最后一件事,是你必须实现ClearMeshData函数。
void AMyProceduralMesh::ClearMeshData()
{
vertices.Empty();
triangles.Empty();
uvs.Empty();
normals.Empty();
vertexColors.Empty();
tangents.Empty();
}
这将删除所有数组。之所以这样做,是因为每次更改对象时,构建脚本(OnConstruction)都会运行。这将导致每次修改后添加3 个新的 顶点和1个新三角形,这是你不想要的(它可能会崩溃,因为其他数组的长度不会匹配顶点数组)。这不是干净的解决方案, 但它可以工作。我们现在可以编译了。
创建一个新的蓝图,选择刚刚创建的程序化网格类作为父类。然后,您可以将该蓝图添加到关卡,并且应该看到一个三角形。
2、三角形纹理处理
为了正确处理三角形的纹理,我们需要为每个顶点提供 0-1 范围内的 2D UV坐标。在上面示例中这很容易,因为对边和临边等长(三角形是正方形的一半):
uvs.Add(FVector2D(0.0f, 0.0f));
uvs.Add(FVector2D(1.0f, 0.0f));
uvs.Add(FVector2D(0.0f, 1.0f));
//uvs.Init(FVector2D(0.0f, 0.0f), 3);
3、结论
好了,第一个教程结束,下一部分将介绍细分平面,还将有一个额外的实验。本教程源码如下:
#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "MyProceduralMesh.generated.h"
UCLASS()
class TUTORIALS_API AMyProceduralMesh : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere, Category = "MyProceduralMesh")
UProceduralMeshComponent* pm;
public:
AMyProceduralMesh();
UPROPERTY()
TArray<FVector> vertices;
UPROPERTY()
TArray<FVector> normals;
UPROPERTY()
TArray<int32> triangles;
UPROPERTY()
TArray<FVector2D> uvs;
UPROPERTY()
TArray<FLinearColor> vertexColors;
UPROPERTY()
TArray<FProcMeshTangent> tangents;
virtual void OnConstruction(const FTransform& Transform) override;
void ClearMeshData();
};
#include "Tutorials.h"
#include "MyProceduralMesh.h"
AMyProceduralMesh::AMyProceduralMesh()
{
PrimaryActorTick.bCanEverTick = true;
pm = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("ProceduralMesh"));
pm->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
void AMyProceduralMesh::OnConstruction(const FTransform& Transform)
{
ClearMeshData();
vertices.Add(FVector(0.0f, 0.0f, 0.0f));
vertices.Add(FVector(100.0f, 0.0f, 0.0f));
vertices.Add(FVector(0.0f, 100.0f, 0.0f));
triangles.Add(0);
triangles.Add(2);
triangles.Add(1);
uvs.Add(FVector2D(0.0f, 0.0f));
uvs.Add(FVector2D(1.0f, 0.0f));
uvs.Add(FVector2D(0.0f, 1.0f));
//uvs.Init(FVector2D(0.0f, 0.0f), 3);
normals.Init(FVector(0.0f, 0.0f, 1.0f), 3);
vertexColors.Init(FLinearColor(0.0f, 0.0f, 0.0f, 1.0f), 3);
tangents.Init(FProcMeshTangent(1.0f, 0.0f, 0.0f), 3);
//Function that creates mesh section
pm->CreateMeshSection_LinearColor(0, vertices, triangles, normals, uvs, vertexColors, tangents, false);
}
void AMyProceduralMesh::ClearMeshData()
{
vertices.Empty();
triangles.Empty();
uvs.Empty();
normals.Empty();
vertexColors.Empty();
tangents.Empty();
}
原文链接:Procedural Mesh in UE4 #1 – Triangle
BimAnt翻译整理,转载请标明出处