Files
BladeSmith/docs/_archive/plans/2026-01-22-物理模拟架构细节.md
T
starmen1265 56cb314799 docs: 归档原 plans 目录,迁移实施清单到 impl/
原 docs/plans/ 下的 10 份混合文档(需求+设计混杂)归档到 docs/_archive/plans/ 作为决策考古用途。hammer-deformation-enhancement 实施清单迁移到 docs/impl/。后续工作产出统一进入 docs/requirements/ 与 docs/design/ 两个清晰分层。

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-11 08:57:46 +08:00

27 KiB
Raw Blame History

物理模拟架构细节

所属系统: 高真实度锻造系统 文档类型: 技术实现细节 最后更新: 2026-01-22


1. 系统概述

1.1 目标

实现游戏娱乐级的金属锻造物理模拟,包含:

  • 加工硬化: 应变累积导致材料变硬
  • 应力分布: 不均匀形变导致内部应力
  • 淬火硬化: 快速冷却导致的马氏体相变
  • 晶粒演化: 温度和应变影响微观组织

1.2 架构分层

┌────────────────────────────────────────┐
│         游戏逻辑层(CPU)                │
│  - 锤击输入处理                         │
│  - 形变触发                             │
│  - 参数控制                             │
└──────────────┬─────────────────────────┘
               │
┌──────────────▼─────────────────────────┐
│      顶点层(CPU/GPU混合)              │
│  - 累积应变                             │
│  - 硬度系数                             │
│  - 晶粒尺寸                             │
│  - 应力值                               │
└──────────────┬─────────────────────────┘
               │ 双向采样
┌──────────────▼─────────────────────────┐
│       体素层(GPU Compute Shader)       │
│  - 温度场                               │
│  - 应变率场                             │
│  - 冷却速率场                           │
│  - 相态标记                             │
└────────────────────────────────────────┘

2. 数据结构

2.1 扩展体素结构

USTRUCT(BlueprintType)
struct FTemperatureVoxel
{
    GENERATED_BODY()

    // ===== 已有字段(保留) =====
    UPROPERTY()
    float Temperature;              // 温度(°C)

    UPROPERTY()
    float PlasticityCoefficient;    // 塑性系数(0-1)

    UPROPERTY()
    uint8 Flags;                    // 标志位(bit 0: 表面体素)

    // ===== 新增字段 =====

    // 平均应变率(记录形变活跃度)
    UPROPERTY()
    float AverageStrainRate;        // 0-1范围,归一化的应变率

    // 冷却速率(用于淬火判断)
    UPROPERTY()
    float CoolingRate;              // °C/s

    // 相态标记
    UPROPERTY()
    EPhaseState PhaseState;         // 奥氏体/马氏体/珠光体

    // 淬火标记(是否经历过快速冷却)
    UPROPERTY()
    bool bHasBeenQuenched;          // 淬火状态

    FTemperatureVoxel()
        : Temperature(20.0f)
        , PlasticityCoefficient(0.0f)
        , Flags(0)
        , AverageStrainRate(0.0f)
        , CoolingRate(0.0f)
        , PhaseState(EPhaseState::Austenite)  // 初始为奥氏体
        , bHasBeenQuenched(false)
    {}
};

/**
 * 相态枚举
 */
UENUM(BlueprintType)
enum class EPhaseState : uint8
{
    Austenite      UMETA(DisplayName = "奥氏体"),    // 高温软相(>800°C)
    Martensite     UMETA(DisplayName = "马氏体"),    // 淬火硬相
    Pearlite       UMETA(DisplayName = "珠光体"),    // 缓冷软相
    Bainite        UMETA(DisplayName = "贝氏体"),    // 中间相
};

2.2 顶点属性结构

class ADynamicIronBlock
{
private:
    // ===== 顶点自定义属性层 =====

    // 初始位置(用于无漂移纹理采样)
    TDynamicMeshVertexAttribute<FVector>* InitialPositions;

