Unreal学习笔记2 | 您所在的位置:网站首页 › 画三角形的简单代码 › Unreal学习笔记2 |
文章目录
1. 概述2. 详论2.1. 代码实现2.2. 解析:Component2.3. 解析:材质2.4. 解析:包围盒2.5. 解析:Section
3. 其他4. 参考
1. 概述
之所以写这个绘制简单三角形的实例其实是想知道如何在Unreal中通过代码绘制自定义Mesh,如果你会绘制一个三角形,那么自然就会绘制复杂的Mesh了。所以这是很多图形工作者的第一课。 2. 详论 2.1. 代码实现Actor是Unreal的基本显示对象,有点类似于Unity中的GameObject或者OSG中的Node。因此,我们首先要实现一个继承自AActor的类 头文件CustomMeshActor.h: #pragma once // clang-format off #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "CustomMeshActor.generated.h" // clang-format on UCLASS() class UESTUDY_API ACustomMeshActor : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties ACustomMeshActor(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; UStaticMesh* CreateMesh(); void CreateGeometry(FStaticMeshRenderData* RenderData); void CreateMaterial(UStaticMesh* mesh); public: // Called every frame virtual void Tick(float DeltaTime) override; UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UStaticMeshComponent* staticMeshComponent; };实现CustomMeshActor.cpp: #include "CustomMeshActor.h" #include "Output.h" // Sets default values ACustomMeshActor::ACustomMeshActor() { // 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; } // Called when the game starts or when spawned void ACustomMeshActor::BeginPlay() { Super::BeginPlay(); staticMeshComponent = NewObject(this); staticMeshComponent->SetMobility(EComponentMobility::Stationary); SetRootComponent(staticMeshComponent); staticMeshComponent->RegisterComponent(); UStaticMesh* mesh = CreateMesh(); if (mesh) { staticMeshComponent->SetStaticMesh(mesh); } } UStaticMesh* ACustomMeshActor::CreateMesh() { UStaticMesh* mesh = NewObject(staticMeshComponent); mesh->NeverStream = true; mesh->SetIsBuiltAtRuntime(true); TUniquePtr RenderData = MakeUnique(); CreateGeometry(RenderData.Get()); CreateMaterial(mesh); mesh->SetRenderData(MoveTemp(RenderData)); mesh->InitResources(); mesh->CalculateExtendedBounds(); //设置包围盒之后调用这个函数起效,否则会被视锥体剔除 return mesh; } void ACustomMeshActor::CreateMaterial(UStaticMesh* mesh) { UMaterial* material1 = (UMaterial*)StaticLoadObject( UMaterial::StaticClass(), nullptr, TEXT("Material'/Game/Materials/RedColor.RedColor'")); mesh->AddMaterial(material1); UMaterial* material2 = (UMaterial*)StaticLoadObject( UMaterial::StaticClass(), nullptr, TEXT("Material'/Game/Materials/GreenColor.GreenColor'")); mesh->AddMaterial(material2); } void ACustomMeshActor::CreateGeometry(FStaticMeshRenderData* RenderData) { RenderData->AllocateLODResources(1); FStaticMeshLODResources& LODResources = RenderData->LODResources[0]; int vertexNum = 4; TArray xyzList; xyzList.Add(FVector(0, 0, 50)); xyzList.Add(FVector(100, 0, 50)); xyzList.Add(FVector(100, 100, 50)); xyzList.Add(FVector(0, 100, 50)); TArray uvList; uvList.Add(FVector2D(0, 1)); uvList.Add(FVector2D(0, 0)); uvList.Add(FVector2D(1, 0)); uvList.Add(FVector2D(1, 1)); // 设置顶点数据 TArray StaticMeshBuildVertices; StaticMeshBuildVertices.SetNum(vertexNum); for (int m = 0; m FStaticMeshSection& section = Sections.AddDefaulted_GetRef(); section.bEnableCollision = false; section.MaterialIndex = 0; section.NumTriangles = 1; section.FirstIndex = 0; section.MinVertexIndex = 0; section.MaxVertexIndex = 2; } { FStaticMeshSection& section = Sections.AddDefaulted_GetRef(); section.bEnableCollision = false; section.MaterialIndex = 0; section.NumTriangles = 1; section.FirstIndex = 3; section.MinVertexIndex = 3; section.MaxVertexIndex = 5; } double boundArray[7] = {0, 0, 0, 200, 200, 200, 200}; //设置包围盒 FBoxSphereBounds BoundingBoxAndSphere; BoundingBoxAndSphere.Origin = FVector(boundArray[0], boundArray[1], boundArray[2]); BoundingBoxAndSphere.BoxExtent = FVector(boundArray[3], boundArray[4], boundArray[5]); BoundingBoxAndSphere.SphereRadius = boundArray[6]; RenderData->Bounds = BoundingBoxAndSphere; } // Called every frame void ACustomMeshActor::Tick(float DeltaTime) { Super::Tick(DeltaTime); }然后将这个类对象ACustomMeshActor拖放到场景中,显示结果如下: Actor只是一个空壳,具体的功能是通过各种类型的Component实现的(这一点与Unity不谋而合),这里使用的是UStaticMeshComponent,这也是Unreal场景中用的最多的Mesh组件。 这里组件初始化是在BeginPlay()中创建的,如果在构造函数中创建,那么就不能使用NewObject,而应该使用如下方法: // Sets default values ACustomMeshActor::ACustomMeshActor() { // 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; staticMeshComponent = CreateDefaultSubobject(TEXT("SceneRoot")); staticMeshComponent->SetMobility(EComponentMobility::Static); SetRootComponent(staticMeshComponent); UStaticMesh* mesh = CreateMesh(); if (mesh) { staticMeshComponent->SetStaticMesh(mesh); } }承接2,在BeginPlay()中创建和在构造函数中创建的区别就在于前者是运行时创建,而后者在程序运行之前就创建了,可以在未运行的编辑器状态下看到静态网格体和材质。 承接2,在构造函数中创建的UStaticMeshComponent移动性被设置成Static了,这时运行会提示“光照需要重建”,也就是静态对象需要烘焙光照,在工具栏"构建"->"仅构建光照"烘培一下即可。这种方式运行时渲染效率最高。 对比4,运行时创建的UStaticMeshComponent移动性可以设置成Stationary,表示这个静态物体不移动,启用缓存光照法,并且缓存动态阴影。 2.3. 解析:材质在UE编辑器分别创建了红色和绿色简单材质,注意材质是单面还是双面的,C++代码设置的要和材质蓝图中设置的要保持一致。最开始我参考的就是参考文献1中的代码,代码中设置成双面,但是我自己的材质蓝图中用的单面,程序启动直接崩溃了。 如果场景中材质显示不正确,比如每次浏览场景时的效果都不一样,说明可能法向量没有设置,我最开始就没有注意这个问题以为是光照的问题。 单面材质的话,正面是逆时针序还是顺时针序?从这个案例来看应该是逆时针。UE是个左手坐标系,X轴向前,法向量是(0, 0, 1),从法向量的一边看过去,顶点顺序是(100, 100, 50)->(100, 0, 50)->(0, 0, 50),明显是逆时针。 2.4. 解析:包围盒包围盒参数最好要设置,UE似乎默认实现了视景体裁剪,不在范围内的物体会不显示。如果在某些视角场景对象突然不显示了,可能包围盒参数没有设置正确,导致视景体裁剪错误地筛选掉了当前场景对象。 FBoxSphereBounds BoundingBoxAndSphere; //... RenderData->Bounds = BoundingBoxAndSphere; //... mesh->CalculateExtendedBounds(); //设置包围盒之后调用这个函数起效,否则会被视锥体剔除即使是一个平面,包围盒的三个Size参数之一也不能为0,否则还是可能会在某些视角场景对象不显示。 2.5. 解析:SectionMesh内部是可以进行划分的,划分成多少个section就使用多少个材质,比如这里划分了两个section,最后就使用了两个材质。如下代码所示: FStaticMeshLODResources::FStaticMeshSectionArray& Sections = LODResources.Sections; { FStaticMeshSection& section = Sections.AddDefaulted_GetRef(); section.bEnableCollision = false; section.MaterialIndex = 0; section.NumTriangles = 1; section.FirstIndex = 0; section.MinVertexIndex = 0; section.MaxVertexIndex = 2; } { FStaticMeshSection& section = Sections.AddDefaulted_GetRef(); section.bEnableCollision = false; section.MaterialIndex = 0; section.NumTriangles = 1; section.FirstIndex = 3; section.MinVertexIndex = 3; section.MaxVertexIndex = 5; } 3. 其他除了本文介绍的方法之外,也有其他的实现办法,具体可以参考文献3-5。实在是没有时间进行进一步的研究了,因此记录备份一下。另外,文献6-7可能对了解UE关于Mesh的内部实现有所帮助,笔者反正是看麻了。不得不说,这么一个微小的功能涉及到的内容还真不少,看来有的研究了。 4. 参考 UE4绘制简单三角形(二)UE4之坐标系[UE4 C++]三种方式绘制三角形Building a StaticMesh in C++ during runtimeBuild static mesh from description虚幻 – StaticMesh 分析Creating a Custom Mesh Component in UE4上一篇 目录 下一篇 代码地址 |
今日新闻 |
推荐新闻 |
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 |