问题描述
我正在尝试通过自定义组件加载obj文件来呈现它。
左侧的模型使用100的比例从搅拌器中导出,而右侧的模型来自相同的.obj文件,但直接从代码中进行解析并通过构建自定义的顶点工厂,索引缓冲区和位置进行渲染用FMeshBatch::bWireframe = true
缓冲:
如您所见,右侧的模型呈现出明显的视觉伪像。
我首先创建了一个扩展了UPrimitiveComponent
的自定义类和一个扩展了FPrimitiveComponent
的自定义代理:
// UTestPrimitiveComponent.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "UTestPrimitiveComponent.generated.h"
class FPrimitiveSceneProxy;
class FObjectinitializer;
class UStaticmesh;
class FStaticmeshDescription;
struct FRawMesh;
USTRUCT()
struct FOBJMesh {
GENERATED_BODY()
TArray<FVector> vertices;
TArray<FVector> normals;
TArray<FVector2D> uvs;
TArray<int32> tex_indices; // Unused
TArray<uint32> vertex_indices;
TArray<int32> normals_indices; // Unused
};
UCLASS(ClassGroup = (Custom),Meta = (BlueprintSpawnableComponent))
class RENDERINGPIPELINE_API UTestPrimitiveComponent :
public UPrimitiveComponent
{
GENERATED_BODY()
private:
FOBJMesh* TheMesh;
void ParseLine(FString& line,FRawMesh& Desc);
void ReadnormalFromLine(FString& line,FVector& normal);
void ReadUVFromLine(FString& line,FVector2D& uv);
void ReadVertexFromLine(FString& line,FVector& vertex);
void ReadFaceInfoFromLine(FString& element,FRawMesh& Desc);
public:
UTestPrimitiveComponent(const FObjectinitializer&);
void BeginPlay() override;
__forceinline FOBJMesh* GetMesh() { return TheMesh; }
FPrimitiveSceneProxy* CreateSceneProxy() override;
};
这是代理和组件的清单:
#include "UTestPrimitiveComponent.h"
#include "Components/MeshComponent.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "Engine/Staticmesh.h"
#include "PrimitiveSceneProxy.h"
#include "StaticmeshDescription.h"
#include "Rendering/StaticmeshVertexBuffer.h"
#include "RawMesh.h"
#include "RHICommandList.h"
#include "PrimitiveSceneProxy.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Misc/DefaultValueHelper.h"
#include "Containers/Array.h"
#include "EngineMinimal.h"
class UTestPrimitiveComponent;
template <typename T>
static FVertexBufferRHIRef CreateVertexBufferWithData(TArray<T>& data) {
FRHIResourceCreateInfo info;
auto size = sizeof (T)* data.Num();
FVertexBufferRHIRef buf = RHICreateVertexBuffer(size,BUF_Static | BUF_ShaderResource,info);
void* ptr = static_cast<void*>(RHILockVertexBuffer(buf,size,RLM_writeonly));
FMemory::Memcpy(ptr,static_cast<void*>(data.GetData()),size);
RHIUnlockVertexBuffer(buf);
return buf;
}
struct FOBJIndexBuffer : public FIndexBuffer {
TArray<uint32> Indices;
virtual void InitRHI() override {
FRHIResourceCreateInfo info;
auto ElementSizeInBytes = sizeof(uint32);
auto ArraySize = ElementSizeInBytes * Indices.Num();
IndexBufferRHI = RHICreateIndexBuffer(ElementSizeInBytes,ArraySize,BUF_Static,info);
void* ptr = static_cast<void*>(RHILockIndexBuffer(IndexBufferRHI,RLM_writeonly));
FMemory::Memcpy(ptr,static_cast<void*>(Indices.GetData()),ArraySize);
RHIUnlockIndexBuffer(IndexBufferRHI);
}
};
struct FOBjpositionBuffer : public FVertexBuffer {
TArray<FVector> Vertices;
FShaderResourceViewRHIRef PositionComponentSRV;
virtual void InitRHI() override {
FRHIResourceCreateInfo info;
VertexBufferRHI = CreateVertexBufferWithData(Vertices);
if (VertexBufferRHI)
{
PositionComponentSRV =
RHICreateShaderResourceView(FShaderResourceViewInitializer(VertexBufferRHI,PF_R32_FLOAT));
}
}
};
struct FOBJMeshBuffers : public FRenderResource {
struct FTangentData {
FPackednormal X;
FPackednormal Z;
public:
FTangentData(FPackednormal InZ,FPackednormal InX)
: X(InX),Z(InZ) {}
};
FVertexBuffer TexCoordsBuffer;
FVertexBuffer TangentsBuffer;
TArray<FVector2D> uvs;
TArray<FTangentData> Tangents;
FShaderResourceViewRHIRef TexCoordsBufferSRV;
FShaderResourceViewRHIRef TangentsBufferSRV;
virtual void InitRHI() override {
TexCoordsBuffer.VertexBufferRHI = CreateVertexBufferWithData(uvs);
if (TexCoordsBuffer.VertexBufferRHI) {
// PF_G16R16F if using half precision,todo when implementing full thing
TexCoordsBufferSRV = RHICreateShaderResourceView(FShaderResourceViewInitializer(TexCoordsBuffer.VertexBufferRHI,PF_G32R32F));
}
TangentsBuffer.VertexBufferRHI = CreateVertexBufferWithData(Tangents);
if (TangentsBuffer.VertexBufferRHI) {
// PF_R8G8B8A8_SnorM if using half precision,todo when implementing full thing
TangentsBufferSRV =
RHICreateShaderResourceView(FShaderResourceViewInitializer(TangentsBuffer.VertexBufferRHI,PF_R8G8B8A8_SnorM));
}
BeginInitResource(&TexCoordsBuffer);
BeginInitResource(&TangentsBuffer);
}
};
struct FOBJRenderData {
FOBjpositionBuffer OBjpositionBuffer;
FOBJMeshBuffers OBJMeshBuffers;
};
class OBJSceneProxy : public FPrimitiveSceneProxy {
public:
UMaterialInterface* Material;
FLocalVertexFactory VertexFactory;
FOBJMeshBuffers MeshBuffers;
FOBjpositionBuffer PositionBuffer;
FOBJIndexBuffer IndexBuffer;
UTestPrimitiveComponent* TheComponent;
OBJSceneProxy(UTestPrimitiveComponent* Component)
: FPrimitiveSceneProxy(Component,TEXT("OBJ Component")),VertexFactory(GetScene().GetFeatureLevel(),"FObjSceneProxy"),TheComponent(Component) {
IndexBuffer.Indices = TheComponent->GetMesh()->vertex_indices;
PositionBuffer.Vertices = TheComponent->GetMesh()->vertices;
MeshBuffers.uvs = Component->GetMesh()->uvs;
for (int i = 1; i < IndexBuffer.Indices.Num(); i++) {
// This block of code here is mostly wrong; Still,it shouldn't impact the rendering as much
// Blender y u start counting from 1
auto CurIndex = IndexBuffer.Indices[i] - 1;
auto PrevIndex = IndexBuffer.Indices[i - 1] - 1;
auto prevVertex = PositionBuffer.Vertices[PrevIndex];
auto curVertex = PositionBuffer.Vertices[CurIndex];
auto TangentRight = (curVertex - prevVertex).GetSafenormal();
auto TangentFront = FVector::Crossproduct(TangentRight,curVertex.GetSafenormal());
MeshBuffers.Tangents.Add(FOBJMeshBuffers::FTangentData(TangentFront,TangentRight));
}
BeginInitResource(&IndexBuffer);
BeginInitResource(&PositionBuffer);
BeginInitResource(&MeshBuffers);
InitFactory();
BeginInitResource(&VertexFactory);
Material = UMaterial::GetDefaultMaterial(MD_Surface);
}
virtual ~OBJSceneProxy()
{
IndexBuffer.ReleaseResource();
PositionBuffer.ReleaseResource();
MeshBuffers.TexCoordsBuffer.ReleaseResource();
MeshBuffers.TangentsBuffer.ReleaseResource();
MeshBuffers.ReleaseResource();
VertexFactory.ReleaseResource();
}
void InitFactory() {
struct FactoryParams {
FLocalVertexFactory* Factory;
FOBJMeshBuffers* MeshBuffers;
FOBjpositionBuffer* PositionBuffer;
} Params;
Params.Factory = &VertexFactory;
Params.PositionBuffer = &PositionBuffer;
Params.MeshBuffers = &MeshBuffers;
ENQUEUE_RENDER_COMMAND(OBJInitVertexFactory)([Params] (FRHICommandListImmediate& RHICmdList) {
FLocalVertexFactory::FDataType Data;
// Position stuff
Data.PositionComponent = FVertexStreamComponent(
Params.PositionBuffer,sizeof(FVector),VET_Float3
);
Data.PositionComponentSRV = Params.PositionBuffer->PositionComponentSRV;
// Tex Coords stuff
Data.TextureCoordinatesSRV = Params.MeshBuffers->TexCoordsBufferSRV;
Data.TextureCoordinates.Add(FVertexStreamComponent(
&Params.MeshBuffers->TexCoordsBuffer,sizeof(FVector2D),VET_Float4,// Doppio della dimensione (VET_Float2): Why?
EVertexStreamUsage::ManualFetch
));
// Tangents stuff
Data.TangentsSRV = Params.MeshBuffers->TangentsBufferSRV;
typedef FPackednormal TangentType;
auto TangentElemType = VET_Short2;
auto TangentXOffset = STRUCT_OFFSET(FOBJMeshBuffers::FTangentData,X);
auto TangentZOffset = STRUCT_OFFSET(FOBJMeshBuffers::FTangentData,Z);
auto TangentSizeInBytes = sizeof(TangentType);
Data.TangentBasisComponents[0] = FVertexStreamComponent(
&Params.MeshBuffers->TangentsBuffer,TangentXOffset,TangentSizeInBytes,TangentElemType,EVertexStreamUsage::ManualFetch
);
Data.TangentBasisComponents[1] = FVertexStreamComponent(
&Params.MeshBuffers->TangentsBuffer,TangentZOffset,EVertexStreamUsage::ManualFetch
);
Data.LightMapCoordinateComponent = FVertexStreamComponent(
&Params.MeshBuffers->TexCoordsBuffer,VET_Float2,EVertexStreamUsage::ManualFetch
);
FColorVertexBuffer::BindDefaultColorVertexBuffer(Params.Factory,Data,FColorVertexBuffer::NullBindStride::FColorSizeforComponentOverride);
Params.Factory->SetData(Data);
Params.Factory->InitResource();
});
}
// (StartIndex + IndexCount) * IndexBuffer->GetStride()
void DrawStaticElements(FStaticPrimitiveDrawInterface* DrawInterface)
{
check(IsInRenderingThread());
FMaterialRenderProxy* Proxy = Material->GetRenderProxy();
FMeshBatch batch;
batch.bWireframe = true;
batch.VertexFactory = &VertexFactory;
batch.Type = PT_TriangleList;
batch.MaterialRenderProxy = Proxy;
batch.DepthPriorityGroup = SDPG_World;
batch.CastShadow = true;
batch.LOdindex = 0;
auto& BatchElement = batch.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
// for i = FirstIndex to NumPrimitives ?
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
DrawInterface->DrawMesh(batch,MAX_FLT);
}
FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const
{
// Partially copied from StaticmeshRenderer.cpp
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = true;
Result.bRenderInMainPass = true;
Result.bVeLocityRelevance = IsMovable() && Result.bOpaque && Result.bRenderInMainPass;
Result.bStaticRelevance = true;
Result.bDynamicRelevance = false;
return Result;
}
};
UTestPrimitiveComponent::UTestPrimitiveComponent(const FObjectinitializer& Objectinitializer) :
UPrimitiveComponent(Objectinitializer) {
UActorComponent::PrimaryComponentTick.bCanEverTick = true;
TheMesh = new FOBJMesh();
FString result;
auto gamePath = FPaths::ProjectContentDir();
auto fullPath = FPaths::Combine(gamePath,TEXT("RawContent"),TEXT("cube.obj"));
FFileHelper::LoadFiletoString(result,*fullPath);
FRawMesh meshInfo;
TArray<FString> lines;
result.ParseIntoArrayLines(lines);
for (auto line : lines) {
ParseLine(line,meshInfo);
}
}
void UTestPrimitiveComponent::BeginPlay()
{
GEngine->AddOnScreenDebugMessage(1,5.0f,FColor::Red,FString::Printf(TEXT("Vertices: %d,UVs: %d,normals: %d"),TheMesh->vertices.Num(),TheMesh->uvs.Num(),TheMesh->normals.Num()));
GEngine->AddOnScreenDebugMessage(2,FString::Printf(TEXT("VIndices: %d,UVIndices: %d,NIndices: %d"),TheMesh->vertex_indices.Num(),TheMesh->tex_indices.Num(),TheMesh->normals_indices.Num()));
}
FPrimitiveSceneProxy* UTestPrimitiveComponent::CreateSceneProxy()
{
return (FPrimitiveSceneProxy*)(new OBJSceneProxy(this));
}
void UTestPrimitiveComponent::ParseLine(FString& line,FRawMesh& Desc)
{
if (line[0] == 'v') {
if (line[1] == 'n') {
// It is a normal
line.RemoveFromStart(TEXT("vn "));
FVector normal;
ReadnormalFromLine(line,normal);
TheMesh->normals.Add(normal);
}
else if (line[1] == 't') {
// It is an uv vertex
line.RemoveFromStart(TEXT("vt "));
FVector2D uv;
ReadUVFromLine(line,uv);
TheMesh->uvs.Add(uv);
}
else {
// It is a vertex
line.RemoveFromStart(TEXT("v "));
FVector vertex;
ReadVertexFromLine(line,vertex);
TheMesh->vertices.Add(vertex);
}
}
else if (line[0] == 'f') {
line.RemoveFromStart("f ");
TArray<FString> elements;
line.ParseIntoArray(elements,TEXT(" "));
for (int i = 0; i < elements.Num(); i++) {
ReadFaceInfoFromLine(elements[i],Desc);
}
}
// Ignore the line if it's a comment,a usrlib/matlib/other stuff directive
else return;
}
void UTestPrimitiveComponent::ReadnormalFromLine(FString& line,FVector& normal)
{
TArray<FString> elements;
line.ParseIntoArray(elements,TEXT(" "));
FDefaultValueHelper::ParseFloat(elements[0],normal.X);
FDefaultValueHelper::ParseFloat(elements[1],normal.Y);
FDefaultValueHelper::ParseFloat(elements[2],normal.Z);
}
void UTestPrimitiveComponent::ReadUVFromLine(FString& line,FVector2D& uv)
{
TArray<FString> elements;
line.ParseIntoArray(elements,uv.X);
FDefaultValueHelper::ParseFloat(elements[1],uv.Y);
if (elements.Num() == 3) {
// We have a w! Ignore,UE4 does not support it
// FDefaultValueHelper::ParseFloat(elements[2],uv.Z);
}
}
void UTestPrimitiveComponent::ReadVertexFromLine(FString& line,FVector& vertex)
{
TArray<FString> elements;
line.ParseIntoArray(elements,vertex.X);
FDefaultValueHelper::ParseFloat(elements[1],vertex.Y);
FDefaultValueHelper::ParseFloat(elements[2],vertex.Z);
if (elements.Num() == 4) {
// We have a w! UE4 does not support it
// FDefaultValueHelper::ParseFloat(elements[3],vertex.W);
}
}
void UTestPrimitiveComponent::ReadFaceInfoFromLine(FString& element,FRawMesh& Desc)
{
int vertex_index;
// Splitting the vi/ui/ni triplet
TArray<FString> tripletString;
element.ParseIntoArray(tripletString,TEXT("/"));
FDefaultValueHelper::ParseInt(tripletString[0],vertex_index);
TheMesh->vertex_indices.Add(vertex_index);
/* UE4 does not use this stuff
if (tripletString.Num() > 1) {
FDefaultValueHelper::ParseInt(tripletString[1],uv_index);
TheMesh->tex_indices.Add(uv_index);
}
if (tripletString.Num() > 2) {
FDefaultValueHelper::ParseInt(tripletString[2],normal_index);
TheMesh->normals_indices.Add(normal_index);
}
*/
}
我完全不知道为什么渲染损坏了,有人可以帮我吗?
解决方法
已解决,问题是Blender开始从1开始计算顶点(所以,问题是我如何读取obj文件)