    // 累积塑性应变(0-1,归一化)
    TDynamicMeshVertexAttribute<float>* AccumulatedStrain;

    // 当前硬度系数(0-1,0=最软,1=最硬)
    TDynamicMeshVertexAttribute<float>* HardnessCoefficient;

    // 打磨度(表面状态,0=生锈,1=光亮)
    TDynamicMeshVertexAttribute<float>* PolishAmount;

    // 晶粒尺寸(0-1,归一化,0=细晶粒,1=粗晶粒)
    TDynamicMeshVertexAttribute<float>* GrainSize;

    // 应力值(简化标量,0-1,0=无应力,1=高应力)
    TDynamicMeshVertexAttribute<float>* StressValue;

    // 应力方向(可选,三维向量)
    TDynamicMeshVertexAttribute<FVector>* StressDirection;

public:
    // 初始化所有顶点属性
    void InitializeVertexAttributes();
};

3. 加工硬化模型

3.1 物理原理

加工硬化(Work Hardening):

  • 现象: 金属在塑性形变后变硬,屈服强度提高
  • 机理: 位错密度增加,晶格畸变
  • 可逆性: 加热到再结晶温度(约600°C)后软化

3.2 简化模型

/**
 * 应变-硬度映射(简化经验公式)
 * 基于真实钢的应力-应变曲线拟合
 */
float CalculateHardnessFromStrain(float AccumulatedStrain)
{
    // 参数(可调)
    const float InitialHardness = 0.2f;     // 初始硬度
    const float MaxHardness = 1.0f;          // 最大硬度
    const float SaturationStrain = 0.5f;     // 饱和应变

    // Ludwik方程简化版:σ = σ₀ + k·εⁿ
    float HardnessIncrease = MaxHardness - InitialHardness;
    float StrainEffect = FMath::Pow(AccumulatedStrain / SaturationStrain, 0.5f);

    return InitialHardness + HardnessIncrease * FMath::Min(StrainEffect, 1.0f);
}

/**
 * 温度软化因子
 * 高温下硬度降低(模拟动态回复)
 */
float CalculateThermalSoftening(float Temperature, float Hardness)
{
    // 再结晶温度(简化)
    const float RecrystallizationTemp = 600.0f;  // °C

    if (Temperature < RecrystallizationTemp)
        return Hardness;  // 低温:无软化

    // 高温:软化
    float Overheating = Temperature - RecrystallizationTemp;
    float SofteningFactor = FMath::Clamp(Overheating / 400.0f, 0.0f, 0.8f);

    return Hardness * (1.0f - SofteningFactor);
}

3.3 实现代码

/**
 * 锤击时更新硬度和应变
 */
void ADynamicIronBlock::OnHammerImpact(
    FVector HitLocation,
    float ImpactRadius,
    float ImpactForce)
{
    FVector LocalHitPos = GetTransform().InverseTransformPosition(HitLocation);

    DynamicMesh->EditMesh([&](FDynamicMesh3& Mesh)
    {
        for (int32 VID : Mesh.VertexIndicesItr())
        {
            FVector VertexPos = Mesh.GetVertex(VID);
            float Distance = FVector::Dist(VertexPos, LocalHitPos);

            if (Distance < ImpactRadius)
            {
                // 1. 计算局部形变量(简化)
                float DistanceRatio = Distance / ImpactRadius;
                float LocalDeformation = FMath::Pow(1.0f - DistanceRatio, 2.0f);
                float StrainIncrease = LocalDeformation * ImpactForce * 0.0001f;

                // 2. 累积应变
                float CurrentStrain = AccumulatedStrain->GetValue(VID);
                float NewStrain = FMath::Min(1.0f, CurrentStrain + StrainIncrease);
                AccumulatedStrain->SetValue(VID, NewStrain);

                // 3. 从温度体素采样温度
                FVector VoxelPos = VertexPos / VoxelSize;
                float Temperature = SampleTemperatureFromVoxel(VoxelPos);

                // 4. 计算硬度(应变硬化)
                float StrainHardness = CalculateHardnessFromStrain(NewStrain);

                // 5. 温度软化
                float FinalHardness = CalculateThermalSoftening(Temperature, StrainHardness);

                // 6. 更新硬度
                HardnessCoefficient->SetValue(VID, FinalHardness);

                // 7. 硬度影响形变阻力(可选:反馈到形变计算)
                // 下一锤击时,高硬度区域形变小
            }
        }
    });
}

