修复UAV贴图创建

This commit is contained in:
zhangqing
2025-11-06 19:16:19 +08:00
parent 94a19cbdf4
commit b09c1e512f
9 changed files with 428 additions and 603 deletions
+1 -3
View File
@@ -1,12 +1,10 @@
{ {
"version": "1.0", "version": "1.0",
"components": [ "components": [
"Component.Unreal.Debugger",
"Component.Unreal.Ide",
"Microsoft.Net.Component.4.6.2.TargetingPack", "Microsoft.Net.Component.4.6.2.TargetingPack",
"Microsoft.VisualStudio.Component.Unreal.Workspace",
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL", "Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL",
"Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64", "Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64",
"Microsoft.VisualStudio.Component.VC.Llvm.Clang",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.Windows11SDK.22621", "Microsoft.VisualStudio.Component.Windows11SDK.22621",
"Microsoft.VisualStudio.Workload.CoreEditor", "Microsoft.VisualStudio.Workload.CoreEditor",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"FileVersion": 3, "FileVersion": 3,
"EngineAssociation": "{DE41D031-4C3F-817F-365A-5AAD9A1CE2AD}", "EngineAssociation": "5.5",
"Category": "", "Category": "",
"Description": "", "Description": "",
"Modules": [ "Modules": [
Binary file not shown.
+333 -508
View File
@@ -3,611 +3,436 @@
#include "IronBlock.h" #include "IronBlock.h"
#include "FDeformComputeShader.h" #include "FDeformComputeShader.h" // 确保这个头文件存在且包含了 FDeformationCSDispatchParams
#include "StaticMeshResources.h" #include "StaticMeshResources.h"
#include "TextureResource.h" #include "TextureResource.h"
#include "Engine/TextureRenderTarget2D.h" #include "Engine/TextureRenderTarget2D.h"
#include "Engine/Texture.h"
#include "Engine/StaticMesh.h" #include "Engine/StaticMesh.h"
#include "Engine/Texture2D.h" #include "Engine/Texture2D.h"
#include "Materials/MaterialInstanceDynamic.h" #include "Materials/MaterialInstanceDynamic.h"
#include "PhysicsEngine/BodySetup.h" #include "PhysicsEngine/BodySetup.h"
#include "RHICommandList.h"
#include "RenderGraphBuilder.h" // 包含 FRDGBuilder
#include "Engine/Texture.h"
#include "GenericPlatform/GenericPlatformProcess.h"
// Sets default values // Sets default values
AIronBlock::AIronBlock(): StaticMeshComp(nullptr), PositionTexture0(nullptr), PositionTexture1(nullptr), AIronBlock::AIronBlock() : StaticMeshComp(nullptr),
PositionRenderTarget(nullptr), PositionRenderTarget0(nullptr), PositionRenderTarget1(nullptr),
DynMaterial(nullptr), DynMaterial(nullptr),
CurrentImpactPoint(), CurrentImpactPoint(),
CurrentImpactNormal() CurrentImpactNormal()
{ {
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. // 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; PrimaryActorTick.bCanEverTick = true;
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent"));
RootComponent = StaticMeshComp;
// 设置默认网格(立方体)
UStaticMesh* DefaultMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Engine/BasicShapes/Cube"));
if (DefaultMesh)
{
StaticMeshComp->SetStaticMesh(DefaultMesh);
}
} }
// Called when the game starts or when spawned // Called when the game starts or when spawned
void AIronBlock::BeginPlay() void AIronBlock::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
//CalculateLocalBounds(); InitializePositionTexture();
InitializePositionTexture();
}
UTexture2D* AIronBlock::CreatePositionTexture(const FString& TexName, int32 Size, const TArray<FLinearColor>& InitialData)
{
// 创建纹理对象
UTexture2D* NewTexture = NewObject<UTexture2D>(
this,
FName(*TexName),
RF_Transient
);
// 设置基本属性
//NewTexture->Filter = TF_Nearest;
NewTexture->SRGB = false;
NewTexture->bNoTiling = true;
NewTexture->AddressX = TA_Clamp;
NewTexture->AddressY = TA_Clamp;
//NewTexture->CompressionSettings = TC_HDR;
//NewTexture->MipGenSettings = TMGS_NoMipmaps;
//NewTexture->NeverStream = true;
//NewTexture->LODGroup = TEXTUREGROUP_RenderTarget; // 关键:标记为渲染目标组
// 设置纹理尺寸和格式
// NewTexture->SetPlatformData(new FTexturePlatformData());
// NewTexture->GetPlatformData()->SizeX = Size;
// NewTexture->GetPlatformData()->SizeY = Size;
// // 使用兼容格式
// EPixelFormat SafeFormat = PF_A32B32G32R32F;
// NewTexture->GetPlatformData()->PixelFormat = SafeFormat;
NewTexture->SetPlatformData(new FTexturePlatformData());
NewTexture->GetPlatformData()->SizeX = Size;
NewTexture->GetPlatformData()->SizeY = Size;
NewTexture->GetPlatformData()->PixelFormat = PF_R32_FLOAT;
// 添加初始Mip
FTexture2DMipMap* Mip = new FTexture2DMipMap();
//NewTexture->GetPlatformData()->Mips.Reset();
Mip->SizeX = Size;
Mip->SizeY = Size;
const int32 DataSize = Size * Size * sizeof(FLinearColor);
Mip->BulkData.Lock(LOCK_READ_WRITE);
Mip->BulkData.Realloc(DataSize);
Mip->BulkData.Unlock();
NewTexture->GetPlatformData()->Mips.Add(Mip);
// // 如果有初始数据,填充纹理
// if (InitialData.Num() >= Size * Size)
// {
// const int32 DataSize = Size * Size * sizeof(FLinearColor);
// Mip->BulkData.Lock(LOCK_READ_WRITE);
// FMemory::Memcpy(Mip->BulkData.Realloc(DataSize), InitialData.GetData(), DataSize);
// Mip->BulkData.Unlock();
// }
// else
// {
// UE_LOG(LogTemp, Warning, TEXT("Initial data size mismatch for texture %s"), *TexName);
// }
// 标记需要更新资源
NewTexture->UpdateResource();
//ETextureDimension Dimension = NewTexture->GetResource()->GetTextureRHI()->GetDesc().Dimension;
return NewTexture;
}
void AIronBlock::OnEndFrameInitializeTextures()
{
// 确保纹理资源已创建
if (PositionTexture0 && PositionTexture0->GetResource() &&
PositionTexture1 && PositionTexture1->GetResource()
/*&& PositionRenderTarget && PositionRenderTarget->GetResource()*/)
{
// 等待资源提交完成
FRenderCommandFence Fence;
Fence.BeginFence();
Fence.Wait();
// 初始化纹理对
InitializeTexturePair(0);
InitializeTexturePair(1);
//CreateSafeUAV(PositionRenderTarget);
// 移除委托
FCoreDelegates::OnEndFrame.Remove(TextureInitHandle);
TextureInitHandle.Reset();
UE_LOG(LogTemp, Log, TEXT("Position textures initialized"));
}
} }
void AIronBlock::EndPlay(const EEndPlayReason::Type EndPlayReason) void AIronBlock::EndPlay(const EEndPlayReason::Type EndPlayReason)
{ {
// 清理资源 // RenderTargets 会被 UPROPERTY 自动 GC,无需手动释放 RHI 资源
if (TextureInitHandle.IsValid())
{
FCoreDelegates::OnEndFrame.Remove(TextureInitHandle);
TextureInitHandle.Reset();
}
// 释放 RHI 资源 Super::EndPlay(EndPlayReason);
ENQUEUE_RENDER_COMMAND(ReleaseResources)(
[this](FRHICommandListImmediate& RHICmdList)
{
for (int i = 0; i < 2; i++)
{
TexturePairs->ReleaseRHI();
}
});
Super::EndPlay(EndPlayReason);
} }
// void CreateTexture() // 安全的渲染目标获取
// { FTextureRHIRef AIronBlock::GetRenderTargetRHI(UTextureRenderTarget2D* RT)
// const int32 RequestedMipIdx = 0;
// const FTexture2DMipMap* RequestedMip = GetPlatformMip(RequestedMipIdx);
//
// // create texture with ResourceMem data when available
// const FRHITextureCreateDesc Desc =
// FRHITextureCreateDesc::Create2D(TEXT("FTexture2DResource"), RequestedMip->SizeX, RequestedMip->SizeY, PixelFormat)
// .SetNumMips(1)
// .SetFlags(CreationFlags)
// .SetExtData(PlatformData->GetExtData())
// .SetBulkData(ResourceMem)
// .SetClassName(Texture2DResourceName)
// .SetOwnerName(GetOwnerName());
//
// TextureRHI = RHICreateTexture(Desc);
//
// // if( ResourceMem && !State.bReadyForStreaming) // To investigate!
// if (ResourceMem)
// {
// // when using resource memory the RHI texture has already been initialized with data and won't need to have mips copied
// check(State.NumRequestedLODs == ResourceMem->GetNumMips());
// check(RequestedMip->SizeX == ResourceMem->GetSizeX() && RequestedMip->SizeY == ResourceMem->GetSizeY());
// for( int32 MipIndex=0; MipIndex < MipData.Num(); MipIndex++ )
// {
// MipData[MipIndex] = nullptr;
// MipDataSize[MipIndex] = 0;
// }
// }
// else
// {
// // Read the resident mip-levels into the RHI texture.
// for (int32 RHIMipIdx = 0; RHIMipIdx < State.NumRequestedLODs; ++RHIMipIdx)
// {
// const int32 ResourceMipIdx = RHIMipIdx + RequestedMipIdx;
// if (MipData[ResourceMipIdx])
// {
// uint32 DestPitch = -1;
// uint64 Size = ~0ULL;
// void* TheMipData = RHILockTexture2D(TextureRHI, RHIMipIdx, RLM_WriteOnly, DestPitch, false /* bLockWithinMiptail */, true /* bFlushRHIThread */, &Size);
// //check(Size != ~0ULL);
//
// GetData( ResourceMipIdx, TheMipData, DestPitch, Size );
// RHIUnlockTexture2D(TextureRHI, RHIMipIdx, false );
// }
// }
// }
// }
void AIronBlock::InitializeTexturePair(int32 Index)
{ {
if (Index < 0 || Index > 1) return; if (!RT || !RT->GetResource()) return nullptr;
return RT->GetResource()->TextureRHI;
UTexture2D* Texture = (Index == 0) ? PositionTexture0 : PositionTexture1;
if (!Texture || !Texture->GetResource()) return;
// 获取纹理引用
FTextureRHIRef RHITexture = Texture->GetResource()->TextureRHI;
if (!RHITexture.IsValid()) return;
TexturePairs[Index].RHITexture = RHITexture;
// 在渲染线程创建 UAV
ENQUEUE_RENDER_COMMAND(CreateTextureUAV)(
[this, Index](FRHICommandListImmediate& RHICmdList)
{
// 创建UAV描述
FRHITextureDesc Desc = TexturePairs[Index].RHITexture->GetDesc();
ensure(Desc.Flags & TexCreate_UAV); // 必须包含UAV标志
ensure(Desc.Format == PF_R32_FLOAT); // 匹配格式
// FRHIViewDesc::FUnorderedAccessView UAVDesc(FRHIViewDesc::FTextureUAV()
// .SetTexture2D(TextureRHI)
// .SetFormat(Desc.Format)
// );
//FUnorderedAccessViewRHIRef UAV = RHICreateUnorderedAccessView(TextureRHI, UAVDesc);
FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV();
ViewDesc.SetFormat(Desc.Format);
ViewDesc.SetDimensionFromTexture(TexturePairs[Index].RHITexture);
// ViewDesc.SetDimension(ETextureDimension::Texture2D);
// ViewDesc.SetPlane(ERHITexturePlane::Primary);
// ViewDesc.SetMipLevel(0);
// ViewDesc.SetArrayRange(0,1);
// 使用RDG安全创建
// FRDGBuilder GraphBuilder(RHICmdList);
// FRDGTextureRef RDGTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(TexturePairs[Index].RHITexture, TEXT("PositionTexture")));
// FRDGTextureUAVDesc UAVDesc(RDGTexture);
// TexturePairs[Index].UAV = GraphBuilder.CreateUAV(UAVDesc);
// 立即执行
// GraphBuilder.Execute();
//ViewDesc.SetDimensionFromTexture(TexturePairs[Index].RHITexture);
TexturePairs[Index].UAV = RHICmdList.CreateUnorderedAccessView(TexturePairs[Index].RHITexture,ViewDesc);
UE_LOG(LogTemp, Log, TEXT("Created UAV for texture %d"), Index);
}
);
} }
void AIronBlock::GetStaticMeshVertexPositions(TArray<FVector3f>& OutPositions) void AIronBlock::GetStaticMeshVertexPositions(TArray<FVector3f>& OutPositions)
{ {
OutPositions.Empty(); OutPositions.Empty();
if (!StaticMeshComp || !StaticMeshComp->GetStaticMesh()) if (!StaticMeshComp || !StaticMeshComp->GetStaticMesh())
return; return;
// 获取静态网格资源 // 获取静态网格资源
UStaticMesh* StaticMesh = StaticMeshComp->GetStaticMesh(); UStaticMesh* StaticMesh = StaticMeshComp->GetStaticMesh();
if (StaticMesh->GetRenderData() && StaticMesh->GetRenderData()->LODResources.Num() > 0) // 确保有有效的 RenderData 和 LOD
{ if (StaticMesh->GetRenderData() && StaticMesh->GetRenderData()->LODResources.Num() > 0)
// 获取LOD0的顶点数据 {
FStaticMeshLODResources& LOD = StaticMesh->GetRenderData()->LODResources[0]; // 获取LOD0的顶点数据
const FPositionVertexBuffer& VertexBuffer = LOD.VertexBuffers.PositionVertexBuffer; FStaticMeshLODResources& LOD = StaticMesh->GetRenderData()->LODResources[0];
const FPositionVertexBuffer& VertexBuffer = LOD.VertexBuffers.PositionVertexBuffer;
const int32 VertexCount = VertexBuffer.GetNumVertices(); const int32 VertexCount = VertexBuffer.GetNumVertices();
OutPositions.SetNum(VertexCount); OutPositions.SetNum(VertexCount);
for (int32 i = 0; i < VertexCount; i++) for (int32 i = 0; i < VertexCount; i++)
{ {
OutPositions[i] = VertexBuffer.VertexPosition(i); OutPositions[i] = VertexBuffer.VertexPosition(i);
} }
} }
} }
/**
* 核心修复:创建一个配置好用于变形计算的 RenderTarget
*/
UTextureRenderTarget2D* AIronBlock::CreateDeformRT(const FString& RTName, int32 Size)
{
UTextureRenderTarget2D* RenderTarget = NewObject<UTextureRenderTarget2D>(this, FName(*RTName));
if (RenderTarget)
{
// **关键修复 1:** 设置为32位浮点数格式,以匹配 FLinearColor 和 CS 的 PF_A32B32G32R32F
RenderTarget->RenderTargetFormat = RTF_RGBA32f;
// **关键修复 2:** 明确告诉引擎我们需要为这个纹理创建UAV
RenderTarget->bCanCreateUAV = true;
RenderTarget->bNeedsTwoCopies = true; // 告知引擎这个纹理会被用于Ping-Pong,优化内存
RenderTarget->SizeX = Size;
RenderTarget->SizeY = Size;
RenderTarget->bAutoGenerateMips = false;
RenderTarget->AddressX = TA_Clamp;
RenderTarget->AddressY = TA_Clamp;
// 立即创建RHI资源
RenderTarget->UpdateResource();
}
return RenderTarget;
}
void AIronBlock::InitializePositionTexture() void AIronBlock::InitializePositionTexture()
{ {
// 创建静态网格组件 CalculateLocalBounds();
if (!StaticMeshComp)
{
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent"));
RootComponent = StaticMeshComp;
// 设置默认网格(立方体) // 获取顶点位置 (用于计算初始UV->WorldPos)
UStaticMesh* DefaultMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Engine/BasicShapes/Cube")); TArray<FVector3f> VertexPositions;
if (DefaultMesh) GetStaticMeshVertexPositions(VertexPositions);
{
StaticMeshComp->SetStaticMesh(DefaultMesh);
}
}
// 计算本地边界 // 创建位置纹理
CalculateLocalBounds(); const int32 TextureSize = 256; // 纹理尺寸
TArray<FLinearColor> InitialData;
InitialData.SetNum(TextureSize * TextureSize);
// 获取顶点位置 // 初始化纹理数据 (将UV映射到本地包围盒的XY平面)
TArray<FVector3f> VertexPositions; for (int32 Y = 0; Y < TextureSize; ++Y)
GetStaticMeshVertexPositions(VertexPositions); {
for (int32 X = 0; X < TextureSize; ++X)
{
int32 Index = Y * TextureSize + X;
FVector2D UV(FVector2D(X, Y) / (TextureSize - 1.0f)); // 使用 255.0 或 (Size-1)
// 创建位置纹理 // 将UV坐标转换为本地空间位置 (XY平面)
const int32 TextureSize = 256; // 纹理尺寸 FVector LocalPos(
TArray<FLinearColor> InitialData; FMath::Lerp(LocalBounds.Min.X, LocalBounds.Max.X, UV.X),
InitialData.SetNum(TextureSize * TextureSize); FMath::Lerp(LocalBounds.Min.Y, LocalBounds.Max.Y, UV.Y),
LocalBounds.Min.Z // 假设初始是一个平面
);
// 初始化纹理数据 InitialData[Index] = FLinearColor(LocalPos.X, LocalPos.Y, LocalPos.Z, 1.0f);
for (int32 Y = 0; Y < TextureSize; ++Y) }
{ }
for (int32 X = 0; X < TextureSize; ++X)
{
int32 Index = Y * TextureSize + X;
FVector2D UV(FVector2D(X, Y) / (TextureSize - 1));
// 将UV坐标转换为本地空间位置 // --- 关键修复: 创建两个 RenderTarget ---
FVector LocalPos( PositionRenderTarget0 = CreateDeformRT(TEXT("PositionRT0"), TextureSize);
FMath::Lerp(LocalBounds.Min.X, LocalBounds.Max.X, UV.X), PositionRenderTarget1 = CreateDeformRT(TEXT("PositionRT1"), TextureSize);
FMath::Lerp(LocalBounds.Min.Y, LocalBounds.Max.Y, UV.Y),
LocalBounds.Min.Z
);
InitialData[Index] = FLinearColor(LocalPos.X, LocalPos.Y, LocalPos.Z, 1.0f); // --- 将初始数据写入GPU ---
} // 我们需要将CPU端的 InitialData 写入到GPU纹理中
} // 最简单的方法是创建一个临时的UTexture2D,用BulkData上传,然后拷贝到RT
// 创建纹理对 UTexture2D* TempTexture = UTexture2D::CreateTransient(TextureSize, TextureSize, PF_A32B32G32R32F); // 必须匹配格式
PositionTexture0 = CreatePositionTexture(TEXT("PositionTexture0"), TextureSize, InitialData); if (TempTexture && PositionRenderTarget0 && PositionRenderTarget1)
PositionTexture1 = CreatePositionTexture(TEXT("PositionTexture1"), TextureSize, InitialData); {
// 锁定 Mip 并拷贝数据
FTexture2DMipMap& Mip = TempTexture->GetPlatformData()->Mips[0];
void* MipData = Mip.BulkData.Lock(LOCK_READ_WRITE);
FMemory::Memcpy(MipData, InitialData.GetData(), InitialData.Num() * sizeof(FLinearColor));
Mip.BulkData.Unlock();
TempTexture->UpdateResource(); // 提交到GPU
// 创建渲染目标 // 获取RHI资源
PositionRenderTarget = NewObject<UTextureRenderTarget2D>(this); FTextureRHIRef DestRT0_RHI = GetRenderTargetRHI(PositionRenderTarget0);
PositionRenderTarget->SizeX = TextureSize; FTextureRHIRef DestRT1_RHI = GetRenderTargetRHI(PositionRenderTarget1);
PositionRenderTarget->SizeY = TextureSize; FTextureRHIRef SrcTex_RHI = TempTexture->GetResource() ? TempTexture->GetResource()->TextureRHI : nullptr;
PositionRenderTarget->RenderTargetFormat = RTF_RGBA16f;
PositionRenderTarget->bAutoGenerateMips = false;
PositionRenderTarget->UpdateResource();
// 设置变形材质 if (DestRT0_RHI && DestRT1_RHI && SrcTex_RHI)
SetupDeformationMaterial(); {
// 在渲染线程上执行拷贝
ENQUEUE_RENDER_COMMAND(InitializeDeformRTs)(
[DestRT0_RHI, DestRT1_RHI, SrcTex_RHI](FRHICommandListImmediate& RHICmdList)
{
// 将初始数据拷贝到两个RT
RHICmdList.CopyTexture(SrcTex_RHI, DestRT0_RHI, {});
RHICmdList.CopyTexture(SrcTex_RHI, DestRT1_RHI, {});
});
// 初始化纹理对 // 确保拷贝完成
InitializeTexturePair(0); FlushRenderingCommands();
InitializeTexturePair(1); }
}
// 延迟初始化纹理资源 // 设置变形材质
TextureInitHandle = FDelegateHandle(); SetupDeformationMaterial();
GetWorld()->GetTimerManager().SetTimerForNextTick([this]() UE_LOG(LogTemp, Log, TEXT("Position textures initialized"));
{
OnEndFrameInitializeTextures();
});
}
// 创建UAV的安全方法
FUnorderedAccessViewRHIRef AIronBlock::CreateSafeUAV(UTextureRenderTarget2D* RenderTarget)
{
if (!RenderTarget || !RenderTarget->GetResource())
return nullptr;
FTextureRHIRef TextureRHI = RenderTarget->GetResource()->GetTexture2DRHI();
if (!TextureRHI.IsValid())
return nullptr;
// 创建UAV描述
FUnorderedAccessViewRHIRef UAV;
ENQUEUE_RENDER_COMMAND(CreateUAVCommand)(
[TextureRHI, &UAV](FRHICommandListImmediate& RHICmdList)
{
FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV();
ViewDesc.SetMipLevel(0);
ViewDesc.SetPlane(ERHITexturePlane::Primary);
ViewDesc.SetFormat(PF_A32B32G32R32F);
UAV = RHICmdList.CreateUnorderedAccessView(TextureRHI, ViewDesc);
}
);
// 等待渲染命令完成
FlushRenderingCommands();
return UAV;
} }
void AIronBlock::CalculateLocalBounds() void AIronBlock::CalculateLocalBounds()
{ {
if (!StaticMeshComp) if (!StaticMeshComp)
{ {
StaticMeshComp = GetComponentByClass<UStaticMeshComponent>(); StaticMeshComp = GetComponentByClass<UStaticMeshComponent>();
} }
if (StaticMeshComp && StaticMeshComp->GetStaticMesh()) if (StaticMeshComp && StaticMeshComp->GetStaticMesh())
{ {
// 获取本地空间包围盒 // 获取本地空间包围盒
LocalBounds = StaticMeshComp->GetStaticMesh()->GetBoundingBox(); LocalBounds = StaticMeshComp->GetStaticMesh()->GetBoundingBox();
} }
else else
{ {
// 智能默认值 // 智能默认值
float Extent = 50.0f; float Extent = 50.0f;
LocalBounds = FBox( LocalBounds = FBox(
FVector(-Extent, -Extent, 0), FVector(-Extent, -Extent, 0),
FVector(Extent, Extent, Extent * 0.2f) FVector(Extent, Extent, Extent * 0.2f)
); );
} }
// 确保包围盒有效 // 确保包围盒有效
if (LocalBounds.GetVolume() <= 0) if (LocalBounds.GetVolume() <= 0)
{ {
LocalBounds = FBox(FVector(-50, -50, 0), FVector(50, 50, 10)); LocalBounds = FBox(FVector(-50, -50, 0), FVector(50, 50, 10));
} }
} }
void AIronBlock::SetupDeformationMaterial() void AIronBlock::SetupDeformationMaterial()
{ {
// 创建动态材质实例 // 创建动态材质实例
if (StaticMeshComp) if (StaticMeshComp)
{ {
// 加载基础材质 // 加载基础材质 (请确保你的材质支持读取 PositionTexture)
UMaterial* BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial")); UMaterial* BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Game/YourMaterialFolder/M_DeformableIron")); // 示例路径
if (BaseMaterial) if (!BaseMaterial)
{ {
DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, this); BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial"));
StaticMeshComp->SetMaterial(0, DynMaterial); }
// 设置初始参数 if (BaseMaterial)
if (DynMaterial) {
{ DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, this);
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), PositionTexture0); StaticMeshComp->SetMaterial(0, DynMaterial);
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min));
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max)); // 设置初始参数
DynMaterial->SetScalarParameterValue(TEXT("DeformationStrength"), 1.0f); if (DynMaterial)
} {
} // 初始时读取 RT0
else DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), PositionRenderTarget0);
{ DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min));
UE_LOG(LogTemp, Warning, TEXT("Failed to load base material for iron block")); DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max));
} DynMaterial->SetScalarParameterValue(TEXT("DeformationStrength"), 1.0f);
} }
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Failed to load base material for iron block"));
}
}
} }
void AIronBlock::OnHammerHit(const FHitResult& HitResult) void AIronBlock::OnHammerHit(const FHitResult& HitResult)
{ {
// 获取锤子冲击力 // 获取锤子冲击力
float ImpactForce = 0.0f; float ImpactForce = 0.0f;
if (HitResult.GetComponent() && HitResult.GetComponent()->GetPhysicsLinearVelocity().IsNearlyZero()) // 检查 HitResult.GetComponent() 是否有效
{ if (HitResult.GetComponent() && !HitResult.GetComponent()->GetPhysicsLinearVelocity().IsNearlyZero())
ImpactForce = HitResult.GetComponent()->GetPhysicsLinearVelocity().Size(); {
} ImpactForce = HitResult.GetComponent()->GetPhysicsLinearVelocity().Size();
}
// 默认冲击力 // 默认冲击力
if (ImpactForce < 10.0f) ImpactForce = 500.0f; if (ImpactForce < 10.0f) ImpactForce = 500.0f;
// 转换到本地空间 // 转换到本地空间
FVector ImpactPoint = HitResult.ImpactPoint; FVector ImpactPoint = HitResult.ImpactPoint;
FVector ImpactNormal = HitResult.ImpactNormal; FVector ImpactNormal = HitResult.ImpactNormal;
FVector LocalImpactPoint = GetTransform().InverseTransformPosition(ImpactPoint); FVector LocalImpactPoint = GetTransform().InverseTransformPosition(ImpactPoint);
FVector LocalImpactNormal = GetTransform().InverseTransformVector(ImpactNormal); FVector LocalImpactNormal = GetTransform().InverseTransformVector(ImpactNormal);
UpdateComputeShaderParams(LocalImpactPoint, LocalImpactNormal, ImpactForce); UpdateComputeShaderParams(LocalImpactPoint, LocalImpactNormal, ImpactForce);
ExecuteDeformationCS(); ExecuteDeformationCS();
} }
// 安全的纹理资源获取
FTextureRHIRef AIronBlock::GetTextureRHI(UTexture2D* Texture)
{
return Texture && Texture->GetResource() ? Texture->GetResource()->TextureRHI : nullptr;
}
// 安全的渲染目标获取
FTextureRHIRef AIronBlock::GetRenderTargetRHI(UTextureRenderTarget2D* RT)
{
if (!RT || !RT->GetResource()) return nullptr;
return RT->GetResource()->TextureRHI;
}
void AIronBlock::UpdateComputeShaderParams(FVector LocalImpactPoint, FVector LocalImpactNormal, float ImpactForce) void AIronBlock::UpdateComputeShaderParams(FVector LocalImpactPoint, FVector LocalImpactNormal, float ImpactForce)
{ {
// 更新碰撞参数 // 更新碰撞参数
CurrentImpactPoint = LocalImpactPoint; CurrentImpactPoint = LocalImpactPoint;
CurrentImpactNormal = LocalImpactNormal.GetSafeNormal(); CurrentImpactNormal = LocalImpactNormal.GetSafeNormal();
CurrentImpactForce = FMath::Clamp(ImpactForce, 0.0f, 5000.0f); CurrentImpactForce = FMath::Clamp(ImpactForce, 0.0f, 5000.0f);
// 根据冲击力计算动态变形半径 // 根据冲击力计算动态变形半径
CurrentDeformationRadius = FMath::Lerp(5.0f, 30.0f, CurrentImpactForce / 2000.0f); CurrentDeformationRadius = FMath::Lerp(5.0f, 30.0f, CurrentImpactForce / 2000.0f);
// 根据材质类型调整塑性系数 // 根据材质类型调整塑性系数 (示例)
UMaterialInterface* Material = StaticMeshComp->GetMaterial(0); UMaterialInterface* Material = StaticMeshComp->GetMaterial(0);
if(Material && Material->GetName().Contains("Steel")) if (Material && Material->GetName().Contains("Steel"))
{ {
CurrentPlasticity = 0.4f; // 钢材塑性较低 CurrentPlasticity = 0.4f; // 钢材塑性较低
} }
else else
{ {
CurrentPlasticity = 0.8f; // 默认高塑性 CurrentPlasticity = 0.8f; // 默认高塑性
} }
// 切换纹理缓冲 // 切换纹理缓冲索引
CurrentTextureIndex = 1 - CurrentTextureIndex; CurrentTextureIndex = 1 - CurrentTextureIndex;
} }
void AIronBlock::ExecuteDeformationCS() void AIronBlock::ExecuteDeformationCS()
{ {
// 确保纹理资源有效 // 确保纹理资源有效
if (!PositionTexture0 || !PositionTexture0->GetResource() || if (!PositionRenderTarget0 || !PositionRenderTarget0->GetResource() ||
!PositionTexture1 || !PositionTexture1->GetResource()) !PositionRenderTarget1 || !PositionRenderTarget1->GetResource())
{ {
UE_LOG(LogTemp, Error, TEXT("Position textures not initialized!")); UE_LOG(LogTemp, Error, TEXT("Position render targets not initialized!"));
return; return;
} }
// 获取当前纹理资源 // --- 关键修复: 决定哪个是读取RT,哪个是写入RT ---
UTexture2D* CurrentTex = (CurrentTextureIndex == 0) ? PositionTexture0 : PositionTexture1; UTextureRenderTarget2D* ReadTarget = (CurrentTextureIndex == 0) ? PositionRenderTarget0 : PositionRenderTarget1;
UTexture2D* PrevTex = (CurrentTextureIndex == 0) ? PositionTexture1 : PositionTexture0; UTextureRenderTarget2D* WriteTarget = (CurrentTextureIndex == 1) ? PositionRenderTarget0 : PositionRenderTarget1; // Flipped
// 获取纹理RHI资源 // --- 关键修复: 直接获取 RT 的 RHI 和 UAV ---
FTextureRHIRef InputRHI = GetTextureRHI(PrevTex); FTextureRHIRef InputRHI = GetRenderTargetRHI(ReadTarget);
FTextureRHIRef OutputRHI = GetRenderTargetRHI(PositionRenderTarget); // 从 WriteTarget 获取 UAV。因为我们创建时设置了 bCanCreateUAV=true,所以这个UAV是有效的。
FUnorderedAccessViewRHIRef OutputUAV = nullptr;
if (WriteTarget && WriteTarget->GetResource())
{
FTextureResource* RTResource = WriteTarget->GetResource();
FTextureRenderTargetResource* WriteRTResource = static_cast<FTextureRenderTargetResource*>(RTResource);
if (WriteRTResource)
{
OutputUAV = WriteRTResource->GetRenderTargetUAV();
}
}
if (!InputRHI || !OutputRHI) if (!InputRHI || !OutputUAV)
{ {
UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources!")); UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources or UAV!"));
return; return;
} }
// 计算纹理UV坐标 // 计算纹理UV坐标
const int32 TexSize = PositionRenderTarget->SizeX; const int32 TexSize = ReadTarget->SizeX;
FVector2D ImpactUV( FVector2D ImpactUV(
(CurrentImpactPoint.X - LocalBounds.Min.X) / LocalBounds.GetSize().X, (CurrentImpactPoint.X - LocalBounds.Min.X) / LocalBounds.GetSize().X,
(CurrentImpactPoint.Y - LocalBounds.Min.Y) / LocalBounds.GetSize().Y (CurrentImpactPoint.Y - LocalBounds.Min.Y) / LocalBounds.GetSize().Y
); );
// 计算更新区域 (只更新受影响部分) // 计算更新区域 (只更新受影响部分)
const float RadiusRatio = CurrentDeformationRadius / LocalBounds.GetExtent().GetMax(); const float RadiusRatio = CurrentDeformationRadius / LocalBounds.GetExtent().GetMax();
FIntRect UpdateRegion; FIntRect UpdateRegion;
UpdateRegion.Min.X = FMath::Clamp((ImpactUV.X - RadiusRatio) * TexSize, 0, TexSize); UpdateRegion.Min.X = FMath::Clamp(FMath::FloorToInt((ImpactUV.X - RadiusRatio) * TexSize), 0, TexSize - 1);
UpdateRegion.Min.Y = FMath::Clamp((ImpactUV.Y - RadiusRatio) * TexSize, 0, TexSize); UpdateRegion.Min.Y = FMath::Clamp(FMath::FloorToInt((ImpactUV.Y - RadiusRatio) * TexSize), 0, TexSize - 1);
UpdateRegion.Max.X = FMath::Min((ImpactUV.X + RadiusRatio) * TexSize, (float)TexSize); UpdateRegion.Max.X = FMath::Clamp(FMath::CeilToInt((ImpactUV.X + RadiusRatio) * TexSize), 1, TexSize);
UpdateRegion.Max.Y = FMath::Min((ImpactUV.Y + RadiusRatio) * TexSize, (float)TexSize); UpdateRegion.Max.Y = FMath::Clamp(FMath::CeilToInt((ImpactUV.Y + RadiusRatio) * TexSize), 1, TexSize);
// 准备参数 // 确保 Min < Max
FDeformationCSDispatchParams Params; if (UpdateRegion.Min.X >= UpdateRegion.Max.X) UpdateRegion.Max.X = UpdateRegion.Min.X + 1;
Params.ImpactPoint = CurrentImpactPoint; if (UpdateRegion.Min.Y >= UpdateRegion.Max.Y) UpdateRegion.Max.Y = UpdateRegion.Min.Y + 1;
Params.ImpactNormal = CurrentImpactNormal;
Params.ImpactForce = CurrentImpactForce;
Params.DeformationRadius = CurrentDeformationRadius;
Params.Plasticity = CurrentPlasticity;
Params.InputTexture = InputRHI;
// 创建UAV用于输出 // 准备参数
FUnorderedAccessViewRHIRef OutputUAV; FDeformationCSDispatchParams Params;
ENQUEUE_RENDER_COMMAND(CreateOutputUAV)( Params.ImpactPoint = CurrentImpactPoint; // 确保类型匹配 (FVector -> FVector3f)
[OutputRHI, &OutputUAV](FRHICommandListImmediate& RHICmdList) Params.ImpactNormal = CurrentImpactNormal;
{ Params.ImpactForce = CurrentImpactForce;
FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV(); Params.DeformationRadius = CurrentDeformationRadius;
ViewDesc.SetMipLevel(0); Params.Plasticity = CurrentPlasticity;
ViewDesc.SetPlane(ERHITexturePlane::Primary); Params.InputTexture = InputRHI;
ViewDesc.SetFormat(PF_A32B32G32R32F); Params.OutputTexture = OutputUAV; // **直接使用RT的UAV**
Params.UpdateRegion = UpdateRegion;
Params.TextureSize = FIntVector(TexSize, TexSize, 1);
OutputUAV = RHICmdList.CreateUnorderedAccessView(OutputRHI, ViewDesc); // 在渲染线程中执行Compute Shader
} ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS)(
); [Params](FRHICommandListImmediate& RHICmdList)
FlushRenderingCommands(); {
Params.OutputTexture = OutputUAV; // 在这里调用你的 Compute Shader 调度逻辑
FDeformationCS::Dispatch(RHICmdList, Params);
});
Params.UpdateRegion = UpdateRegion; // 等待渲染命令完成,以便在CPU上读取 (如果需要立即更新碰撞体)
Params.TextureSize = FIntVector(TexSize, TexSize, 1); // 注意:这是一个同步点,会影响性能。
// FlushRenderingCommands();
// UpdateCollisionAsync(UpdateRegion, WriteTarget); // 异步更新碰撞体
// 在渲染线程中执行Compute Shader // --- 关键修复: 更新材质参数,使其下一帧读取我们刚刚写入的纹理 ---
ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS)( if (DynMaterial)
[Params](FRHICommandListImmediate& RHICmdList) {
{ DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), WriteTarget);
FDeformationCS::Dispatch(RHICmdList, Params); DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min));
}); DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max));
}
// 更新材质参数
if (DynMaterial)
{
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), CurrentTex);
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min));
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max));
}
} }
// 异步更新物理碰撞体
void AIronBlock::UpdateCollisionAsync(const FIntRect& UpdateRegion) // 异步更新物理碰撞体 (示例,未优化)
void AIronBlock::UpdateCollisionAsync(const FIntRect& UpdateRegion, UTextureRenderTarget2D* FromTarget)
{ {
if (!PositionRenderTarget || !PositionRenderTarget->GetResource()) // 注意:ReadPixels 是一项非常慢的操作 (GPU->CPU 同步)
{ // 更好的方法是使用另一个Compute Shader将顶点数据简化并读回到一个小的StructuredBuffer中
return;
}
// 从GPU读取关键区域数据
TArray<FColor> SampledData;
FReadSurfaceDataFlags Flags;
Flags.SetLinearToGamma(false);
PositionRenderTarget->GameThread_GetRenderTargetResource()->ReadPixels( if (!FromTarget || !FromTarget->GetResource())
SampledData, {
Flags, return;
FIntRect(UpdateRegion.Min.X, UpdateRegion.Min.Y, }
UpdateRegion.Max.X, UpdateRegion.Max.Y) // 从GPU读取关键区域数据
); TArray<FLinearColor> SampledData; // 使用 FLinearColor
FReadSurfaceDataFlags Flags;
Flags.SetLinearToGamma(false);
// 创建临时碰撞体 FRenderTarget* RTResource = FromTarget->GameThread_GetRenderTargetResource();
TArray<FVector> CollisionVerts; if (!RTResource) return;
for(FColor& Pixel : SampledData)
{
FVector Pos(
Pixel.R * 255.0f / 10.0f, // 根据实际范围调整
Pixel.G * 255.0f / 10.0f,
Pixel.B * 255.0f / 10.0f
);
CollisionVerts.Add(Pos);
}
// 更新简单碰撞体 RTResource->ReadLinearColorPixels(
UBodySetup* BodySetup = StaticMeshComp->GetBodySetup(); SampledData,
BodySetup->AggGeom.ConvexElems[0].VertexData = CollisionVerts; Flags,
BodySetup->CreatePhysicsMeshes(); UpdateRegion // FIntRect
);
// 创建临时碰撞体
TArray<FVector> CollisionVerts;
for (const FLinearColor& Pixel : SampledData)
{
// 像素数据已经是 FLinearColor (即 FVector)
CollisionVerts.Add(FVector(Pixel.R, Pixel.G, Pixel.B));
}
if (CollisionVerts.Num() == 0) return;
// 更新简单碰撞体
UBodySetup* BodySetup = StaticMeshComp->GetBodySetup();
if (BodySetup && BodySetup->AggGeom.ConvexElems.Num() > 0)
{
BodySetup->AggGeom.ConvexElems[0].VertexData = CollisionVerts;
BodySetup->CreatePhysicsMeshes(); // 重新创建物理网格体
}
} }
// Called every frame // Called every frame
void AIronBlock::Tick(float DeltaTime) void AIronBlock::Tick(float DeltaTime)
{ {
Super::Tick(DeltaTime); Super::Tick(DeltaTime);
} }
+49 -47
View File
@@ -4,90 +4,92 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GameFramework/Actor.h" #include "GameFramework/Actor.h"
// FDeformComputeShader.h 假定包含了 FDeformationCSDispatchParams 结构
#include "FDeformComputeShader.h"
#include "IronBlock.generated.h" #include "IronBlock.generated.h"
class UTextureRenderTarget2D;
class UMaterialInstanceDynamic;
class UStaticMeshComponent;
UCLASS() UCLASS()
class BLADESMITH_API AIronBlock : public AActor class BLADESMITH_API AIronBlock : public AActor
{ {
GENERATED_BODY() GENERATED_BODY()
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
UStaticMeshComponent* StaticMeshComp; UStaticMeshComponent* StaticMeshComp;
UPROPERTY()
UTexture2D* PositionTexture0;
/** 用于Ping-Pong缓冲的渲染目标 A */
UPROPERTY() UPROPERTY()
UTexture2D* PositionTexture1; UTextureRenderTarget2D* PositionRenderTarget0;
UPROPERTY() /** 用于Ping-Pong缓冲的渲染目标 B */
UTextureRenderTarget2D* PositionRenderTarget; UPROPERTY()
UTextureRenderTarget2D* PositionRenderTarget1;
/** 应用于网格的动态材质实例 */
UPROPERTY()
UMaterialInstanceDynamic* DynMaterial;
UPROPERTY()
UMaterialInstanceDynamic* DynMaterial;
public: public:
// Sets default values for this actor's properties // Sets default values for this actor's properties
AIronBlock(); AIronBlock();
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void OnHammerHit(const FHitResult& HitResult); void OnHammerHit(const FHitResult& HitResult);
protected: protected:
// Called when the game starts or when spawned // Called when the game starts or when spawned
virtual void BeginPlay() override; virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
void InitializeTexturePair(int32 Index);
/** 初始化两个渲染目标和它们的初始数据 */
void InitializePositionTexture();
/** 创建一个配置好用于变形计算的 RenderTarget */
UTextureRenderTarget2D* CreateDeformRT(const FString& RTName, int32 Size);
/** 获取静态网格体的顶点位置 (用于计算初始纹理) */
void GetStaticMeshVertexPositions(TArray<FVector3f>& OutPositions); void GetStaticMeshVertexPositions(TArray<FVector3f>& OutPositions);
UTexture2D* CreatePositionTexture(const FString& TexName, int32 Size, const TArray<FLinearColor>& InitialData);
void InitializePositionTexture(); /** 设置动态材质实例和参数 */
FUnorderedAccessViewRHIRef CreateSafeUAV(UTextureRenderTarget2D* RenderTarget); void SetupDeformationMaterial();
void OnEndFrameInitializeTextures();
void SetupDeformationMaterial(); /** 安全地获取 RenderTarget 的 RHI 资源 */
FTextureRHIRef GetTextureRHI(UTexture2D* Texture);
FTextureRHIRef GetRenderTargetRHI(UTextureRenderTarget2D* RT); FTextureRHIRef GetRenderTargetRHI(UTextureRenderTarget2D* RT);
private: private:
/** 更新 Compute Shader 所需的参数 */
void UpdateComputeShaderParams(FVector LocalImpactPoint, FVector LocalImpactNormal, float ImpactForce); void UpdateComputeShaderParams(FVector LocalImpactPoint, FVector LocalImpactNormal, float ImpactForce);
/** 在渲染线程上执行 Compute Shader */
void ExecuteDeformationCS(); void ExecuteDeformationCS();
void UpdateCollisionAsync(const FIntRect& UpdateRegion);
/** (可选) 异步更新物理碰撞体 */
void UpdateCollisionAsync(const FIntRect& UpdateRegion, UTextureRenderTarget2D* FromTarget);
/** 计算网格体的本地包围盒 */
void CalculateLocalBounds(); void CalculateLocalBounds();
// 双缓冲纹理对结构体 // 网格体的本地包围盒
struct FDeformationTexturePair
{
// 纹理资源引用
FTextureRHIRef RHITexture;
// UAV 资源引用
FUnorderedAccessViewRHIRef UAV;
//FRDGTextureUAVRef UAV;
// 安全释放资源
void ReleaseRHI()
{
UAV.SafeRelease();
//UAV->GetRHI()->Release();
RHITexture.SafeRelease();
}
};
// 双缓冲纹理对数组 [0] = 第一对, [1] = 第二对
FDeformationTexturePair TexturePairs[2];
// 当前使用的纹理对索引
int32 CurrentPairIndex = 0;
FBox LocalBounds; FBox LocalBounds;
// 纹理资源初始化委托
FDelegateHandle TextureInitHandle; // --- 碰撞参数 ---
// 当前碰撞参数
FVector CurrentImpactPoint; FVector CurrentImpactPoint;
FVector CurrentImpactNormal; FVector CurrentImpactNormal;
float CurrentImpactForce = 0.0f; float CurrentImpactForce = 0.0f;
float CurrentDeformationRadius = 10.0f; float CurrentDeformationRadius = 10.0f;
float CurrentPlasticity = 0.7f; float CurrentPlasticity = 0.7f;
// 双缓冲纹理索引 /** * 双缓冲纹理索引
* 0: 读 RT0, 写 RT1
* 1: 读 RT1, 写 RT0
*/
int32 CurrentTextureIndex = 0; int32 CurrentTextureIndex = 0;
public: public:
// Called every frame // Called every frame
virtual void Tick(float DeltaTime) override; virtual void Tick(float DeltaTime) override;
}; };