修复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.
+371 -546
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())
{ Super::EndPlay(EndPlayReason);
FCoreDelegates::OnEndFrame.Remove(TextureInitHandle);
TextureInitHandle.Reset();
}
// 释放 RHI 资源
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();
OutPositions.SetNum(VertexCount); const int32 VertexCount = VertexBuffer.GetNumVertices();
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;
// 设置默认网格(立方体)
UStaticMesh* DefaultMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Engine/BasicShapes/Cube"));
if (DefaultMesh)
{
StaticMeshComp->SetStaticMesh(DefaultMesh);
}
}
// 计算本地边界
CalculateLocalBounds();
// 获取顶点位置
TArray<FVector3f> VertexPositions;
GetStaticMeshVertexPositions(VertexPositions);
// 创建位置纹理
const int32 TextureSize = 256; // 纹理尺寸
TArray<FLinearColor> InitialData;
InitialData.SetNum(TextureSize * TextureSize);
// 初始化纹理数据
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坐标转换为本地空间位置
FVector LocalPos(
FMath::Lerp(LocalBounds.Min.X, LocalBounds.Max.X, UV.X),
FMath::Lerp(LocalBounds.Min.Y, LocalBounds.Max.Y, UV.Y),
LocalBounds.Min.Z
);
InitialData[Index] = FLinearColor(LocalPos.X, LocalPos.Y, LocalPos.Z, 1.0f);
}
}
// 创建纹理对
PositionTexture0 = CreatePositionTexture(TEXT("PositionTexture0"), TextureSize, InitialData);
PositionTexture1 = CreatePositionTexture(TEXT("PositionTexture1"), TextureSize, InitialData);
// 创建渲染目标
PositionRenderTarget = NewObject<UTextureRenderTarget2D>(this);
PositionRenderTarget->SizeX = TextureSize;
PositionRenderTarget->SizeY = TextureSize;
PositionRenderTarget->RenderTargetFormat = RTF_RGBA16f;
PositionRenderTarget->bAutoGenerateMips = false;
PositionRenderTarget->UpdateResource();
// 设置变形材质
SetupDeformationMaterial();
// 初始化纹理对
InitializeTexturePair(0);
InitializeTexturePair(1);
// 延迟初始化纹理资源
TextureInitHandle = FDelegateHandle();
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
{
OnEndFrameInitializeTextures();
});
}
// 创建UAV的安全方法 // 获取顶点位置 (用于计算初始UV->WorldPos)
FUnorderedAccessViewRHIRef AIronBlock::CreateSafeUAV(UTextureRenderTarget2D* RenderTarget) TArray<FVector3f> VertexPositions;
{ GetStaticMeshVertexPositions(VertexPositions);
if (!RenderTarget || !RenderTarget->GetResource())
return nullptr; // 创建位置纹理
const int32 TextureSize = 256; // 纹理尺寸
FTextureRHIRef TextureRHI = RenderTarget->GetResource()->GetTexture2DRHI(); TArray<FLinearColor> InitialData;
if (!TextureRHI.IsValid()) InitialData.SetNum(TextureSize * TextureSize);
return nullptr;
// 初始化纹理数据 (将UV映射到本地包围盒的XY平面)
// 创建UAV描述 for (int32 Y = 0; Y < TextureSize; ++Y)
FUnorderedAccessViewRHIRef UAV; {
ENQUEUE_RENDER_COMMAND(CreateUAVCommand)( for (int32 X = 0; X < TextureSize; ++X)
[TextureRHI, &UAV](FRHICommandListImmediate& RHICmdList) {
{ int32 Index = Y * TextureSize + X;
FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV(); FVector2D UV(FVector2D(X, Y) / (TextureSize - 1.0f)); // 使用 255.0 或 (Size-1)
ViewDesc.SetMipLevel(0);
ViewDesc.SetPlane(ERHITexturePlane::Primary); // 将UV坐标转换为本地空间位置 (XY平面)
ViewDesc.SetFormat(PF_A32B32G32R32F); FVector LocalPos(
FMath::Lerp(LocalBounds.Min.X, LocalBounds.Max.X, UV.X),
UAV = RHICmdList.CreateUnorderedAccessView(TextureRHI, ViewDesc); FMath::Lerp(LocalBounds.Min.Y, LocalBounds.Max.Y, UV.Y),
} LocalBounds.Min.Z // 假设初始是一个平面
); );
// 等待渲染命令完成 InitialData[Index] = FLinearColor(LocalPos.X, LocalPos.Y, LocalPos.Z, 1.0f);
FlushRenderingCommands(); }
}
return UAV;
// --- 关键修复: 创建两个 RenderTarget ---
PositionRenderTarget0 = CreateDeformRT(TEXT("PositionRT0"), TextureSize);
PositionRenderTarget1 = CreateDeformRT(TEXT("PositionRT1"), TextureSize);
// --- 将初始数据写入GPU ---
// 我们需要将CPU端的 InitialData 写入到GPU纹理中
// 最简单的方法是创建一个临时的UTexture2D,用BulkData上传,然后拷贝到RT
UTexture2D* TempTexture = UTexture2D::CreateTransient(TextureSize, TextureSize, PF_A32B32G32R32F); // 必须匹配格式
if (TempTexture && PositionRenderTarget0 && PositionRenderTarget1)
{
// 锁定 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资源
FTextureRHIRef DestRT0_RHI = GetRenderTargetRHI(PositionRenderTarget0);
FTextureRHIRef DestRT1_RHI = GetRenderTargetRHI(PositionRenderTarget1);
FTextureRHIRef SrcTex_RHI = TempTexture->GetResource() ? TempTexture->GetResource()->TextureRHI : nullptr;
if (DestRT0_RHI && DestRT1_RHI && SrcTex_RHI)
{
// 在渲染线程上执行拷贝
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, {});
});
// 确保拷贝完成
FlushRenderingCommands();
}
}
// 设置变形材质
SetupDeformationMaterial();
UE_LOG(LogTemp, Log, TEXT("Position textures initialized"));
} }
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 ImpactNormal = HitResult.ImpactNormal; FVector ImpactPoint = HitResult.ImpactPoint;
FVector LocalImpactPoint = GetTransform().InverseTransformPosition(ImpactPoint); FVector ImpactNormal = HitResult.ImpactNormal;
FVector LocalImpactNormal = GetTransform().InverseTransformVector(ImpactNormal); FVector LocalImpactPoint = GetTransform().InverseTransformPosition(ImpactPoint);
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;
} }
// 获取当前纹理资源
UTexture2D* CurrentTex = (CurrentTextureIndex == 0) ? PositionTexture0 : PositionTexture1;
UTexture2D* PrevTex = (CurrentTextureIndex == 0) ? PositionTexture1 : PositionTexture0;
// 获取纹理RHI资源
FTextureRHIRef InputRHI = GetTextureRHI(PrevTex);
FTextureRHIRef OutputRHI = GetRenderTargetRHI(PositionRenderTarget);
if (!InputRHI || !OutputRHI) // --- 关键修复: 决定哪个是读取RT,哪个是写入RT ---
{ UTextureRenderTarget2D* ReadTarget = (CurrentTextureIndex == 0) ? PositionRenderTarget0 : PositionRenderTarget1;
UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources!")); UTextureRenderTarget2D* WriteTarget = (CurrentTextureIndex == 1) ? PositionRenderTarget0 : PositionRenderTarget1; // Flipped
return;
} // --- 关键修复: 直接获取 RT 的 RHI 和 UAV ---
FTextureRHIRef InputRHI = GetRenderTargetRHI(ReadTarget);
// 计算纹理UV坐标 // 从 WriteTarget 获取 UAV。因为我们创建时设置了 bCanCreateUAV=true,所以这个UAV是有效的。
const int32 TexSize = PositionRenderTarget->SizeX; FUnorderedAccessViewRHIRef OutputUAV = nullptr;
FVector2D ImpactUV( if (WriteTarget && WriteTarget->GetResource())
(CurrentImpactPoint.X - LocalBounds.Min.X) / LocalBounds.GetSize().X, {
(CurrentImpactPoint.Y - LocalBounds.Min.Y) / LocalBounds.GetSize().Y FTextureResource* RTResource = WriteTarget->GetResource();
); FTextureRenderTargetResource* WriteRTResource = static_cast<FTextureRenderTargetResource*>(RTResource);
if (WriteRTResource)
// 计算更新区域 (只更新受影响部分) {
const float RadiusRatio = CurrentDeformationRadius / LocalBounds.GetExtent().GetMax(); OutputUAV = WriteRTResource->GetRenderTargetUAV();
FIntRect UpdateRegion; }
UpdateRegion.Min.X = FMath::Clamp((ImpactUV.X - RadiusRatio) * TexSize, 0, TexSize); }
UpdateRegion.Min.Y = FMath::Clamp((ImpactUV.Y - RadiusRatio) * TexSize, 0, TexSize);
UpdateRegion.Max.X = FMath::Min((ImpactUV.X + RadiusRatio) * TexSize, (float)TexSize); if (!InputRHI || !OutputUAV)
UpdateRegion.Max.Y = FMath::Min((ImpactUV.Y + RadiusRatio) * TexSize, (float)TexSize); {
UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources or UAV!"));
// 准备参数 return;
FDeformationCSDispatchParams Params; }
Params.ImpactPoint = CurrentImpactPoint;
Params.ImpactNormal = CurrentImpactNormal; // 计算纹理UV坐标
Params.ImpactForce = CurrentImpactForce; const int32 TexSize = ReadTarget->SizeX;
Params.DeformationRadius = CurrentDeformationRadius; FVector2D ImpactUV(
Params.Plasticity = CurrentPlasticity; (CurrentImpactPoint.X - LocalBounds.Min.X) / LocalBounds.GetSize().X,
Params.InputTexture = InputRHI; (CurrentImpactPoint.Y - LocalBounds.Min.Y) / LocalBounds.GetSize().Y
);
// 创建UAV用于输出
FUnorderedAccessViewRHIRef OutputUAV; // 计算更新区域 (只更新受影响部分)
ENQUEUE_RENDER_COMMAND(CreateOutputUAV)( const float RadiusRatio = CurrentDeformationRadius / LocalBounds.GetExtent().GetMax();
[OutputRHI, &OutputUAV](FRHICommandListImmediate& RHICmdList) FIntRect UpdateRegion;
{ UpdateRegion.Min.X = FMath::Clamp(FMath::FloorToInt((ImpactUV.X - RadiusRatio) * TexSize), 0, TexSize - 1);
FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV(); UpdateRegion.Min.Y = FMath::Clamp(FMath::FloorToInt((ImpactUV.Y - RadiusRatio) * TexSize), 0, TexSize - 1);
ViewDesc.SetMipLevel(0); UpdateRegion.Max.X = FMath::Clamp(FMath::CeilToInt((ImpactUV.X + RadiusRatio) * TexSize), 1, TexSize);
ViewDesc.SetPlane(ERHITexturePlane::Primary); UpdateRegion.Max.Y = FMath::Clamp(FMath::CeilToInt((ImpactUV.Y + RadiusRatio) * TexSize), 1, TexSize);
ViewDesc.SetFormat(PF_A32B32G32R32F);
// 确保 Min < Max
OutputUAV = RHICmdList.CreateUnorderedAccessView(OutputRHI, ViewDesc); if (UpdateRegion.Min.X >= UpdateRegion.Max.X) UpdateRegion.Max.X = UpdateRegion.Min.X + 1;
} if (UpdateRegion.Min.Y >= UpdateRegion.Max.Y) UpdateRegion.Max.Y = UpdateRegion.Min.Y + 1;
);
FlushRenderingCommands(); // 准备参数
Params.OutputTexture = OutputUAV; FDeformationCSDispatchParams Params;
Params.ImpactPoint = CurrentImpactPoint; // 确保类型匹配 (FVector -> FVector3f)
Params.UpdateRegion = UpdateRegion; Params.ImpactNormal = CurrentImpactNormal;
Params.TextureSize = FIntVector(TexSize, TexSize, 1); Params.ImpactForce = CurrentImpactForce;
Params.DeformationRadius = CurrentDeformationRadius;
// 在渲染线程中执行Compute Shader Params.Plasticity = CurrentPlasticity;
ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS)( Params.InputTexture = InputRHI;
[Params](FRHICommandListImmediate& RHICmdList) Params.OutputTexture = OutputUAV; // **直接使用RT的UAV**
{ Params.UpdateRegion = UpdateRegion;
FDeformationCS::Dispatch(RHICmdList, Params); Params.TextureSize = FIntVector(TexSize, TexSize, 1);
});
// 在渲染线程中执行Compute Shader
// 更新材质参数 ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS)(
if (DynMaterial) [Params](FRHICommandListImmediate& RHICmdList)
{ {
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), CurrentTex); // 在这里调用你的 Compute Shader 调度逻辑
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min)); FDeformationCS::Dispatch(RHICmdList, Params);
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max)); });
}
// 等待渲染命令完成,以便在CPU上读取 (如果需要立即更新碰撞体)
// 注意:这是一个同步点,会影响性能。
// FlushRenderingCommands();
// UpdateCollisionAsync(UpdateRegion, WriteTarget); // 异步更新碰撞体
// --- 关键修复: 更新材质参数,使其下一帧读取我们刚刚写入的纹理 ---
if (DynMaterial)
{
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), WriteTarget);
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;
} if (!FromTarget || !FromTarget->GetResource())
// 从GPU读取关键区域数据 {
TArray<FColor> SampledData; return;
FReadSurfaceDataFlags Flags; }
Flags.SetLinearToGamma(false); // 从GPU读取关键区域数据
TArray<FLinearColor> SampledData; // 使用 FLinearColor
PositionRenderTarget->GameThread_GetRenderTargetResource()->ReadPixels( FReadSurfaceDataFlags Flags;
SampledData, Flags.SetLinearToGamma(false);
Flags,
FIntRect(UpdateRegion.Min.X, UpdateRegion.Min.Y, FRenderTarget* RTResource = FromTarget->GameThread_GetRenderTargetResource();
UpdateRegion.Max.X, UpdateRegion.Max.Y) if (!RTResource) return;
);
RTResource->ReadLinearColorPixels(
// 创建临时碰撞体 SampledData,
TArray<FVector> CollisionVerts; Flags,
for(FColor& Pixel : SampledData) UpdateRegion // FIntRect
{ );
FVector Pos(
Pixel.R * 255.0f / 10.0f, // 根据实际范围调整 // 创建临时碰撞体
Pixel.G * 255.0f / 10.0f, TArray<FVector> CollisionVerts;
Pixel.B * 255.0f / 10.0f for (const FLinearColor& Pixel : SampledData)
); {
CollisionVerts.Add(Pos); // 像素数据已经是 FLinearColor (即 FVector)
} CollisionVerts.Add(FVector(Pixel.R, Pixel.G, Pixel.B));
}
// 更新简单碰撞体
UBodySetup* BodySetup = StaticMeshComp->GetBodySetup(); if (CollisionVerts.Num() == 0) return;
BodySetup->AggGeom.ConvexElems[0].VertexData = CollisionVerts;
BodySetup->CreatePhysicsMeshes(); // 更新简单碰撞体
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);
}
}
+55 -53
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; /** 应用于网格的动态材质实例 */
public: UPROPERTY()
UMaterialInstanceDynamic* DynMaterial;
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;
};
};