4. 应力分布模型

4.1 物理原理

应力来源:

  1. 不均匀形变: 相邻区域形变量不同 → 内部应力
  2. 热应力: 温度梯度导致膨胀不均
  3. 相变应力: 奥氏体→马氏体体积膨胀4%

应力后果:

  • 弹性回弹: 形变后部分恢复
  • 扭曲: 不对称应力导致变形
  • 开裂: 应力超过材料强度

4.2 简化应力计算

/**
 * 计算顶点应力(简化模型)
 * 基于局部应变梯度
 */
float CalculateVertexStress(
    const FDynamicMesh3& Mesh,
    int32 VID,
    const TDynamicMeshVertexAttribute<float>& StrainAttr)
{
    float CenterStrain = StrainAttr.GetValue(VID);

    // 获取邻居顶点
    TArray<int32> Neighbors;
    for (int32 NbrVID : Mesh.VtxVerticesItr(VID))
    {
        Neighbors.Add(NbrVID);
    }

    if (Neighbors.Num() == 0)
        return 0.0f;

    // 计算应变方差(梯度度量)
    float StrainSum = 0.0f;
    float StrainSumSq = 0.0f;

    for (int32 NbrVID : Neighbors)
    {
        float NbrStrain = StrainAttr.GetValue(NbrVID);
        StrainSum += NbrStrain;
        StrainSumSq += NbrStrain * NbrStrain;
    }

    float MeanStrain = StrainSum / Neighbors.Num();
    float MeanStrainSq = StrainSumSq / Neighbors.Num();
    float Variance = MeanStrainSq - MeanStrain * MeanStrain;

    // 应力 = 应变梯度(简化)
    float Stress = FMath::Sqrt(FMath::Max(Variance, 0.0f));

    return Stress;
}

/**
 * 更新所有顶点的应力
 */
void ADynamicIronBlock::UpdateStressDistribution()
{
    if (!DynamicMesh->HasMesh())
        return;

    DynamicMesh->EditMesh([&](FDynamicMesh3& Mesh)
    {
        for (int32 VID : Mesh.VertexIndicesItr())
        {
            float Stress = CalculateVertexStress(Mesh, VID, *AccumulatedStrain);

            // 应力松弛(模拟时效)
            float CurrentStress = StressValue->GetValue(VID);
            float RelaxationRate = 0.01f;  // 每帧松弛1%
            float NewStress = FMath::Lerp(CurrentStress, Stress, RelaxationRate);

            StressValue->SetValue(VID, NewStress);
        }
    });
}

4.3 应力导致开裂(可视化)

/**
 * 检测高应力区域并标记裂纹
 */
void ADynamicIronBlock::DetectCracks()
{
    const float StressThreshold = 0.8f;  // 应力阈值

    DynamicMesh->EditMesh([&](FDynamicMesh3& Mesh)
    {
        for (int32 TID : Mesh.TriangleIndicesItr())
        {
            FIndex3i Tri = Mesh.GetTriangle(TID);

            // 计算三角形平均应力
            float AvgStress = (
                StressValue->GetValue(Tri.A) +
                StressValue->GetValue(Tri.B) +
                StressValue->GetValue(Tri.C)
            ) / 3.0f;

            // 应力过高 → 标记为裂纹(材质中显示)
            if (AvgStress > StressThreshold)
            {
                // 方法1: 修改顶点颜色
                FColor CrackColor = FColor::Black;
                Mesh.SetVertexColor(Tri.A, CrackColor);
                Mesh.SetVertexColor(Tri.B, CrackColor);
                Mesh.SetVertexColor(Tri.C, CrackColor);

                // 方法2: 拓扑分裂(极端情况)
                if (AvgStress > 0.95f)
                {
                    SplitTriangleAlongCrack(Mesh, TID);
                }
            }
        }
    });
}

