- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
之所以写这个绘制简单三角形的实例其实是想知道如何在Unreal中通过代码绘制自定义Mesh,如果你会绘制一个三角形,那么自然就会绘制复杂的Mesh了。所以这是很多图形工作者的第一课.
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<UStaticMeshComponent>(this);
staticMeshComponent->SetMobility(EComponentMobility::Stationary);
SetRootComponent(staticMeshComponent);
staticMeshComponent->RegisterComponent();
UStaticMesh* mesh = CreateMesh();
if (mesh) {
staticMeshComponent->SetStaticMesh(mesh);
}
}
UStaticMesh* ACustomMeshActor::CreateMesh() {
UStaticMesh* mesh = NewObject<UStaticMesh>(staticMeshComponent);
mesh->NeverStream = true;
mesh->SetIsBuiltAtRuntime(true);
TUniquePtr<FStaticMeshRenderData> RenderData =
MakeUnique<FStaticMeshRenderData>();
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<FVector> 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<FVector2D> uvList;
uvList.Add(FVector2D(0, 1));
uvList.Add(FVector2D(0, 0));
uvList.Add(FVector2D(1, 0));
uvList.Add(FVector2D(1, 1));
// 设置顶点数据
TArray<FStaticMeshBuildVertex> StaticMeshBuildVertices;
StaticMeshBuildVertices.SetNum(vertexNum);
for (int m = 0; m < vertexNum; m++) {
StaticMeshBuildVertices[m].Position = xyzList[m];
StaticMeshBuildVertices[m].Color = FColor(255, 0, 0);
StaticMeshBuildVertices[m].UVs[0] = uvList[m];
StaticMeshBuildVertices[m].TangentX = FVector(0, 1, 0); //切线
StaticMeshBuildVertices[m].TangentY = FVector(1, 0, 0); //副切线
StaticMeshBuildVertices[m].TangentZ = FVector(0, 0, 1); //法向量
}
LODResources.bHasColorVertexData = false;
//顶点buffer
LODResources.VertexBuffers.PositionVertexBuffer.Init(StaticMeshBuildVertices);
//法线,切线,贴图坐标buffer
LODResources.VertexBuffers.StaticMeshVertexBuffer.Init(
StaticMeshBuildVertices, 1);
//设置索引数组
TArray<uint32> indices;
int numTriangles = 2;
int indiceNum = numTriangles * 3;
indices.SetNum(indiceNum);
indices[0] = 2;
indices[1] = 1;
indices[2] = 0;
indices[3] = 3;
indices[4] = 2;
indices[5] = 0;
LODResources.IndexBuffer.SetIndices(indices,
EIndexBufferStride::Type::AutoDetect);
LODResources.bHasDepthOnlyIndices = false;
LODResources.bHasReversedIndices = false;
LODResources.bHasReversedDepthOnlyIndices = false;
// LODResources.bHasAdjacencyInfo = false;
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;
}
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<UStaticMeshComponent>(TEXT("SceneRoot"));
staticMeshComponent->SetMobility(EComponentMobility::Static);
SetRootComponent(staticMeshComponent);
UStaticMesh* mesh = CreateMesh();
if (mesh) {
staticMeshComponent->SetStaticMesh(mesh);
}
}
承接2,在BeginPlay()中创建和在构造函数中创建的区别就在于前者是运行时创建,而后者在程序运行之前就创建了,可以在未运行的编辑器状态下看到静态网格体和材质.
承接2,在构造函数中创建的UStaticMeshComponent移动性被设置成Static了,这时运行会提示“光照需要重建”,也就是静态对象需要烘焙光照,在工具栏"构建"->"仅构建光照"烘培一下即可。这种方式运行时渲染效率最高.
对比4,运行时创建的UStaticMeshComponent移动性可以设置成Stationary,表示这个静态物体不移动,启用缓存光照法,并且缓存动态阴影.
在UE编辑器分别创建了红色和绿色简单材质,注意材质是单面还是双面的,C++代码设置的要和材质蓝图中设置的要保持一致。最开始我参考的就是参考文献1中的代码,代码中设置成双面,但是我自己的材质蓝图中用的单面,程序启动直接崩溃了.
如果场景中材质显示不正确,比如每次浏览场景时的效果都不一样,说明可能法向量没有设置,我最开始就没有注意这个问题以为是光照的问题.
单面材质的话,正面是逆时针序还是顺时针序?从这个案例来看应该是逆时针。UE是个左手坐标系,X轴向前,法向量是(0, 0, 1),从法向量的一边看过去,顶点顺序是(100, 100, 50)->(100, 0, 50)->(0, 0, 50),明显是逆时针.
包围盒参数最好要设置,UE似乎默认实现了视景体裁剪,不在范围内的物体会不显示。如果在某些视角场景对象突然不显示了,可能包围盒参数没有设置正确,导致视景体裁剪错误地筛选掉了当前场景对象.
FBoxSphereBounds BoundingBoxAndSphere;
//...
RenderData->Bounds = BoundingBoxAndSphere;
//...
mesh->CalculateExtendedBounds(); //设置包围盒之后调用这个函数起效,否则会被视锥体剔除
即使是一个平面,包围盒的三个Size参数之一也不能为0,否则还是可能会在某些视角场景对象不显示.
Mesh内部是可以进行划分的,划分成多少个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-5。实在是没有时间进行进一步的研究了,因此记录备份一下。另外,文献6-7可能对了解UE关于Mesh的内部实现有所帮助,笔者反正是看麻了。不得不说,这么一个微小的功能涉及到的内容还真不少,看来有的研究了.
上一篇 目录 下一篇 。
代码地址 。
最后此篇关于Unreal学习笔记2-绘制简单三角形的文章就讲到这里了,如果你想了解更多关于Unreal学习笔记2-绘制简单三角形的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
SDL3 提供了 SDL_RenderGeometry 函数绘制几何图形,用法和 OpenGL 差不多,先定义顶点数据,然后根据顶点数据绘制几何图形。 绘制三角形的代码如下: std::array
我想在图像上使用三角形类型按钮,但我无法执行此操作... 如何做到这一点? 最佳答案 这个project可以帮你。您可以自定义 UIButton 的形状。 关于iphone - 定制非矩形按钮 三角形
我一直在尝试找出如何使用 Python 制作彩虹三角螺旋。我可以制作一个方形螺旋,但它不会导入颜色。而且它不使用三角形。 输出应该是什么样子: 我取得的成就: 我的代码: import tur
我正在使用 this 研究三角形检测算法文章。我编写了这段代码,但不幸的是,当三角形之间存在交集时,该方法返回 false。 private boolean checkTriangleCollisio
我在资源文件中找到了几个关于如何在 Android 中绘制三角形的答案。但是我没有找到任何可以解释如何更改三角形旋转的内容。 我找到的例子:
对于编码类(class)中的作业,我应该找到一种方法让 Python 制作星号三角形,如下所示: x xx xxx 但是,无论我用我的代码做什么,我都无法做到这一点。最好的我可以得到的是:
我在绘制两个多边形时遇到问题。我想填充两个三角形,但一个大于第二个。我在 winforms 中使用 UserControl。代码: Point[] DOWN = new Point[] {new Po
如何测试三角形和正方形是否相交? 当我们知道它是正方形而不是矩形时,有什么方法可以优化它吗?此外,正方形是轴对齐的,这样应该可以进一步提升性能? 或者我应该把正方形分成三角形,然后对三角形-三角形相交
我有一个方法是画一个多边形,然后将多边形向右旋转90度,使其原来的顶点现在指向右边。 这是绘制多边形(三角形)的代码,但我不知道如何旋转它。 Point[] points = new Point[3]
我知道有高效的多边形裁剪算法(例如 Maillot、Vatti、Greiner-Hormann)。然而,这些算法适用于任意多边形,尽管它们适合我的情况,但在我看来,对像我这样的简单情况使用这种通用算法
我的问题可能很愚蠢,但我没有找到三角形 strip 使用的好例子: http://i.stack.imgur.com/KL8jk.png 像这样的顶点: A: -0.5f, -0.5f, // Bo
我正在尝试创建一个等边三角形,您可以在 fiddle 中看到它: 我的想法是,我将笔放在 (0, 0) 处,然后在 (20, 11) 处画线,但三角形看起来不正确。 最佳答案 您的三角形已被
通过编写一些逻辑代码,只是无法弄清楚如何以所需的形式获得 01 三角形的输出,三角形确实打印出来,但不是根据要求的输出。 import java.util.Scanner; import java
我一直在尝试制作一个简单的 pygame 程序来检查光标是否在三角形内部或外部。我通过找到较大三角形的面积,然后从鼠标位置到所有三个点制作三个内部三角形并找到它们的面积来完成此操作。 根据我的理解,如
我有一个方法 drawTriangle,它在 JAVA 中的 OpenGL 程序的 display() 方法中被引用。 public void drawTriangle(GL gl, int x1,
我正在尝试用 C++ 创建一个程序,该程序将数字的三角形模式放入二维数组中。 示例: 1 3 4 5 9 2 9 4 6 1 顶行是一个数字(整数),三角形的每一行比它上面的行多一
所以我最近一直在尝试学习 OpenGL,遵循了几个文本和视频教程。 我无法绘制三角形,我已经双重和三次检查我是否以正确的顺序执行了所有必要的步骤,但我显然遗漏了一些东西 在添加一些代码之前,我应该声明
我遇到了一个用递归绘制谢尔宾斯基三角形的程序。我如何解释这段代码是调用 sierpinski1 直到 n == 0,然后只绘制 3 个小三角形(每次调用一个三角形),因为 n == 0 是绘制某些东西
我有一个需要 3 个点的函数,我将使用这些点来绘制一个三角形,就好像我在使用 glVertex 函数一样。 但由于我想在避免透视变形的同时对这个三角形进行纹理贴图,我必须对其进行 segmentati
下面的代码应该为三角形添加一个 3d 对象,但我收到错误 Assets/Scripts/MakeTriangle.cs(6,28):错误 CS0120:需要对象引用才能访问非静态成员 `UnityEn
我是一名优秀的程序员,十分优秀!