欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Unreal學習之簡單三角形的繪制詳解

 更新時間:2023年02月02日 08:25:26   作者:charlee44  
之所以寫這個繪制簡單三角形的實例其實是想知道如何在Unreal中通過代碼繪制自定義Mesh,如果你會繪制一個三角形,那么自然就會繪制復雜的Mesh了。所以這是很多圖形工作者的第一課,快跟隨小編一起學習起來吧

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<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拖放到場景中,顯示結果如下:

2.2 解析:Component

1.Actor只是一個空殼,具體的功能是通過各種類型的Component實現的(這一點與Unity不謀而合),這里使用的是UStaticMeshComponent,這也是Unreal場景中用的最多的Mesh組件。

2.這里組件初始化是在BeginPlay()中創(chuàng)建的,如果在構造函數中創(chuàng)建,那么就不能使用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);
    }
}

3.承接2,在BeginPlay()中創(chuàng)建和在構造函數中創(chuàng)建的區(qū)別就在于前者是運行時創(chuàng)建,而后者在程序運行之前就創(chuàng)建了,可以在未運行的編輯器狀態(tài)下看到靜態(tài)網格體和材質。

4.承接2,在構造函數中創(chuàng)建的UStaticMeshComponent移動性被設置成Static了,這時運行會提示“光照需要重建”,也就是靜態(tài)對象需要烘焙光照,在工具欄"構建"->"僅構建光照"烘培一下即可。這種方式運行時渲染效率最高。

5.對比4,運行時創(chuàng)建的UStaticMeshComponent移動性可以設置成Stationary,表示這個靜態(tài)物體不移動,啟用緩存光照法,并且緩存動態(tài)陰影。

2.3 解析:材質

在UE編輯器分別創(chuàng)建了紅色和綠色簡單材質,注意材質是單面還是雙面的,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 解析:Section

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. 其他

除了本文介紹的方法之外,也有其他的實現辦法,具體可以參考文獻3-5。實在是沒有時間進行進一步的研究了,因此記錄備份一下。另外,文獻6-7可能對了解UE關于Mesh的內部實現有所幫助,筆者反正是看麻了。不得不說,這么一個微小的功能涉及到的內容還真不少,看來有的研究了。

以上就是Unreal學習之簡單三角形的繪制詳解的詳細內容,更多關于Unreal繪制三角形的資料請關注腳本之家其它相關文章!

相關文章

  • C語言 風靡一時的黃金礦工游戲實現流程詳解

    C語言 風靡一時的黃金礦工游戲實現流程詳解

    《黃金礦工》是一款非常經典的游戲。在游戲中,玩家通過不斷挖礦,獲取金子,最終能夠闖入下一關。在這個過程中,會不斷有巖石、煙霧、老鼠來搗亂,甚至還會出現扛著炸藥包的小老鼠,玩家必須戰(zhàn)勝它們,才能進入更深的礦坑
    2021-11-11
  • 關于C++類的成員初始化列表的相關問題

    關于C++類的成員初始化列表的相關問題

    下面小編就為大家?guī)硪黄P于C++類的成員初始化列表的相關問題。小編覺得挺
    2016-05-05
  • 關于Qt添加opencv和libtorch庫的問題

    關于Qt添加opencv和libtorch庫的問題

    這篇文章主要介紹了Qt添加opencv和libtorch庫的相關知識,兩種方法一種是通過手動添加,一種是通過qt creator添加,需要的朋友可以參考下
    2022-01-01
  • 在Visual Studio中用C++語言創(chuàng)建DLL動態(tài)鏈接庫圖文教程

    在Visual Studio中用C++語言創(chuàng)建DLL動態(tài)鏈接庫圖文教程

    這篇文章主要介紹了在Visual Studio中用C++語言創(chuàng)建DLL動態(tài)鏈接庫圖文教程,本文詳細講解了DLL庫的創(chuàng)建過程,并給出了代碼示例,需要的朋友可以參考下
    2014-09-09
  • C++二分查找(折半查找)算法實例詳解

    C++二分查找(折半查找)算法實例詳解

    這篇文章主要介紹了C++二分查找(折半查找)算法,結合實例形式詳細分析了二分查找算法的原理、思想、實現方法與相關操作技巧,需要的朋友可以參考下
    2017-05-05
  • C++實現LeetCode(52.N皇后問題之二)

    C++實現LeetCode(52.N皇后問題之二)

    這篇文章主要介紹了C++實現LeetCode(52.N皇后問題之二),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-07-07
  • C語言中的setlinebuf()、utmpname()、rewind函數使用

    C語言中的setlinebuf()、utmpname()、rewind函數使用

    這篇文章主要介紹了C語言中的setlinebuf()、utmpname()、rewind函數使用,是C語言中操作文件的一些基本函數,需要的朋友可以參考下
    2015-08-08
  • DOS簡易版C語言貪吃蛇

    DOS簡易版C語言貪吃蛇

    這篇文章主要為大家詳細介紹了DOS簡易版C語言貪吃蛇,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • C++學習筆記之類與對象詳解

    C++學習筆記之類與對象詳解

    這篇文章主要為大家介紹了C++類與對象,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12
  • C++中Digraphs、Trigraphs和Tokens的深入講解

    C++中Digraphs、Trigraphs和Tokens的深入講解

    這篇文章主要給大家介紹了關于C++中Digraphs、Trigraphs和Tokens的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用C++具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-09-09

最新評論