5. 淬火硬化系统

5.1 物理原理

淬火(Quenching):

  • 过程: 高温奥氏体(>800°C)快速冷却(水/油)
  • 结果: 形成马氏体,硬度剧增
  • 临界: 冷却速率 > 临界冷却速率(约100°C/s)

5.2 淬火判断逻辑

/**
 * 判断是否发生淬火
 */
bool ShouldTriggerQuenching(
    float CurrentTemperature,
    float PreviousTemperature,
    float DeltaTime)
{
    // 计算冷却速率
    float CoolingRate = (PreviousTemperature - CurrentTemperature) / DeltaTime;

    // 淬火条件:
    // 1. 当前温度在奥氏体区间
    // 2. 冷却速率超过临界值
    const float CriticalCoolingRate = 100.0f;  // °C/s
    const float AusteniteMin = 800.0f;
    const float AusteniteMax = 1200.0f;

    if (CurrentTemperature >= AusteniteMin && CurrentTemperature <= AusteniteMax)
    {
        if (CoolingRate > CriticalCoolingRate)
        {
            return true;
        }
    }

    return false;
}

/**
 * 更新体素相态
 */
void ADynamicIronBlock::UpdateVoxelPhaseState(float DeltaTime)
{
    // 在GPU Compute Shader中实现(异步)
    // 这里展示CPU参考实现

    for (int32 Z = 0; Z < TemperatureResolution.Z; ++Z)
    {
        for (int32 Y = 0; Y < TemperatureResolution.Y; ++Y)
        {
            for (int32 X = 0; X < TemperatureResolution.X; ++X)
            {
                FIntVector Coord(X, Y, Z);
                int32 Index = GetTemperatureIndex(Coord);
                FTemperatureVoxel& Voxel = TemperatureGrid[Index];

                // 保存上一帧温度
                float PrevTemp = Voxel.Temperature;

                // 温度传导(已有逻辑)
                UpdateTemperatureVoxel(Coord, DeltaTime);

                // 判断淬火
                if (ShouldTriggerQuenching(Voxel.Temperature, PrevTemp, DeltaTime))
                {
                    // 奥氏体 → 马氏体相变
                    Voxel.PhaseState = EPhaseState::Martensite;
                    Voxel.bHasBeenQuenched = true;

                    // 硬度剧增
                    // (影响顶点硬度的采样)
                }
                // 缓冷:奥氏体 → 珠光体
                else if (Voxel.Temperature < 600.0f &&
                    Voxel.PhaseState == EPhaseState::Austenite)
                {
                    Voxel.PhaseState = EPhaseState::Pearlite;
                }
            }
        }
    }
}

5.3 相变硬度映射

/**
 * 相态→硬度映射
 */
float GetPhaseHardness(EPhaseState Phase)
{
    switch (Phase)
    {
        case EPhaseState::Martensite:
            return 1.0f;   // 马氏体:最硬
        case EPhaseState::Bainite:
            return 0.7f;   // 贝氏体:中硬
        case EPhaseState::Pearlite:
            return 0.4f;   // 珠光体:软
        case EPhaseState::Austenite:
        default:
            return 0.2f;   // 奥氏体:最软
    }
}

/**
 * 顶点从体素采样硬度
 */
float ADynamicIronBlock::SampleHardnessFromVoxel(FVector LocalPos)
{
    // 三线性插值采样温度体素
    FIntVector VoxelCoord = WorldToVoxelCoord(LocalPos);
    int32 Index = GetTemperatureIndex(VoxelCoord);

    if (Index == INDEX_NONE)
        return 0.2f;  // 默认硬度

    const FTemperatureVoxel& Voxel = TemperatureGrid[Index];

    // 基础硬度(来自相态)
    float PhaseHardness = GetPhaseHardness(Voxel.PhaseState);

    // 淬火过才有额外硬度加成
    if (Voxel.bHasBeenQuenched)
    {
        PhaseHardness += 0.2f;  // 淬火加成
    }

    return FMath::Clamp(PhaseHardness, 0.0f, 1.0f);
}

