修复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",
"components": [
"Component.Unreal.Debugger",
"Component.Unreal.Ide",
"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.x86.x64",
"Microsoft.VisualStudio.Component.VC.Llvm.Clang",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
"Microsoft.VisualStudio.Workload.CoreEditor",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"FileVersion": 3,
"EngineAssociation": "{DE41D031-4C3F-817F-365A-5AAD9A1CE2AD}",
"EngineAssociation": "5.5",
"Category": "",
"Description": "",
"Modules": [
Binary file not shown.
+333 -508
View File
@@ -3,611 +3,436 @@
#include "IronBlock.h"
#include "FDeformComputeShader.h"
#include "FDeformComputeShader.h" // 确保这个头文件存在且包含了 FDeformationCSDispatchParams
#include "StaticMeshResources.h"
#include "TextureResource.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Engine/Texture.h"
#include "Engine/StaticMesh.h"
#include "Engine/Texture2D.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "PhysicsEngine/BodySetup.h"
#include "RHICommandList.h"
#include "RenderGraphBuilder.h" // 包含 FRDGBuilder
#include "Engine/Texture.h"
#include "GenericPlatform/GenericPlatformProcess.h"
// Sets default values
AIronBlock::AIronBlock(): StaticMeshComp(nullptr), PositionTexture0(nullptr), PositionTexture1(nullptr),
PositionRenderTarget(nullptr),
DynMaterial(nullptr),
CurrentImpactPoint(),
CurrentImpactNormal()
AIronBlock::AIronBlock() : StaticMeshComp(nullptr),
PositionRenderTarget0(nullptr), PositionRenderTarget1(nullptr),
DynMaterial(nullptr),
CurrentImpactPoint(),
CurrentImpactNormal()
{
// 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;
// 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;
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
void AIronBlock::BeginPlay()
{
Super::BeginPlay();
//CalculateLocalBounds();
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"));
}
InitializePositionTexture();
}
void AIronBlock::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// 清理资源
if (TextureInitHandle.IsValid())
{
FCoreDelegates::OnEndFrame.Remove(TextureInitHandle);
TextureInitHandle.Reset();
}
// RenderTargets 会被 UPROPERTY 自动 GC,无需手动释放 RHI 资源
// 释放 RHI 资源
ENQUEUE_RENDER_COMMAND(ReleaseResources)(
[this](FRHICommandListImmediate& RHICmdList)
{
for (int i = 0; i < 2; i++)
{
TexturePairs->ReleaseRHI();
}
});
Super::EndPlay(EndPlayReason);
Super::EndPlay(EndPlayReason);
}
// void CreateTexture()
// {
// 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)
// 安全的渲染目标获取
FTextureRHIRef AIronBlock::GetRenderTargetRHI(UTextureRenderTarget2D* RT)
{
if (Index < 0 || Index > 1) return;
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);
}
);
if (!RT || !RT->GetResource()) return nullptr;
return RT->GetResource()->TextureRHI;
}
void AIronBlock::GetStaticMeshVertexPositions(TArray<FVector3f>& OutPositions)
{
OutPositions.Empty();
OutPositions.Empty();
if (!StaticMeshComp || !StaticMeshComp->GetStaticMesh())
return;
if (!StaticMeshComp || !StaticMeshComp->GetStaticMesh())
return;
// 获取静态网格资源
UStaticMesh* StaticMesh = StaticMeshComp->GetStaticMesh();
if (StaticMesh->GetRenderData() && StaticMesh->GetRenderData()->LODResources.Num() > 0)
{
// 获取LOD0的顶点数据
FStaticMeshLODResources& LOD = StaticMesh->GetRenderData()->LODResources[0];
const FPositionVertexBuffer& VertexBuffer = LOD.VertexBuffers.PositionVertexBuffer;
// 获取静态网格资源
UStaticMesh* StaticMesh = StaticMeshComp->GetStaticMesh();
// 确保有有效的 RenderData 和 LOD
if (StaticMesh->GetRenderData() && StaticMesh->GetRenderData()->LODResources.Num() > 0)
{
// 获取LOD0的顶点数据
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++)
{
OutPositions[i] = VertexBuffer.VertexPosition(i);
}
}
for (int32 i = 0; i < VertexCount; 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()
{
// 创建静态网格组件
if (!StaticMeshComp)
{
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent"));
RootComponent = StaticMeshComp;
CalculateLocalBounds();
// 设置默认网格(立方体)
UStaticMesh* DefaultMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Engine/BasicShapes/Cube"));
if (DefaultMesh)
{
StaticMeshComp->SetStaticMesh(DefaultMesh);
}
}
// 获取顶点位置 (用于计算初始UV->WorldPos)
TArray<FVector3f> VertexPositions;
GetStaticMeshVertexPositions(VertexPositions);
// 计算本地边界
CalculateLocalBounds();
// 创建位置纹理
const int32 TextureSize = 256; // 纹理尺寸
TArray<FLinearColor> InitialData;
InitialData.SetNum(TextureSize * TextureSize);
// 获取顶点位置
TArray<FVector3f> VertexPositions;
GetStaticMeshVertexPositions(VertexPositions);
// 初始化纹理数据 (将UV映射到本地包围盒的XY平面)
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.0f)); // 使用 255.0 或 (Size-1)
// 创建位置纹理
const int32 TextureSize = 256; // 纹理尺寸
TArray<FLinearColor> InitialData;
InitialData.SetNum(TextureSize * TextureSize);
// 将UV坐标转换为本地空间位置 (XY平面)
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 // 假设初始是一个平面
);
// 初始化纹理数据
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));
InitialData[Index] = FLinearColor(LocalPos.X, LocalPos.Y, LocalPos.Z, 1.0f);
}
}
// 将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
);
// --- 关键修复: 创建两个 RenderTarget ---
PositionRenderTarget0 = CreateDeformRT(TEXT("PositionRT0"), TextureSize);
PositionRenderTarget1 = CreateDeformRT(TEXT("PositionRT1"), TextureSize);
InitialData[Index] = FLinearColor(LocalPos.X, LocalPos.Y, LocalPos.Z, 1.0f);
}
}
// --- 将初始数据写入GPU ---
// 我们需要将CPU端的 InitialData 写入到GPU纹理中
// 最简单的方法是创建一个临时的UTexture2D,用BulkData上传,然后拷贝到RT
// 创建纹理对
PositionTexture0 = CreatePositionTexture(TEXT("PositionTexture0"), TextureSize, InitialData);
PositionTexture1 = CreatePositionTexture(TEXT("PositionTexture1"), TextureSize, InitialData);
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
// 创建渲染目标
PositionRenderTarget = NewObject<UTextureRenderTarget2D>(this);
PositionRenderTarget->SizeX = TextureSize;
PositionRenderTarget->SizeY = TextureSize;
PositionRenderTarget->RenderTargetFormat = RTF_RGBA16f;
PositionRenderTarget->bAutoGenerateMips = false;
PositionRenderTarget->UpdateResource();
// 获取RHI资源
FTextureRHIRef DestRT0_RHI = GetRenderTargetRHI(PositionRenderTarget0);
FTextureRHIRef DestRT1_RHI = GetRenderTargetRHI(PositionRenderTarget1);
FTextureRHIRef SrcTex_RHI = TempTexture->GetResource() ? TempTexture->GetResource()->TextureRHI : nullptr;
// 设置变形材质
SetupDeformationMaterial();
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, {});
});
// 初始化纹理对
InitializeTexturePair(0);
InitializeTexturePair(1);
// 确保拷贝完成
FlushRenderingCommands();
}
}
// 延迟初始化纹理资源
TextureInitHandle = FDelegateHandle();
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
{
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;
// 设置变形材质
SetupDeformationMaterial();
UE_LOG(LogTemp, Log, TEXT("Position textures initialized"));
}
void AIronBlock::CalculateLocalBounds()
{
if (!StaticMeshComp)
{
StaticMeshComp = GetComponentByClass<UStaticMeshComponent>();
}
if (StaticMeshComp && StaticMeshComp->GetStaticMesh())
{
// 获取本地空间包围盒
LocalBounds = StaticMeshComp->GetStaticMesh()->GetBoundingBox();
}
else
{
// 智能默认值
float Extent = 50.0f;
LocalBounds = FBox(
FVector(-Extent, -Extent, 0),
FVector(Extent, Extent, Extent * 0.2f)
);
}
if (!StaticMeshComp)
{
StaticMeshComp = GetComponentByClass<UStaticMeshComponent>();
}
if (StaticMeshComp && StaticMeshComp->GetStaticMesh())
{
// 获取本地空间包围盒
LocalBounds = StaticMeshComp->GetStaticMesh()->GetBoundingBox();
}
else
{
// 智能默认值
float Extent = 50.0f;
LocalBounds = FBox(
FVector(-Extent, -Extent, 0),
FVector(Extent, Extent, Extent * 0.2f)
);
}
// 确保包围盒有效
if (LocalBounds.GetVolume() <= 0)
{
LocalBounds = FBox(FVector(-50, -50, 0), FVector(50, 50, 10));
}
// 确保包围盒有效
if (LocalBounds.GetVolume() <= 0)
{
LocalBounds = FBox(FVector(-50, -50, 0), FVector(50, 50, 10));
}
}
void AIronBlock::SetupDeformationMaterial()
{
// 创建动态材质实例
if (StaticMeshComp)
{
// 加载基础材质
UMaterial* BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial"));
if (BaseMaterial)
{
DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, this);
StaticMeshComp->SetMaterial(0, DynMaterial);
// 创建动态材质实例
if (StaticMeshComp)
{
// 加载基础材质 (请确保你的材质支持读取 PositionTexture)
UMaterial* BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Game/YourMaterialFolder/M_DeformableIron")); // 示例路径
if (!BaseMaterial)
{
BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial"));
}
// 设置初始参数
if (DynMaterial)
{
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), PositionTexture0);
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min));
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"));
}
}
if (BaseMaterial)
{
DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, this);
StaticMeshComp->SetMaterial(0, DynMaterial);
// 设置初始参数
if (DynMaterial)
{
// 初始时读取 RT0
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), PositionRenderTarget0);
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min));
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)
{
// 获取锤子冲击力
float ImpactForce = 0.0f;
if (HitResult.GetComponent() && HitResult.GetComponent()->GetPhysicsLinearVelocity().IsNearlyZero())
{
ImpactForce = HitResult.GetComponent()->GetPhysicsLinearVelocity().Size();
}
// 获取锤子冲击力
float ImpactForce = 0.0f;
// 检查 HitResult.GetComponent() 是否有效
if (HitResult.GetComponent() && !HitResult.GetComponent()->GetPhysicsLinearVelocity().IsNearlyZero())
{
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 LocalImpactPoint = GetTransform().InverseTransformPosition(ImpactPoint);
FVector LocalImpactNormal = GetTransform().InverseTransformVector(ImpactNormal);
// 转换到本地空间
FVector ImpactPoint = HitResult.ImpactPoint;
FVector ImpactNormal = HitResult.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)
{
// 更新碰撞参数
CurrentImpactPoint = LocalImpactPoint;
CurrentImpactNormal = LocalImpactNormal.GetSafeNormal();
CurrentImpactForce = FMath::Clamp(ImpactForce, 0.0f, 5000.0f);
// 更新碰撞参数
CurrentImpactPoint = LocalImpactPoint;
CurrentImpactNormal = LocalImpactNormal.GetSafeNormal();
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);
if(Material && Material->GetName().Contains("Steel"))
{
CurrentPlasticity = 0.4f; // 钢材塑性较低
}
else
{
CurrentPlasticity = 0.8f; // 默认高塑性
}
// 根据材质类型调整塑性系数 (示例)
UMaterialInterface* Material = StaticMeshComp->GetMaterial(0);
if (Material && Material->GetName().Contains("Steel"))
{
CurrentPlasticity = 0.4f; // 钢材塑性较低
}
else
{
CurrentPlasticity = 0.8f; // 默认高塑性
}
// 切换纹理缓冲
CurrentTextureIndex = 1 - CurrentTextureIndex;
// 切换纹理缓冲索引
CurrentTextureIndex = 1 - CurrentTextureIndex;
}
void AIronBlock::ExecuteDeformationCS()
{
// 确保纹理资源有效
if (!PositionTexture0 || !PositionTexture0->GetResource() ||
!PositionTexture1 || !PositionTexture1->GetResource())
{
UE_LOG(LogTemp, Error, TEXT("Position textures not initialized!"));
return;
}
// 确保纹理资源有效
if (!PositionRenderTarget0 || !PositionRenderTarget0->GetResource() ||
!PositionRenderTarget1 || !PositionRenderTarget1->GetResource())
{
UE_LOG(LogTemp, Error, TEXT("Position render targets not initialized!"));
return;
}
// 获取当前纹理资源
UTexture2D* CurrentTex = (CurrentTextureIndex == 0) ? PositionTexture0 : PositionTexture1;
UTexture2D* PrevTex = (CurrentTextureIndex == 0) ? PositionTexture1 : PositionTexture0;
// --- 关键修复: 决定哪个是读取RT,哪个是写入RT ---
UTextureRenderTarget2D* ReadTarget = (CurrentTextureIndex == 0) ? PositionRenderTarget0 : PositionRenderTarget1;
UTextureRenderTarget2D* WriteTarget = (CurrentTextureIndex == 1) ? PositionRenderTarget0 : PositionRenderTarget1; // Flipped
// 获取纹理RHI资源
FTextureRHIRef InputRHI = GetTextureRHI(PrevTex);
FTextureRHIRef OutputRHI = GetRenderTargetRHI(PositionRenderTarget);
// --- 关键修复: 直接获取 RT 的 RHI 和 UAV ---
FTextureRHIRef InputRHI = GetRenderTargetRHI(ReadTarget);
// 从 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)
{
UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources!"));
return;
}
if (!InputRHI || !OutputUAV)
{
UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources or UAV!"));
return;
}
// 计算纹理UV坐标
const int32 TexSize = PositionRenderTarget->SizeX;
FVector2D ImpactUV(
(CurrentImpactPoint.X - LocalBounds.Min.X) / LocalBounds.GetSize().X,
(CurrentImpactPoint.Y - LocalBounds.Min.Y) / LocalBounds.GetSize().Y
);
// 计算纹理UV坐标
const int32 TexSize = ReadTarget->SizeX;
FVector2D ImpactUV(
(CurrentImpactPoint.X - LocalBounds.Min.X) / LocalBounds.GetSize().X,
(CurrentImpactPoint.Y - LocalBounds.Min.Y) / LocalBounds.GetSize().Y
);
// 计算更新区域 (只更新受影响部分)
const float RadiusRatio = CurrentDeformationRadius / LocalBounds.GetExtent().GetMax();
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);
UpdateRegion.Max.Y = FMath::Min((ImpactUV.Y + RadiusRatio) * TexSize, (float)TexSize);
// 计算更新区域 (只更新受影响部分)
const float RadiusRatio = CurrentDeformationRadius / LocalBounds.GetExtent().GetMax();
FIntRect UpdateRegion;
UpdateRegion.Min.X = FMath::Clamp(FMath::FloorToInt((ImpactUV.X - RadiusRatio) * TexSize), 0, TexSize - 1);
UpdateRegion.Min.Y = FMath::Clamp(FMath::FloorToInt((ImpactUV.Y - RadiusRatio) * TexSize), 0, TexSize - 1);
UpdateRegion.Max.X = FMath::Clamp(FMath::CeilToInt((ImpactUV.X + RadiusRatio) * TexSize), 1, TexSize);
UpdateRegion.Max.Y = FMath::Clamp(FMath::CeilToInt((ImpactUV.Y + RadiusRatio) * TexSize), 1, TexSize);
// 准备参数
FDeformationCSDispatchParams Params;
Params.ImpactPoint = CurrentImpactPoint;
Params.ImpactNormal = CurrentImpactNormal;
Params.ImpactForce = CurrentImpactForce;
Params.DeformationRadius = CurrentDeformationRadius;
Params.Plasticity = CurrentPlasticity;
Params.InputTexture = InputRHI;
// 确保 Min < Max
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;
// 创建UAV用于输出
FUnorderedAccessViewRHIRef OutputUAV;
ENQUEUE_RENDER_COMMAND(CreateOutputUAV)(
[OutputRHI, &OutputUAV](FRHICommandListImmediate& RHICmdList)
{
FRHIViewDesc::FTextureUAV::FInitializer ViewDesc = FRHIViewDesc::CreateTextureUAV();
ViewDesc.SetMipLevel(0);
ViewDesc.SetPlane(ERHITexturePlane::Primary);
ViewDesc.SetFormat(PF_A32B32G32R32F);
// 准备参数
FDeformationCSDispatchParams Params;
Params.ImpactPoint = CurrentImpactPoint; // 确保类型匹配 (FVector -> FVector3f)
Params.ImpactNormal = CurrentImpactNormal;
Params.ImpactForce = CurrentImpactForce;
Params.DeformationRadius = CurrentDeformationRadius;
Params.Plasticity = CurrentPlasticity;
Params.InputTexture = InputRHI;
Params.OutputTexture = OutputUAV; // **直接使用RT的UAV**
Params.UpdateRegion = UpdateRegion;
Params.TextureSize = FIntVector(TexSize, TexSize, 1);
OutputUAV = RHICmdList.CreateUnorderedAccessView(OutputRHI, ViewDesc);
}
);
FlushRenderingCommands();
Params.OutputTexture = OutputUAV;
// 在渲染线程中执行Compute Shader
ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS)(
[Params](FRHICommandListImmediate& RHICmdList)
{
// 在这里调用你的 Compute Shader 调度逻辑
FDeformationCS::Dispatch(RHICmdList, Params);
});
Params.UpdateRegion = UpdateRegion;
Params.TextureSize = FIntVector(TexSize, TexSize, 1);
// 等待渲染命令完成,以便在CPU上读取 (如果需要立即更新碰撞体)
// 注意:这是一个同步点,会影响性能。
// FlushRenderingCommands();
// UpdateCollisionAsync(UpdateRegion, WriteTarget); // 异步更新碰撞体
// 在渲染线程中执行Compute Shader
ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS)(
[Params](FRHICommandListImmediate& RHICmdList)
{
FDeformationCS::Dispatch(RHICmdList, Params);
});
// 更新材质参数
if (DynMaterial)
{
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), CurrentTex);
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMin"), FLinearColor(LocalBounds.Min));
DynMaterial->SetVectorParameterValue(TEXT("LocalBoundsMax"), FLinearColor(LocalBounds.Max));
}
// --- 关键修复: 更新材质参数,使其下一帧读取我们刚刚写入的纹理 ---
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())
{
return;
}
// 从GPU读取关键区域数据
TArray<FColor> SampledData;
FReadSurfaceDataFlags Flags;
Flags.SetLinearToGamma(false);
// 注意:ReadPixels 是一项非常慢的操作 (GPU->CPU 同步)
// 更好的方法是使用另一个Compute Shader将顶点数据简化并读回到一个小的StructuredBuffer中
PositionRenderTarget->GameThread_GetRenderTargetResource()->ReadPixels(
SampledData,
Flags,
FIntRect(UpdateRegion.Min.X, UpdateRegion.Min.Y,
UpdateRegion.Max.X, UpdateRegion.Max.Y)
);
if (!FromTarget || !FromTarget->GetResource())
{
return;
}
// 从GPU读取关键区域数据
TArray<FLinearColor> SampledData; // 使用 FLinearColor
FReadSurfaceDataFlags Flags;
Flags.SetLinearToGamma(false);
// 创建临时碰撞体
TArray<FVector> CollisionVerts;
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);
}
FRenderTarget* RTResource = FromTarget->GameThread_GetRenderTargetResource();
if (!RTResource) return;
// 更新简单碰撞体
UBodySetup* BodySetup = StaticMeshComp->GetBodySetup();
BodySetup->AggGeom.ConvexElems[0].VertexData = CollisionVerts;
BodySetup->CreatePhysicsMeshes();
RTResource->ReadLinearColorPixels(
SampledData,
Flags,
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
void AIronBlock::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
+49 -47
View File
@@ -4,90 +4,92 @@
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
// FDeformComputeShader.h 假定包含了 FDeformationCSDispatchParams 结构
#include "FDeformComputeShader.h"
#include "IronBlock.generated.h"
class UTextureRenderTarget2D;
class UMaterialInstanceDynamic;
class UStaticMeshComponent;
UCLASS()
class BLADESMITH_API AIronBlock : public AActor
{
GENERATED_BODY()
UPROPERTY(EditAnywhere)
UStaticMeshComponent* StaticMeshComp;
UPROPERTY()
UTexture2D* PositionTexture0;
UPROPERTY(EditAnywhere)
UStaticMeshComponent* StaticMeshComp;
/** 用于Ping-Pong缓冲的渲染目标 A */
UPROPERTY()
UTexture2D* PositionTexture1;
UTextureRenderTarget2D* PositionRenderTarget0;
UPROPERTY()
UTextureRenderTarget2D* PositionRenderTarget;
/** 用于Ping-Pong缓冲的渲染目标 B */
UPROPERTY()
UTextureRenderTarget2D* PositionRenderTarget1;
/** 应用于网格的动态材质实例 */
UPROPERTY()
UMaterialInstanceDynamic* DynMaterial;
UPROPERTY()
UMaterialInstanceDynamic* DynMaterial;
public:
// Sets default values for this actor's properties
AIronBlock();
UFUNCTION(BlueprintCallable)
void OnHammerHit(const FHitResult& HitResult);
void OnHammerHit(const FHitResult& HitResult);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() 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);
UTexture2D* CreatePositionTexture(const FString& TexName, int32 Size, const TArray<FLinearColor>& InitialData);
void InitializePositionTexture();
FUnorderedAccessViewRHIRef CreateSafeUAV(UTextureRenderTarget2D* RenderTarget);
void OnEndFrameInitializeTextures();
void SetupDeformationMaterial();
FTextureRHIRef GetTextureRHI(UTexture2D* Texture);
/** 设置动态材质实例和参数 */
void SetupDeformationMaterial();
/** 安全地获取 RenderTarget 的 RHI 资源 */
FTextureRHIRef GetRenderTargetRHI(UTextureRenderTarget2D* RT);
private:
/** 更新 Compute Shader 所需的参数 */
void UpdateComputeShaderParams(FVector LocalImpactPoint, FVector LocalImpactNormal, float ImpactForce);
/** 在渲染线程上执行 Compute Shader */
void ExecuteDeformationCS();
void UpdateCollisionAsync(const FIntRect& UpdateRegion);
/** (可选) 异步更新物理碰撞体 */
void UpdateCollisionAsync(const FIntRect& UpdateRegion, UTextureRenderTarget2D* FromTarget);
/** 计算网格体的本地包围盒 */
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;
// 纹理资源初始化委托
FDelegateHandle TextureInitHandle;
// 当前碰撞参数
// --- 碰撞参数 ---
FVector CurrentImpactPoint;
FVector CurrentImpactNormal;
float CurrentImpactForce = 0.0f;
float CurrentDeformationRadius = 10.0f;
float CurrentPlasticity = 0.7f;
// 双缓冲纹理索引
/** * 双缓冲纹理索引
* 0: 读 RT0, 写 RT1
* 1: 读 RT1, 写 RT0
*/
int32 CurrentTextureIndex = 0;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};