Files
BladeSmith/docs/plans/plastic-deformation-voxel-system-plan.md
T
zhangqing 61016f9c2a Add plastic deformation voxel system implementation plan to project
Move plan from user directory to project docs for cross-machine sync.
This plan describes the two-phase GPU-based deformation system:
stress propagation and displacement calculation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-20 19:29:49 +08:00

724 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BladeSmith塑性变形体素系统 - 实现计划
## 概述
基于现有温度系统架构,构建一个完整的塑性变形体素系统。系统采用**两步法**:
1. **应力传播阶段**:锤击施加应力,通过GPU迭代计算应力在体素间扩散
2. **位移计算阶段**:从应力分布计算位移场,应用到网格顶点
## 核心设计决策
- **物理模型**:简化模型(塑性流动 + 应变软化 + 体积守恒)
- **体素数据**:存储应力、应变、位移(混合方式)
- **GPU架构**:应力Ping-Pong双缓冲 + 独立位移/应变缓冲
- **锤击响应**:两步法(应力注入 → GPU计算 → 顶点更新)
- **内存占用**:36字节/体素(20³网格 = 281KB,100³网格 = 34.4MB)
---
## 一、数据结构设计
### 1.1 变形体素结构(添加到DynamicIronBlock.h)
```cpp
/**
* 变形体素结构 - 存储单个体素的力学状态
* 大小:36字节(紧凑型设计)
*/
USTRUCT(BlueprintType)
struct FDeformationVoxel
{
GENERATED_BODY()
// 应力张量(简化为主应力,6字节)
FFloat16 StressPrincipal; // 主应力(沿锤击方向) - 2字节
FFloat16 StressShear; // 剪应力(切向) - 2字节
uint16 StressFlags; // 应力状态标志 - 2字节
// 累积应变(12字节)
FVector3f Strain; // 累积应变向量 (εx, εy, εz)
// 位移场(当前帧变形,12字节)
FVector3f Displacement; // 位移向量 (Δx, Δy, Δz)
// 物理属性(4字节)
float PlasticFlow; // 塑性流动系数 [0,1],由温度决定
// 辅助数据(6字节)
uint16 MaterialID; // 材料ID
uint32 AuxFlags; // 辅助标志位
FDeformationVoxel()
: StressPrincipal(0.0f), StressShear(0.0f), StressFlags(0)
, Strain(FVector3f::ZeroVector), Displacement(FVector3f::ZeroVector)
, PlasticFlow(0.0f), MaterialID(0), AuxFlags(0)
{}
};
```
### 1.2 类成员变量(添加到DynamicIronBlock.h)
```cpp
// ========== 变形系统数据 ==========
// 变形体素网格(CPU侧)
TArray<FDeformationVoxel> DeformationGrid;
// 原始网格顶点位置(未变形状态)
TArray<FVector3f> OriginalVertexPositions;
// 是否启用变形系统
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deformation System")
bool bEnableDeformationSystem = true;
// 变形系统参数
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deformation System")
float YieldStress = 50.0f; // 屈服应力阈值
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deformation System")
float StressDiffusionRate = 0.3f; // 应力扩散速率
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deformation System")
float VolumeConservationStrength = 0.8f; // 体积守恒强度
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deformation System")
float ComplianceFactor = 0.01f; // 柔度(应力→位移转换系数)
// ========== GPU缓冲区 ==========
// 应力缓冲区(Ping-Pong)
FBufferRHIRef StressBufferA;
FBufferRHIRef StressBufferB;
// 位移和应变缓冲区
FBufferRHIRef DisplacementBuffer; // PF_R32G32B32_FLOAT(3D向量)
FBufferRHIRef StrainBuffer; // PF_R32G32B32_FLOAT
// UAV/SRV视图
FUnorderedAccessViewRHIRef StressUAV_A;
FUnorderedAccessViewRHIRef StressUAV_B;
FUnorderedAccessViewRHIRef DisplacementUAV;
FUnorderedAccessViewRHIRef StrainUAV;
FShaderResourceViewRHIRef StressSRV_A;
FShaderResourceViewRHIRef StressSRV_B;
FShaderResourceViewRHIRef DisplacementSRV;
FShaderResourceViewRHIRef StrainSRV;
bool bUseStressBufferA = true; // Ping-Pong标志
// GPU缓冲区是否准备好
std::atomic<bool> bDeformationGPUBuffersReady{false};
```
---
## 二、GPU计算着色器
### 2.1 应力传播着色器(阶段1)
**文件**: `FStressPropagationShader.h` (新建)
```cpp
class MICESHADERS_API FStressPropagationShader : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FStressPropagationShader);
SHADER_USE_PARAMETER_STRUCT(FStressPropagationShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntVector, VoxelResolution)
SHADER_PARAMETER(float, DeltaTime)
SHADER_PARAMETER(float, StressDiffusionRate)
SHADER_PARAMETER(float, YieldStress)
SHADER_PARAMETER(float, StrainSofteningFactor)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float>, InputStressBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float>, PlasticityBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, SurfaceVoxelFlagsBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<float>, OutputStressBuffer)
END_SHADER_PARAMETER_STRUCT()
static FIntVector GetGroupSize() { return FIntVector(8, 8, 8); }
};
```
**HLSL**: `StressPropagationCS.usf` (新建)
```hlsl
[numthreads(8, 8, 8)]
void MainCompute(uint3 DispatchThreadID : SV_DispatchThreadID)
{
int3 voxelCoord = int3(DispatchThreadID);
if (!IsValidCoord(voxelCoord)) return;
int index = GetVoxelIndex(voxelCoord);
float currentStress = InputStressBuffer[index];
float plasticity = PlasticityBuffer[index];
// === 6邻域应力扩散 ===
float neighborStressSum = 0.0;
int neighborCount = 0;
const int3 neighbors[6] = {
int3(-1,0,0), int3(1,0,0),
int3(0,-1,0), int3(0,1,0),
int3(0,0,-1), int3(0,0,1)
};
for (int i = 0; i < 6; i++) {
int3 neighborCoord = voxelCoord + neighbors[i];
if (IsValidCoord(neighborCoord)) {
neighborStressSum += InputStressBuffer[GetVoxelIndex(neighborCoord)];
neighborCount++;
}
}
float avgNeighborStress = neighborStressSum / max(neighborCount, 1);
// === 应力传播(塑性越高,传播越快) ===
float diffusionRate = StressDiffusionRate * (1.0 + plasticity * 2.0);
float stressDiffusion = diffusionRate * (avgNeighborStress - currentStress) * DeltaTime;
// === 应力松弛(超过屈服点的应力衰减) ===
float stressRelaxation = 0.0;
if (currentStress > YieldStress && plasticity > 0.1) {
float excessStress = currentStress - YieldStress;
stressRelaxation = -excessStress * plasticity * StrainSofteningFactor * DeltaTime;
}
// === 表面应力自由释放 ===
bool isSurface = (SurfaceVoxelFlagsBuffer[index] & 1) != 0;
if (isSurface) {
stressRelaxation += -currentStress * 0.1 * DeltaTime;
}
// === 更新应力 ===
float newStress = max(0.0, currentStress + stressDiffusion + stressRelaxation);
OutputStressBuffer[index] = newStress;
}
```
### 2.2 位移计算着色器(阶段2)
**文件**: `FDisplacementFromStressShader.h` (新建)
```cpp
class MICESHADERS_API FDisplacementFromStressShader : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FDisplacementFromStressShader);
SHADER_USE_PARAMETER_STRUCT(FDisplacementFromStressShader, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntVector, VoxelResolution)
SHADER_PARAMETER(float, DeltaTime)
SHADER_PARAMETER(float, VolumeConservationStrength)
SHADER_PARAMETER(float, ComplianceFactor)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float>, StressBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float>, PlasticityBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float3>, PreviousStrainBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<float3>, OutputDisplacementBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<float3>, OutputStrainBuffer)
END_SHADER_PARAMETER_STRUCT()
static FIntVector GetGroupSize() { return FIntVector(8, 8, 8); }
};
```
**HLSL**: `DisplacementFromStressCS.usf` (新建)
```hlsl
[numthreads(8, 8, 8)]
void MainCompute(uint3 DispatchThreadID : SV_DispatchThreadID)
{
int3 voxelCoord = int3(DispatchThreadID);
if (!IsValidCoord(voxelCoord)) return;
int index = GetVoxelIndex(voxelCoord);
float stress = StressBuffer[index];
float plasticity = PlasticityBuffer[index];
float3 previousStrain = PreviousStrainBuffer[index];
// === 计算应力梯度(主变形方向) ===
float3 stressGradient = ComputeStressGradient(voxelCoord);
float3 deformationDirection = length(stressGradient) > 0.001 ?
normalize(stressGradient) :
float3(0, 0, -1);
// === 计算塑性位移 ===
float displacementMagnitude = stress * ComplianceFactor * plasticity;
float3 plasticDisplacement = deformationDirection * displacementMagnitude;
// === 体积守恒修正 ===
float compressionAmount = dot(plasticDisplacement, deformationDirection);
float lateralExpansion = -compressionAmount * VolumeConservationStrength * 0.5;
float3 lateral1 = normalize(cross(deformationDirection, float3(0, 0, 1)));
float3 lateral2 = normalize(cross(deformationDirection, lateral1));
float3 volumeCompensation = (lateral1 + lateral2) * lateralExpansion;
// === 最终位移和应变 ===
float3 totalDisplacement = plasticDisplacement + volumeCompensation;
float3 newStrain = previousStrain + totalDisplacement * DeltaTime;
OutputDisplacementBuffer[index] = totalDisplacement;
OutputStrainBuffer[index] = newStrain;
}
```
---
## 三、锤击响应流程
### 3.1 修改OnHammerImpact(DynamicIronBlock.cpp)
```cpp
void ADynamicIronBlock::OnHammerImpact(
FVector HitLocation, FVector HitNormal,
float ImpactRadius, float ImpactDepth, float ImpactForce)
{
if (!bEnableDeformationSystem || DeformationGrid.Num() == 0)
{
// 回退到旧逻辑(直接修改顶点)
OnHammerImpact_Legacy(HitLocation, HitNormal, ImpactRadius, ImpactDepth, ImpactForce);
return;
}
// === 步骤1:应力注入 ===
ApplyImpactStressToVoxels(HitLocation, HitNormal, ImpactRadius, ImpactDepth, ImpactForce);
// === 步骤2:GPU应力传播(迭代3次) ===
for (int i = 0; i < 3; i++)
{
DispatchStressPropagationCompute(0.016f); // 16ms时间步
}
// === 步骤3:GPU位移计算 ===
DispatchDisplacementCompute(0.016f);
// === 步骤4:下载并应用到网格 ===
DownloadDeformationDataAndApplyToMesh();
}
```
### 3.2 应力注入函数
```cpp
void ADynamicIronBlock::ApplyImpactStressToVoxels(
FVector HitLocation, FVector HitNormal,
float ImpactRadius, float ImpactDepth, float ImpactForce)
{
// 转换到局部坐标
FTransform ActorTransform = GetActorTransform();
FVector LocalHitLocation = ActorTransform.InverseTransformPosition(HitLocation);
// 计算体素坐标
FVector GridOrigin = -BoxSize * 0.5f;
FVector VoxelSizeCM = FVector(TemperatureVoxelSize / 10.0f);
// 遍历影响范围
for (int32 Z = 0; Z < TemperatureResolution.Z; ++Z)
{
for (int32 Y = 0; Y < TemperatureResolution.Y; ++Y)
{
for (int32 X = 0; X < TemperatureResolution.X; ++X)
{
FVector VoxelCenter = GridOrigin + FVector(
(X + 0.5f) * VoxelSizeCM.X,
(Y + 0.5f) * VoxelSizeCM.Y,
(Z + 0.5f) * VoxelSizeCM.Z
);
float Distance = FVector::Dist(VoxelCenter, LocalHitLocation);
if (Distance < ImpactRadius)
{
int32 Index = GetTemperatureIndex(X, Y, Z);
// 高斯衰减
float Falloff = FMath::Exp(-FMath::Square(Distance / ImpactRadius) * 3.0f);
// 应力大小
float StressMagnitude = ImpactForce * ImpactDepth * Falloff * 100.0f;
// 注入到体素
if (DeformationGrid.IsValidIndex(Index))
{
DeformationGrid[Index].StressPrincipal = FMath::Min(
DeformationGrid[Index].StressPrincipal.GetFloat() + StressMagnitude,
1000.0f // 应力上限
);
}
}
}
}
}
// 上传到GPU
UploadDeformationStressToGPU();
}
```
---
## 四、顶点更新机制
### 4.1 从体素位移场采样到顶点
```cpp
void ADynamicIronBlock::UpdateVertexDeformation()
{
UDynamicMeshComponent* DynMeshComp = GetDynamicMeshComponent();
if (!DynMeshComp) return;
UDynamicMesh* DynMesh = DynMeshComp->GetDynamicMesh();
if (!DynMesh) return;
// 修改网格顶点
DynMesh->EditMesh([&](FDynamicMesh3& EditMesh)
{
for (int32 VertexID : EditMesh.VertexIndicesItr())
{
// 获取原始顶点位置
FVector3d OriginalPos = FVector3d(OriginalVertexPositions[VertexID]);
// 采样体素位移场(三线性插值)
FVector Displacement = SampleDisplacementAtPosition(FVector(OriginalPos));
// 新位置 = 原始位置 + 位移
FVector3d NewPosition = OriginalPos + FVector3d(Displacement);
EditMesh.SetVertex(VertexID, NewPosition);
}
}, EDynamicMeshChangeType::GeneralEdit, EDynamicMeshAttributeChangeFlags::Unknown, false);
// 重新计算法线
FGeometryScriptCalculateNormalsOptions NormalOp;
UGeometryScriptLibrary_MeshNormalsFunctions::RecomputeNormals(DynMesh, NormalOp);
DynMeshComp->NotifyMeshUpdated();
}
```
### 4.2 三线性插值采样位移
```cpp
FVector ADynamicIronBlock::SampleDisplacementAtPosition(const FVector& LocalPosition) const
{
if (!bEnableDeformationSystem || DeformationGrid.Num() == 0)
{
return FVector::ZeroVector;
}
// 转换到体素坐标系
FVector GridOrigin = -BoxSize * 0.5f;
FVector VoxelSizeCM = FVector(TemperatureVoxelSize / 10.0f);
FVector VoxelCoord = (LocalPosition - GridOrigin) / VoxelSizeCM;
// 获取相邻8个体素
int32 X0 = FMath::FloorToInt(VoxelCoord.X);
int32 Y0 = FMath::FloorToInt(VoxelCoord.Y);
int32 Z0 = FMath::FloorToInt(VoxelCoord.Z);
int32 X1 = X0 + 1, Y1 = Y0 + 1, Z1 = Z0 + 1;
// 边界检查
if (!IsValidTemperatureCoord(X0, Y0, Z0) || !IsValidTemperatureCoord(X1, Y1, Z1))
{
return FVector::ZeroVector;
}
// 插值权重
float Fx = VoxelCoord.X - X0;
float Fy = VoxelCoord.Y - Y0;
float Fz = VoxelCoord.Z - Z0;
// 获取8个角点的位移
auto GetDisp = [&](int X, int Y, int Z) -> FVector
{
int32 Index = GetTemperatureIndex(X, Y, Z);
return FVector(DeformationGrid[Index].Displacement);
};
FVector D000 = GetDisp(X0, Y0, Z0);
FVector D100 = GetDisp(X1, Y0, Z0);
FVector D010 = GetDisp(X0, Y1, Z0);
FVector D110 = GetDisp(X1, Y1, Z0);
FVector D001 = GetDisp(X0, Y0, Z1);
FVector D101 = GetDisp(X1, Y0, Z1);
FVector D011 = GetDisp(X0, Y1, Z1);
FVector D111 = GetDisp(X1, Y1, Z1);
// 三线性插值
FVector D00 = FMath::Lerp(D000, D100, Fx);
FVector D10 = FMath::Lerp(D010, D110, Fx);
FVector D01 = FMath::Lerp(D001, D101, Fx);
FVector D11 = FMath::Lerp(D011, D111, Fx);
FVector D0 = FMath::Lerp(D00, D10, Fy);
FVector D1 = FMath::Lerp(D01, D11, Fy);
return FMath::Lerp(D0, D1, Fz);
}
```
---
## 五、实现步骤
### 阶段1:基础架构(2-3天)
**目标**:建立数据结构和CPU侧逻辑
1. **添加数据结构** (DynamicIronBlock.h)
- 定义 `FDeformationVoxel` 结构体
- 添加 `DeformationGrid`、`OriginalVertexPositions` 成员变量
- 添加变形系统参数(YieldStress、StressDiffusionRate等)
2. **实现初始化** (DynamicIronBlock.cpp)
- `InitializeDeformationGrid()`: 分配体素数组
- 在 `InitializeBoxMesh()` 中保存原始顶点位置
3. **实现应力注入**
- `ApplyImpactStressToVoxels()`: CPU版本应力注入
- 测试:打印应力分布,验证高斯衰减
4. **实现位移采样**
- `SampleDisplacementAtPosition()`: 三线性插值
- `UpdateVertexDeformation()`: 应用到网格
- 测试:手动设置位移值,观察网格变形
### 阶段2:GPU缓冲区(1-2天)
**目标**:建立GPU缓冲区,实现CPU-GPU数据流
5. **创建GPU缓冲区**
- `CreateDeformationGPUBuffers()`: 创建所有RHI缓冲区
- `ReleaseDeformationGPUBuffers()`: 清理资源
6. **数据传输**
- `UploadDeformationStressToGPU()`: 应力数据 CPU→GPU
- `DownloadDeformationDataAndApplyToMesh()`: 位移数据 GPU→CPU
- 测试:上传后立即下载,验证数据完整性
### 阶段3:GPU计算着色器(3-4天)
**目标**:实现两阶段GPU计算
7. **应力传播Shader**
- 新建 `FStressPropagationShader.h/cpp` (参考FTemperatureComputeShader)
- 新建 `StressPropagationCS.usf`
- 实现6邻域扩散 + 应力松弛
- 测试:单点应力观察传播
8. **位移计算Shader**
- 新建 `FDisplacementFromStressShader.h/cpp`
- 新建 `DisplacementFromStressCS.usf`
- 实现应力→位移 + 体积守恒
- 测试:固定应力验证位移
9. **调度逻辑**
- `DispatchStressPropagationCompute()`: RDG调度应力传播
- `DispatchDisplacementCompute()`: RDG调度位移计算
- 测试:完整流程(应力→传播→位移→网格)
### 阶段4:集成和优化(2-3天)
**目标**:完整集成,调优参数
10. **修改锤击逻辑**
- 更新 `OnHammerImpact()` 使用新系统
- 保留旧逻辑作为fallback (`OnHammerImpact_Legacy()`)
11. **参数调优**
- 调整物理参数观察效果
- 确保变形符合物理直觉
12. **性能优化**
- 添加性能日志
- 优化迭代次数
- 考虑局部更新
13. **调试工具**
- 可视化应力分布(顶点颜色)
- 可视化位移场(Debug Gizmo)
---
## 六、关键文件清单
### 需要修改的文件
1. **C:\MiceCola\Indi\BladeSmith\Source\BladeSmith\DynamicIronBlock.h**
- 添加 `FDeformationVoxel` 结构体
- 添加变形系统成员变量和参数
- 添加新函数声明(约15个函数)
2. **C:\MiceCola\Indi\BladeSmith\Source\BladeSmith\DynamicIronBlock.cpp**
- 实现所有变形系统函数
- 修改 `OnHammerImpact()` 为两步法
- 修改 `BeginPlay()`、`EndPlay()`、`InitializeBoxMesh()`
### 需要新建的文件
3. **C:\MiceCola\Indi\BladeSmith\Plugins\MiceShaders\Source\MiceShaders\Public\FStressPropagationShader.h**
- 应力传播着色器类声明
4. **C:\MiceCola\Indi\BladeSmith\Plugins\MiceShaders\Source\MiceShaders\Private\FStressPropagationShader.cpp**
- Shader实现(IMPLEMENT_GLOBAL_SHADER)
5. **C:\MiceCola\Indi\BladeSmith\Plugins\MiceShaders\Source\MiceShaders\Public\FDisplacementFromStressShader.h**
- 位移计算着色器类声明
6. **C:\MiceCola\Indi\BladeSmith\Plugins\MiceShaders\Source\MiceShaders\Private\FDisplacementFromStressShader.cpp**
- Shader实现
7. **C:\MiceCola\Indi\BladeSmith\Plugins\MiceShaders\Shaders\Private\StressPropagationCS.usf**
- 应力传播HLSL代码
8. **C:\MiceCola\Indi\BladeSmith\Plugins\MiceShaders\Shaders\Private\DisplacementFromStressCS.usf**
- 位移计算HLSL代码
---
## 七、验证和测试
### 单元测试
1. **体素索引测试**
- 验证 `GetTemperatureIndex()` 3D→1D转换正确性
- 测试边界情况
2. **插值测试**
- 设置8个体素位移为已知值
- 验证中心点插值结果精度
3. **应力衰减测试**
- 注入单点应力
- 验证距离衰减符合高斯曲线
### 集成测试
4. **锤击响应测试**
- **冷态测试**(塑性=0):锤击应无明显变形
- **热态测试**(塑性=1):锤击应有深度变形
- **体积守恒验证**:压缩区深度 ≈ 凸起区体积
5. **性能测试**
- 测量每次锤击耗时(目标 <7ms)
- 验证60fps稳定性
- GPU占用率监控
### 物理验证
6. **应力传播验证**
- 单点冲击应力向周围扩散
- 表面体素应力快速释放
7. **温度-塑性耦合验证**
- 高温区域深度变形
- 低温区域轻微变形
### 端到端测试
8. **完整流程测试**
```
1. 启动游戏 → 加载DynamicIronBlock
2. 开启自动加热 → 观察温度上升
3. 等待塑性系数 > 0.5
4. 执行锤击 → 观察变形
5. 验证:顶点位置改变、法线重新计算、BVH更新
```
---
## 八、性能预期
### 内存占用
- **小网格(20×20×20)**:约 0.5 MB
- **中网格(50×50×50)**:约 4 MB
- **大网格(100×100×100)**:约 67 MB
### 计算延迟(一次锤击)
- GPU应力传播:0.1ms × 3迭代 = 0.3ms
- GPU位移计算:0.1ms
- CPU下载:约 1ms(异步)
- 顶点更新:约 5ms(10万顶点)
- **总延迟**:约 6-7ms(<16.7ms/60fps)✅
---
## 九、物理模型说明
### 简化的应力-应变关系
本系统使用简化模型:
```
位移 = 应力 × 柔度 × 塑性系数
Δu = σ × C × P
```
其中:
- `σ`:应力(Pa)
- `C`:柔度系数 (ComplianceFactor)
- `P`:塑性系数(温度决定,0-1)
### 温度对塑性的影响
塑性系数从温度系统读取(复用PlasticityBuffer):
```
P = Smoothstep((T - T_min) / (T_max - T_min))
```
- 温度越高 → 塑性越强 → 应力更易转换为位移
### 体积守恒近似
压缩方向位移后,横向补偿:
```
压缩量 = |位移 · 方向|
横向膨胀 = -压缩量 × 守恒强度 × 0.5
```
---
## 十、调试建议
1. **日志级别**
- 使用 `UE_LOG(LogTemp, Warning, ...)` 输出关键数据
- 每个阶段添加性能计时
2. **可视化工具**
- 顶点颜色显示应力分布(蓝色=低,红色=高)
- Debug Gizmo绘制体素应力箭头
3. **参数调优UI**
- 所有物理参数暴露到Blueprint
- 实时调整观察效果
4. **逐步验证**
- 先验证CPU逻辑(阶段1)
- 再验证GPU传输(阶段2)
- 最后验证GPU计算(阶段3)
---
## 十一、与现有系统的集成
### 与温度系统协作
- **共享缓冲区**:`PlasticityBuffer`、`SurfaceVoxelFlagsBuffer`
- **共享分辨率**:`TemperatureResolution` 用于变形体素网格
- **调度顺序**:温度计算(每1秒) → 变形计算(锤击触发)
### 与BVH系统协作
- 每次 `UpdateVertexDeformation()` 后调用 `GenerateBVH()`
- 确保碰撞检测使用最新几何
### 与顶点颜色系统协作
- 可选:混合显示温度(红色)+ 应力(蓝色)
- 或独立显示:切换模式(温度模式 / 应力模式)
---
**计划完成**。建议按照实现步骤逐步推进,每个阶段充分测试后再进入下一阶段。