6. 晶粒演化模型

6.1 物理原理

晶粒尺寸影响材料特性:

  • 细晶粒: 高强度、高韧性(Hall-Petch关系)
  • 粗晶粒: 高温下更容易形变

晶粒演化因素:

  • 再结晶: 高温+应变 → 新晶粒形成
  • 晶粒长大: 高温下晶粒合并
  • 晶粒细化: 大应变+低温 → 细化

6.2 简化晶粒模型

/**
 * 晶粒尺寸演化(简化标量模型)
 */
float EvolveGrainSize(
    float CurrentGrainSize,    // 当前晶粒尺寸(归一化0-1)
    float Temperature,          // 当前温度(°C)
    float Strain,              // 累积应变
    float DeltaTime)           // 时间步长
{
    const float RecrystallizationTemp = 600.0f;
    const float GrainGrowthTemp = 900.0f;

    // 1. 再结晶:大应变+高温 → 晶粒细化
    if (Temperature > RecrystallizationTemp && Strain > 0.3f)
    {
        float RecrystallizationRate = 0.1f;  // 可调
        return CurrentGrainSize * (1.0f - RecrystallizationRate * DeltaTime);
    }

    // 2. 晶粒长大:高温 → 晶粒粗化
    if (Temperature > GrainGrowthTemp)
    {
        float GrowthRate = 0.05f * (Temperature - GrainGrowthTemp) / 300.0f;
        return FMath::Min(1.0f, CurrentGrainSize * (1.0f + GrowthRate * DeltaTime));
    }

    // 3. 晶粒细化:大应变 → 晶粒细化
    if (Strain > 0.5f)
    {
        float RefinementRate = 0.02f;
        return FMath::Max(0.1f, CurrentGrainSize * (1.0f - RefinementRate * DeltaTime));
    }

    return CurrentGrainSize;
}

/**
 * 更新顶点晶粒尺寸
 */
void ADynamicIronBlock::UpdateGrainStructure(float DeltaTime)
{
    DynamicMesh->EditMesh([&](FDynamicMesh3& Mesh)
    {
        for (int32 VID : Mesh.VertexIndicesItr())
        {
            float CurrentGrainSize = GrainSize->GetValue(VID);
            float Strain = AccumulatedStrain->GetValue(VID);

            // 采样温度
            FVector Pos = Mesh.GetVertex(VID);
            float Temperature = SampleTemperatureFromVoxel(Pos);

            // 演化晶粒尺寸
            float NewGrainSize = EvolveGrainSize(CurrentGrainSize, Temperature, Strain, DeltaTime);

            GrainSize->SetValue(VID, NewGrainSize);
        }
    });
}

6.3 大马士革花纹模拟

/**
 * 初始化层状材料(用于大马士革钢)
 */
void InitializeLayeredMaterialStructure()
{
    DynamicMesh->EditMesh([&](FDynamicMesh3& Mesh)
    {
        TDynamicMeshVertexAttribute<int32>* MaterialLayerID =
            new TDynamicMeshVertexAttribute<int32>(&Mesh);

        for (int32 VID : Mesh.VertexIndicesItr())
        {
            FVector Pos = Mesh.GetVertex(VID);

            // 简单层状分布:沿Z轴
            // 每层厚度2mm
            int32 LayerID = (int32)FMath::Floor(Pos.Z / 2.0f) % 2;

            MaterialLayerID->SetValue(VID, LayerID);
        }
    });
}

/**
 * 渲染大马士革花纹
 * 材质中基于MaterialLayerID采样不同颜色
 */
