525 lines
19 KiB
C++
525 lines
19 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "IronBlock.h"
|
|
// 确保包含了 RDG 所需的头文件
|
|
#include "RenderGraphBuilder.h"
|
|
#include "RenderGraphUtils.h" // FComputeShaderUtils::AddPass
|
|
#include "FDeformComputeShader.h" // 确保这个头文件存在且包含了 FDeformationCSDispatchParams
|
|
#include "StaticMeshResources.h"
|
|
#include "TextureResource.h"
|
|
#include "Engine/TextureRenderTarget2D.h"
|
|
#include "Engine/StaticMesh.h"
|
|
#include "Engine/Texture2D.h"
|
|
#include "Materials/MaterialInstanceDynamic.h"
|
|
#include "PhysicsEngine/BodySetup.h"
|
|
#include "RHICommandList.h"
|
|
#include "Engine/Texture.h"
|
|
#include "GenericPlatform/GenericPlatformProcess.h"
|
|
|
|
|
|
// Sets default values
|
|
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;
|
|
|
|
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent"));
|
|
RootComponent = StaticMeshComp;
|
|
}
|
|
void AIronBlock::PostInitProperties()
|
|
{
|
|
// 必须调用父类实现
|
|
Super::PostInitProperties();
|
|
|
|
// 检查:如果 StaticMeshComp 没有被赋予任何网格(即在编辑器中没有设置)
|
|
// 并且我们正在处理 Class Default Object (CDO),通常用来设置类级的默认值
|
|
if (StaticMeshComp && !StaticMeshComp->GetStaticMesh())
|
|
{
|
|
// 只有当网格为空时,才加载并设置默认的 Cube
|
|
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();
|
|
InitializePositionTexture();
|
|
}
|
|
|
|
|
|
void AIronBlock::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
// RenderTargets 会被 UPROPERTY 自动 GC,无需手动释放 RHI 资源
|
|
|
|
Super::EndPlay(EndPlayReason);
|
|
}
|
|
|
|
// 安全的渲染目标获取
|
|
FTextureRHIRef AIronBlock::GetRenderTargetRHI(UTextureRenderTarget2D* RT)
|
|
{
|
|
if (!RT || !RT->GetResource()) return nullptr;
|
|
return RT->GetResource()->TextureRHI;
|
|
}
|
|
|
|
void AIronBlock::GetStaticMeshVertexPositions(TArray<FVector3f>& OutPositions)
|
|
{
|
|
OutPositions.Empty();
|
|
|
|
if (!StaticMeshComp || !StaticMeshComp->GetStaticMesh())
|
|
return;
|
|
|
|
// 获取静态网格资源
|
|
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);
|
|
|
|
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->InitCustomFormat(Size, Size, PF_A32B32G32R32F, false);
|
|
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()
|
|
{
|
|
CalculateLocalBounds();
|
|
|
|
// 获取顶点位置 (用于计算初始UV->WorldPos)
|
|
TArray<FVector3f> VertexPositions;
|
|
GetStaticMeshVertexPositions(VertexPositions);
|
|
|
|
// 创建位置纹理
|
|
const int32 TextureSize = 256; // 纹理尺寸
|
|
TArray<FLinearColor> InitialData;
|
|
InitialData.SetNum(TextureSize * TextureSize);
|
|
|
|
// 初始化纹理数据 (将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)
|
|
|
|
// 将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 // 假设初始是一个平面
|
|
);
|
|
|
|
InitialData[Index] = FLinearColor(LocalPos.X * 1000, LocalPos.Y * 1000, LocalPos.Z * 1000, 1.0f);
|
|
}
|
|
}
|
|
|
|
// --- 关键修复: 创建两个 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(TempTexture);
|
|
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 (LocalBounds.GetVolume() <= 0)
|
|
{
|
|
LocalBounds = FBox(FVector(-50, -50, 0), FVector(50, 50, 10));
|
|
}
|
|
}
|
|
|
|
void AIronBlock::SetupDeformationMaterial(UTexture2D* OrigTex)
|
|
{
|
|
// 创建动态材质实例
|
|
if (StaticMeshComp)
|
|
{
|
|
// 加载基础材质 (请确保你的材质支持读取 PositionTexture)
|
|
UMaterial* BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Game/Materials/M_Iron")); // 示例路径
|
|
if (!BaseMaterial)
|
|
{
|
|
BaseMaterial = LoadObject<UMaterial>(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial"));
|
|
}
|
|
|
|
if (BaseMaterial)
|
|
{
|
|
DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, this);
|
|
StaticMeshComp->SetMaterial(0, DynMaterial);
|
|
|
|
// 设置初始参数
|
|
if (DynMaterial)
|
|
{
|
|
// 初始时读取 RT0
|
|
DynMaterial->SetTextureParameterValue(TEXT("PositionTexture"), OrigTex);
|
|
//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;
|
|
// 检查 HitResult.GetComponent() 是否有效
|
|
if (HitResult.GetComponent() && !HitResult.GetComponent()->GetPhysicsLinearVelocity().IsNearlyZero())
|
|
{
|
|
ImpactForce = HitResult.GetComponent()->GetPhysicsLinearVelocity().Size();
|
|
}
|
|
|
|
// 默认冲击力
|
|
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);
|
|
UE_LOG(LogTemp,Display,TEXT("[AIronBlock::OnHammerHit] ImpactForce:%f,ImpactPoint:%s,ImpactNormal:%s,LocalImpactPoint:%s,LocalImpactNormal:%s")
|
|
,ImpactForce,*ImpactPoint.ToString(),*ImpactNormal.ToString(),*LocalImpactPoint.ToString(),*LocalImpactNormal.ToString());
|
|
UpdateComputeShaderParams(LocalImpactPoint, LocalImpactNormal, ImpactForce);
|
|
|
|
ExecuteDeformationCS();
|
|
}
|
|
|
|
|
|
void AIronBlock::UpdateComputeShaderParams(FVector LocalImpactPoint, FVector LocalImpactNormal, float ImpactForce)
|
|
{
|
|
// 更新碰撞参数
|
|
CurrentImpactPoint = LocalImpactPoint;
|
|
CurrentImpactNormal = LocalImpactNormal.GetSafeNormal();
|
|
CurrentImpactForce = FMath::Clamp(ImpactForce, 0.0f, 5000.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; // 默认高塑性
|
|
}
|
|
|
|
// 切换纹理缓冲索引
|
|
CurrentTextureIndex = 1 - CurrentTextureIndex;
|
|
UE_LOG(LogTemp,Display,TEXT("[AIronBlock::OnHammerHitUpdateComputeShaderParams]CurrentImpactForce:%f, CurrentImpactPoint:%s,CurrentImpactNormal:%s,CurrentDeformationRadius:%f,CurrentPlasticity:%f")
|
|
,CurrentImpactForce,*CurrentImpactPoint.ToString(),*CurrentImpactNormal.ToString(),CurrentDeformationRadius,CurrentPlasticity);
|
|
}
|
|
|
|
void AIronBlock::ExecuteDeformationCS()
|
|
{
|
|
// 确保纹理资源有效
|
|
if (!PositionRenderTarget0 || !PositionRenderTarget0->GetResource() ||
|
|
!PositionRenderTarget1 || !PositionRenderTarget1->GetResource())
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("Position render targets not initialized!"));
|
|
return;
|
|
}
|
|
|
|
// --- 关键修复: 决定哪个是读取RT,哪个是写入RT ---
|
|
UTextureRenderTarget2D* ReadTarget = (CurrentTextureIndex == 0) ? PositionRenderTarget0 : PositionRenderTarget1;
|
|
UTextureRenderTarget2D* WriteTarget = (CurrentTextureIndex == 1) ? PositionRenderTarget0 : PositionRenderTarget1; // Flipped
|
|
|
|
// --- 关键修复: 在游戏线程只获取 Input RHI Texture 句柄 ---
|
|
FTextureRHIRef InputRHI = GetRenderTargetRHI(ReadTarget);
|
|
FTextureRHIRef WriteRHI = GetRenderTargetRHI(WriteTarget); // 必须获取写入目标的RHI
|
|
|
|
if (!InputRHI.IsValid() || !WriteRHI.IsValid())
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("Invalid texture RHI resources!"));
|
|
return;
|
|
}
|
|
|
|
// 计算纹理UV坐标
|
|
const int32 TexSize = ReadTarget->SizeX;
|
|
FVector2D ImpactUV(
|
|
(CurrentImpactPoint.X - LocalBounds.Min.X) / LocalBounds.GetSize().X,
|
|
(CurrentImpactPoint.Y - LocalBounds.Min.Y) / LocalBounds.GetSize().Y
|
|
);
|
|
UE_LOG(LogTemp,Display,TEXT("[AIronBlock::ExecuteDeformationCS]LocalBounds:%s")
|
|
,*LocalBounds.ToString());
|
|
|
|
// 假设 LocalBounds 仍然是未缩放的原始包围盒
|
|
// 获得 Actor 的缩放
|
|
FVector ComponentScale = StaticMeshComp->GetComponentScale();
|
|
|
|
// 1. 计算缩放后包围盒的最大延伸 (在 X/Y 平面上)
|
|
// 假设您的形变发生在 XY 平面
|
|
float MaxLocalExtentX = LocalBounds.GetExtent().X * ComponentScale.X;
|
|
float MaxLocalExtentY = LocalBounds.GetExtent().Y * ComponentScale.Y;
|
|
float MaxScaledExtent = FMath::Max(MaxLocalExtentX, MaxLocalExtentY);
|
|
|
|
// 2. 使用这个缩放后的最大延伸进行比例计算
|
|
// 这里的 CurrentDeformationRadius 应该是一个世界空间的值。
|
|
const float RadiusRatio = CurrentDeformationRadius / MaxScaledExtent;
|
|
// 计算更新区域 (只更新受影响部分)
|
|
//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);
|
|
|
|
// 确保 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;
|
|
|
|
// 临时代码,先更新全部区域
|
|
UpdateRegion.Min.X = 0;
|
|
UpdateRegion.Min.Y = 0;
|
|
UpdateRegion.Max.X = TexSize;
|
|
UpdateRegion.Max.Y = TexSize;
|
|
|
|
// --- 关键修改:使用 RDG ---
|
|
ENQUEUE_RENDER_COMMAND(ExecuteDeformationCS_RDG)(
|
|
[
|
|
// 捕获所有需要的参数
|
|
InputRHI,
|
|
WriteRHI,
|
|
CurrentImpactPoint = this->CurrentImpactPoint, // 拷贝一份
|
|
CurrentImpactNormal = this->CurrentImpactNormal,
|
|
CurrentImpactForce = this->CurrentImpactForce,
|
|
CurrentDeformationRadius = this->CurrentDeformationRadius,
|
|
CurrentPlasticity = this->CurrentPlasticity,
|
|
UpdateRegion, //
|
|
TexSize //
|
|
](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
// 1. 创建 RDG Builder
|
|
FRDGBuilder builder(RHICmdList);
|
|
|
|
// 2. 将我们的外部 RT 注册到 RDG 中
|
|
// 注意:我们使用 WriteRHI->GetDesc() 来获取纹理描述
|
|
FRDGTextureRef RDGReadTexture = builder.RegisterExternalTexture(
|
|
CreateRenderTarget(InputRHI, TEXT("DeformInputTex"))
|
|
);
|
|
FRDGTextureRef RDGWriteTexture = builder.RegisterExternalTexture(
|
|
CreateRenderTarget(WriteRHI, TEXT("DeformOutputTex"))
|
|
);
|
|
|
|
// 3. 分配着色器参数
|
|
FDeformComputeShader::FParameters* Params = builder.AllocParameters<FDeformComputeShader::FParameters>();
|
|
|
|
// 4. 填充 CPU 参数
|
|
Params->ImpactPoint = FVector3f(CurrentImpactPoint); // 注意:从 FVector 转为 FVector3f
|
|
Params->ImpactNormal = FVector3f(CurrentImpactNormal);
|
|
Params->ImpactForce = CurrentImpactForce;
|
|
Params->DeformationRadius = CurrentDeformationRadius;
|
|
Params->Plasticity = CurrentPlasticity;
|
|
Params->UpdateRegionParam = FIntVector4(UpdateRegion.Min.X, UpdateRegion.Min.Y, UpdateRegion.Max.X, UpdateRegion.Max.Y);
|
|
Params->TextureSize = FIntVector(TexSize, TexSize, 1);
|
|
UE_LOG(LogTemp,Display,TEXT("[AIronBlock::ExecuteDeformationCS]ImpactForce:%f, ImpactPoint:%s,ImpactNormal:%s,DeformationRadius:%f,Plasticity:%f,UpdateRegion:%s")
|
|
,Params->ImpactForce,*Params->ImpactPoint.ToString(),*Params->ImpactNormal.ToString(),Params->DeformationRadius,Params->Plasticity,*Params->UpdateRegionParam.ToString());
|
|
|
|
|
|
// 5. ✨ 魔法发生的地方:RDG 为我们创建 SRV 和 UAV
|
|
// 这就是解决你 nullptr 问题的核心
|
|
Params->InputTexture = builder.CreateSRV(RDGReadTexture);
|
|
Params->OutputTexture = builder.CreateUAV(RDGWriteTexture);
|
|
|
|
// 6. 获取着色器实例
|
|
TShaderMapRef<FDeformComputeShader> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
|
|
if (!ComputeShader.IsValid())
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("FDeformComputeShader is not valid!"));
|
|
return;
|
|
}
|
|
|
|
// 7. 计算线程组数量
|
|
// (注意:你的USF需要处理 UpdateRegion,这里我们调度整个区域)
|
|
FIntVector NumGroups = FIntVector::DivideAndRoundUp(
|
|
FIntVector(UpdateRegion.Width(), UpdateRegion.Height(), 1),
|
|
FDeformComputeShader::GetGroupSize()
|
|
);
|
|
// 如果你的shader总是处理整个纹理,请使用:
|
|
// FIntVector NumGroups = FIntVector::DivideAndRoundUp(
|
|
// Params->TextureSize, FDeformComputeShader::GetGroupSize()
|
|
// );
|
|
|
|
// 8. 添加 Compute Pass
|
|
FComputeShaderUtils::AddPass(
|
|
builder,
|
|
RDG_EVENT_NAME("ExecuteDeformationCS"),
|
|
ComputeShader,
|
|
Params,
|
|
NumGroups
|
|
);
|
|
|
|
// 9. 执行 RDG (这会处理所有屏障和调度)
|
|
builder.Execute();
|
|
});
|
|
|
|
// --- 结束 RDG 修改 ---
|
|
|
|
// 确保渲染命令完成 (如果你需要立即更新碰撞体)
|
|
// 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, UTextureRenderTarget2D* FromTarget)
|
|
{
|
|
// 注意:ReadPixels 是一项非常慢的操作 (GPU->CPU 同步)
|
|
// 更好的方法是使用另一个Compute Shader将顶点数据简化并读回到一个小的StructuredBuffer中
|
|
|
|
if (!FromTarget || !FromTarget->GetResource())
|
|
{
|
|
return;
|
|
}
|
|
// 从GPU读取关键区域数据
|
|
TArray<FLinearColor> SampledData; // 使用 FLinearColor
|
|
FReadSurfaceDataFlags Flags;
|
|
Flags.SetLinearToGamma(false);
|
|
|
|
FRenderTarget* RTResource = FromTarget->GameThread_GetRenderTargetResource();
|
|
if (!RTResource) return;
|
|
|
|
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);
|
|
} |