// 材质实现:
/*
float LayerID = MaterialLayerAttribute;  // 从顶点属性读取

float3 HighCarbonColor = float3(0.15, 0.1, 0.08);   // 深色层
float3 LowCarbonColor = float3(0.75, 0.7, 0.65);    // 浅色层

float3 BaseColor = lerp(HighCarbonColor, LowCarbonColor, LayerID);

// 添加扭曲(基于形变)
float Distortion = length(AccumulatedStrain) * 5.0;
float3 WarpedPattern = WarpedNoise(BaseColor, Distortion);

FinalColor = BaseColor * (0.8 + WarpedPattern * 0.2);
*/

7. GPU Compute Shader集成

7.1 Shader结构

MiceShaders/Source/MiceShaders/Private/
├── MetallurgyComputeShader.usf
├── MetallurgyComputeShader.h
└── MetallurgyComputeShader.cpp

7.2 HLSL Shader

// MetallurgyComputeShader.usf

#define VOXEL_SIZE 50
#define THREAD_COUNT 32

// 输入/输出缓冲区
RWStructuredBuffer<float4> TemperatureBufferA;      // 温度 + 塑性系数
RWStructuredBuffer<float4> TemperatureBufferB;      // Ping-Pong

RWStructuredBuffer<float4> MetallurgyBuffer;        // 应变率 + 冷却速率 + 相态 + 淬火标记

// 参数
cbuffer MetallurgyParams
{
    float DeltaTime;
    float3 VoxelWorldMin;
    float VoxelSize;
    float3 Padding;
};

/**
 * 计算 Metallurgy Shader 主函数
 */
[numthreads(THREAD_COUNT, THREAD_COUNT, 1)]
void MetallurgyComputeShader(uint3 ThreadId : SV_DispatchThreadID)
{
    uint VoxelIndex = ThreadId.x + ThreadId.y * VoxelSize;

    // 读取温度
    float Temperature = TemperatureBufferA[VoxelIndex].r;
    float Plasticity = TemperatureBufferA[VoxelIndex].g;

    // 读取应变率
    float StrainRate = MetallurgyBuffer[VoxelIndex].r;

    // 1. 应变率衰减(模拟时效)
    StrainRate *= 0.99;

    // 2. 计算冷却速率
    float3 WorldPos = VoxelToWorldPosition(VoxelIndex);
    float SurfaceFactor = IsSurfaceVoxel(WorldPos) ? 1.0 : 0.1;
    float CoolingRate = SurfaceFactor * (Temperature - 20.0) * 0.1;

    // 3. 相态判断
    uint PhaseState = 0;  // 奥氏体
    if (CoolingRate > 100.0 && Temperature > 800.0)
    {
        PhaseState = 1;  // 马氏体(淬火)
    }
    else if (Temperature < 600.0)
    {
        PhaseState = 2;  // 珠光体(缓冷)
    }

    // 写回
    MetallurgyBuffer[VoxelIndex] = float4(StrainRate, CoolingRate, PhaseState, 0.0);
}

/**
 * 辅助函数:体素索引转世界坐标
 */
float3 VoxelToWorldPosition(uint Index)
{
    uint X = Index % VoxelSize;
    uint Y = (Index / VoxelSize) % VoxelSize;
    uint Z = Index / (VoxelSize * VoxelSize);

    return VoxelWorldMin + float3(X, Y, Z) * VoxelSize;
}

/**
 * 辅助函数:判断是否为表面体素
 */
bool IsSurfaceVoxel(float3 WorldPos)
{
    // 简化:边界体素为表面
    // 实际应使用法线判断
    return false;
}

7.3 C++调用

// MetallurgyComputeShader.h
class FMetallurgyComputeShader
{
public:
    static void Dispatch(
        FRHICommandListImmediate& RHICmdList,
        FStructuredBufferRHIRef TemperatureBufferA,
        FStructuredBufferRHIRef TemperatureBufferB,
        FStructuredBufferRHIRef MetallurgyBuffer,
        float DeltaTime,
        FIntVector VoxelResolution);
};

// MetallurgyComputeShader.cpp
void FMetallurgyComputeShader::Dispatch(
    FRHICommandListImmediate& RHICmdList,
    FStructuredBufferRHIRef TemperatureBufferA,
    FStructuredBufferRHIRef TemperatureBufferB,
    FStructuredBufferRHIRef MetallurgyBuffer,
    float DeltaTime,
    FIntVector VoxelResolution)
{
    // 设置Shader
    TShaderMapRef<FMetallurgyComputeShader> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));

    // 设置参数
    RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
    ComputeShader->SetParameters(RHICmdList, TemperatureBufferA, TemperatureBufferB,
        MetallurgyBuffer, DeltaTime, VoxelMin, VoxelSize);

    // Dispatch
    int32 ThreadGroupCount = (VoxelResolution.X + 31) / 32;
    RHICmdList.DispatchComputeShader(ThreadGroupCount, ThreadGroupCount, 1);
}

8. 材质可视化

8.1 硬度可视化

// 材质中:基于硬度调整颜色
float Hardness = SampleHardnessFromVoxel(WorldPos);

float3 SoftColor = float3(0.6, 0.5, 0.4);   // 软:棕色
float3 HardColor = float3(0.8, 0.8, 0.85);  // 硬:银白色

float3 HardnessColor = lerp(SoftColor, HardColor, Hardness);

8.2 应力可视化

// 顶点颜色.A通道存储应力
float Stress = VertexColor.a;

float3 LowStressColor = float3(0.3, 0.5, 0.7);   // 低应力:蓝色
float3 HighStressColor = float3(1.0, 0.3, 0.1);  // 高应力:红色

float3 StressColor = lerp(LowStressColor, HighStressColor, Stress);

// 混合到基础颜色
BaseColor = lerp(BaseColor, StressColor, Stress * 0.5);

8.3 相态可视化

// 从体素采样相态
float PhaseState = SamplePhaseStateFromVoxel(WorldPos);

float3 AusteniteColor = float3(1.0, 0.8, 0.6);    // 奥氏体:浅橙色
float3 MartensiteColor = float3(0.9, 0.85, 0.8);  // 马氏体:银白色
float3 PearliteColor = float3(0.7, 0.6, 0.5);     // 珠光体:深灰色

float3 PhaseColor;
if (PhaseState < 0.5)
    PhaseColor = AusteniteColor;
else if (PhaseState < 1.5)
    PhaseColor = MartensiteColor;
else
    PhaseColor = PearliteColor;

// 根据温度过渡
float Temp = SampleTemperatureFromVoxel(WorldPos);
float MixFactor = smoothstep(600.0, 800.0, Temp);

BaseColor = lerp(PhaseColor, AusteniteColor, MixFactor);

9. 性能优化

9.1 GPU优化策略

// 1. 使用共享内存(GroupShared)
groupshared float SharedTemperature[THREAD_COUNT * THREAD_COUNT];

// 2. 波前优化(Wave Intrinsics)
uint WaveSize = GetWaveSize();
float WaveSum = WaveActiveSum(StrainRate);

// 3. 避免分支 divergence
if (CoolingRate > 100.0)  // 差:条件跳转
    PhaseState = 1.0;
PhaseState = step(100.0, CoolingRate);  // 好:无分支

9.2 CPU-GPU异步

// 异步Dispatch
ENQUEUE_RENDER_COMMAND(FMetallurgyComputeDispatch)(
    [this, DeltaTime](FRHICommandListImmediate& RHICmdList)
    {
        FMetallurgyComputeShader::Dispatch(
            RHICmdList,
            TemperatureBufferA,
            TemperatureBufferB,
            MetallurgyBuffer,
            DeltaTime,
            TemperatureResolution
        );

        // 标记完成
        bMetallurgyComputeComplete = true;
    }
);

// CPU继续其他工作,不用等待GPU

9.3 LOD策略

// 根据摄像机距离调整计算频率
float DistanceToCamera = FVector::Dist(GetActorLocation(), CameraLocation);

int32 ComputeFrequency;
if (DistanceToCamera < 200.0f)
    ComputeFrequency = 1;   // 每帧计算
else if (DistanceToCamera < 500.0f)
    ComputeFrequency = 2;   // 每2帧计算
else
    ComputeFrequency = 5;   // 每5帧计算

static int32 FrameCounter = 0;
if (++FrameCounter % ComputeFrequency == 0)
{
    DispatchMetallurgyComputeShader();
}

10. 参数配置示例

10.1 碳钢(C105)

// 加工硬化参数
InitialHardness = 0.25f;
SaturationStrain = 0.6f;
WorkHardeningExponent = 0.55f;

// 相变参数
AustenitizingTemp = 800.0f;
QuenchingCriticalRate = 120.0f;
MartensiteHardness = 0.95f;

// 晶粒参数
InitialGrainSize = 0.5f;
RecrystallizationTemp = 650.0f;

10.2 高碳钢(C150)

// 更容易硬化
InitialHardness = 0.35f;
SaturationStrain = 0.4f;

// 淬火更敏感
QuenchingCriticalRate = 80.0f;  // 更容易淬火
MartensiteHardness = 1.0f;       // 更硬

10.3 低碳钢(C20)

// 难硬化
InitialHardness = 0.15f;
SaturationStrain = 0.8f;

// 难淬火
QuenchingCriticalRate = 200.0f;  // 很难淬火
MartensiteHardness = 0.7f;

11. 调试工具

11.1 体素数据可视化

void ADynamicIronBlock::DebugDrawVoxelData()
{
    for (int32 Z = 0; Z < TemperatureResolution.Z; ++Z)
    {
        for (int32 Y = 0; Y < TemperatureResolution.Y; ++Y)
        {
            for (int32 X = 0; X < TemperatureResolution.X; ++X)
            {
                FIntVector Coord(X, Y, Z);
                int32 Index = GetTemperatureIndex(Coord);
                const FTemperatureVoxel& Voxel = TemperatureGrid[Index];

                // 只绘制表面体素
                if (!(Voxel.Flags & VOXEL_FLAG_SURFACE))
                    continue;

                FVector WorldPos = VoxelCoordToWorldPos(Coord);

                // 颜色:温度
                FLinearColor Color = FLinearColor(
                    FMath::Clamp(Voxel.Temperature / 1200.0f, 0.0f, 1.0f),
                    0.0f,
                    1.0f - FMath::Clamp(Voxel.Temperature / 1200.0f, 0.0f, 1.0f)
                );

                DrawDebugPoint(GetWorld(), WorldPos, 3.0f, Color.ToFColor(), false, 0.1f);
            }
        }
    }
}

11.2 应力热图

void ADynamicIronBlock::DebugDrawStressHeatmap()
{
    if (!DynamicMesh->HasMesh())
        return;

    for (int32 VID : DynamicMesh->VertexIndicesItr())
    {
        if (VID % 50 != 0)  // 稀疏采样
            continue;

        float Stress = StressValue->GetValue(VID);
        FVector WorldPos = GetActorTransform().TransformPosition(DynamicMesh->GetVertex(VID));

        // 蓝色(低应力)→ 红色(高应力)
        FColor HeatColor = FLinearColor::LerpUsingHSV(
            FColor::Blue,
            FColor::Red,
            Stress
        ).ToFColor();

        DrawDebugPoint(GetWorld(), WorldPos, 5.0f, HeatColor, false, 5.0f);
    }
}

12. 已知限制

  1. 简化模型: 所有物理模型均为经验公式,非严格物理计算
  2. 无热-力耦合: 温度场和应力场独立计算,未考虑形变生热
  3. 无磁-电效应: 忽略了电磁场对相变的影响
  4. 各向同性: 假设材料各向同性,真实钢有晶粒取向

适用场景: 游戏娱乐级,足够真实感但非科研级精度。