docs: 添加几何侧技术设计(刀刃形成/表面磨损/网格质量)
三个对应几何与外观的技术设计。design/01 已包含评估增补(参数语义统一、双区实现回填、流水线协作、形变连续性、碰撞渲染同步、体积守恒策略、性能预算协调),修复了 InverseTransformPosition 法线转换 bug。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,635 @@
|
||||
# 表面磨损-顶点属性与无漂移采样设计
|
||||
|
||||
**项目**: BladeSmith
|
||||
**文档类型**: 技术设计(专题)
|
||||
**版本**: 2.0
|
||||
**最后更新**: 2026-08-11
|
||||
**状态**: 整合自原 `2026-01-22-表面磨损系统细节.md`
|
||||
|
||||
> 本文档描述表面磨损系统的顶点属性存储、无漂移纹理采样与材质实现。需求见 [requirements/02-表面磨损与外观需求.md](../requirements/02-表面磨损与外观需求.md)。总体架构见 [00-锻造系统总体技术架构.md](00-锻造系统总体技术架构.md)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 技术背景
|
||||
|
||||
铁锈层厚度0.1-2mm,成分为Fe₂O₃/Fe₃O₄,附着强度低易被机械摩擦去除。锤击压力导致摩擦力使铁锈层破碎脱落,塑性形变暴露新鲜金属表面,高温使部分氧化层还原。表面特征必须随形变一起移动,不能漂移。
|
||||
|
||||
---
|
||||
|
||||
## 2. 技术方案选择
|
||||
|
||||
### 2.1 方案对比
|
||||
|
||||
| 方案 | 分辨率 | 内存 | 性能 | 漂移问题 | 推荐度 |
|
||||
|------|--------|------|------|---------|--------|
|
||||
| **基于顶点** | ★★★☆☆ | ★★★★★ | ★★★★★ | ✅无 | ★★★★★ |
|
||||
| **基于纹理** | ★★★★★ | ★★☆☆☆ | ★★★☆☆ | ⚠️有扭曲 | ★★★☆☆ |
|
||||
| **基于体素** | ★★☆☆☆ | ★★★☆☆ | ★★★★☆ | ✅无 | ★★★☆☆ |
|
||||
|
||||
**选择**: 基于顶点属性(方案A) + 程序化细节补充
|
||||
|
||||
### 2.2 数据通道分配
|
||||
|
||||
顶点颜色(RGBA) + UV1通道的完整编码方案:
|
||||
|
||||
| 通道 | 用途 | 范围 | 更新频率 | 说明 |
|
||||
|------|------|------|---------|------|
|
||||
| **R** | 温度可视化 | 0-1 | 每帧 | 从体素采样,黑体辐射颜色 |
|
||||
| **G** | 打磨度(Polish) | 0-1 | 锤击时 | 0=完全生锈,1=完全光亮 |
|
||||
| **B** | 噪声种子(Seed) | 0-1 | 初始化 | 用于程序化噪声,固定不变 |
|
||||
| **A** | 硬度/应变 | 0-1 | 锤击时 | 可视化累积应变 |
|
||||
| **UV1.x** | 初始位置X | 0-1 | 初始化 | 编码3D位置的一部分 |
|
||||
| **UV1.y** | 初始位置Y | 0-1 | 初始化 | 编码3D位置的一部分 |
|
||||
|
||||
额外数据通过自定义顶点属性存储。
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据结构定义
|
||||
|
||||
### 3.1 扩展顶点属性
|
||||
|
||||
```cpp
|
||||
class ADynamicIronBlock
|
||||
{
|
||||
private:
|
||||
// ===== 顶点自定义属性层 =====
|
||||
TDynamicMeshVertexAttribute<FVector>* InitialPositions; // 初始局部位置
|
||||
TDynamicMeshVertexAttribute<float>* PolishAmount; // 打磨度(0-1)
|
||||
TDynamicMeshVertexAttribute<float>* RustSeed; // 铁锈噪声种子
|
||||
TDynamicMeshVertexAttribute<float>* OxideThickness; // 氧化层厚度
|
||||
|
||||
// ===== 材质参数 =====
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface")
|
||||
float RustRoughness = 0.9f; // 铁锈粗糙度
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface")
|
||||
float MetalRoughness = 0.2f; // 金属粗糙度
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface")
|
||||
FLinearColor RustBaseColor = FLinearColor(0.6f, 0.3f, 0.1f); // 铁锈基色
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface")
|
||||
FLinearColor MetalBaseColor = FLinearColor(0.8f, 0.8f, 0.85f); // 金属基色
|
||||
|
||||
public:
|
||||
// 初始化顶点属性
|
||||
void InitializeVertexAttributes();
|
||||
};
|
||||
```
|
||||
|
||||
### 3.2 初始化逻辑
|
||||
|
||||
```cpp
|
||||
void ADynamicIronBlock::InitializeVertexAttributes()
|
||||
{
|
||||
if (!DynamicMesh->HasMesh())
|
||||
return;
|
||||
|
||||
DynamicMesh->EditMesh([&](FDynamicMesh3& Mesh)
|
||||
{
|
||||
// 确保属性系统已启用
|
||||
if (!Mesh.HasAttributes())
|
||||
{
|
||||
Mesh.EnableAttributes();
|
||||
}
|
||||
|
||||
// 创建自定义属性层
|
||||
InitialPositions = new TDynamicMeshVertexAttribute<FVector>(&Mesh);
|
||||
InitialPositions->Initialize(FVector::ZeroVector);
|
||||
|
||||
PolishAmount = new TDynamicMeshVertexAttribute<float>(&Mesh);
|
||||
PolishAmount->Initialize(0.0f);
|
||||
|
||||
RustSeed = new TDynamicMeshVertexAttribute<float>(&Mesh);
|
||||
RustSeed->Initialize(0.0f);
|
||||
|
||||
OxideThickness = new TDynamicMeshVertexAttribute<float>(&Mesh);
|
||||
OxideThickness->Initialize(1.0f); // 初始完全氧化
|
||||
|
||||
// 获取或创建UV1层
|
||||
FDynamicMeshUVOverlay* UV1 = Mesh.Attributes()->GetUVLayer(1);
|
||||
if (!UV1)
|
||||
{
|
||||
Mesh.Attributes()->SetNumUVLayers(2);
|
||||
UV1 = Mesh.Attributes()->GetUVLayer(1);
|
||||
}
|
||||
|
||||
// 计算包围盒(用于位置编码)
|
||||
FAABB3 Box = Mesh.GetBounds();
|
||||
FVector BoxMin = Box.Min;
|
||||
FVector BoxMax = Box.Max;
|
||||
FVector BoxSize = Box.Max - Box.Min;
|
||||
|
||||
// 初始化每个顶点
|
||||
for (int32 VID : Mesh.VertexIndicesItr())
|
||||
{
|
||||
FVector LocalPos = Mesh.GetVertex(VID);
|
||||
|
||||
// 1. 保存初始位置(不变)
|
||||
InitialPositions->SetValue(VID, LocalPos);
|
||||
|
||||
// 2. 初始化打磨度为0(完全生锈)
|
||||
PolishAmount->SetValue(VID, 0.0f);
|
||||
|
||||
// 3. 分配随机铁锈种子
|
||||
RustSeed->SetValue(VID, FMath::FRand());
|
||||
|
||||
// 4. 编码初始位置到UV1(仅X和Y)
|
||||
FVector2f EncodedPos;
|
||||
EncodedPos.X = (LocalPos.X - BoxMin.X) / (BoxSize.X + 1e-6f);
|
||||
EncodedPos.Y = (LocalPos.Y - BoxMin.Y) / (BoxSize.Y + 1e-6f);
|
||||
UV1->SetElement(VID, EncodedPos);
|
||||
|
||||
// 5. 编码初始位置Z到顶点颜色.B通道(替换随机种子方案)
|
||||
FColor VertexColor = Mesh.GetVertexColor(VID);
|
||||
FLinearColor LinearColor(VertexColor);
|
||||
LinearColor.B = (LocalPos.Z - BoxMin.Z) / (BoxSize.Z + 1e-6f); // Z坐标归一化
|
||||
LinearColor.G = 0.0f; // 初始打磨度
|
||||
Mesh.SetVertexColor(VID, LinearColor.ToFColor());
|
||||
}
|
||||
});
|
||||
|
||||
// 更新材质参数
|
||||
if (DynamicMaterialInstance)
|
||||
{
|
||||
DynamicMaterialInstance->SetScalarParameterValue(TEXT("RustRoughness"), RustRoughness);
|
||||
DynamicMaterialInstance->SetScalarParameterValue(TEXT("MetalRoughness"), MetalRoughness);
|
||||
DynamicMaterialInstance->SetVectorParameterValue(TEXT("RustColor"), RustBaseColor);
|
||||
DynamicMaterialInstance->SetVectorParameterValue(TEXT("MetalColor"), MetalBaseColor);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 锤击更新逻辑
|
||||
|
||||
### 4.1 打磨度更新
|
||||
|
||||
```cpp
|
||||
void ADynamicIronBlock::OnHammerImpact(
|
||||
FVector HitLocation,
|
||||
FVector HitNormal,
|
||||
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)
|
||||
{
|
||||
// 计算打磨强度
|
||||
float DistanceRatio = Distance / ImpactRadius;
|
||||
float FalloffExponent = 2.0f;
|
||||
float Intensity = FMath::Pow(1.0f - DistanceRatio, FalloffExponent);
|
||||
|
||||
// 打磨强度与力度成正比
|
||||
float PolishIncrease = Intensity * ImpactForce * 0.001f; // 可调系数
|
||||
|
||||
// 累积打磨度(不可逆:只能增加,不能减少)
|
||||
float CurrentPolish = PolishAmount->GetValue(VID);
|
||||
float NewPolish = FMath::Min(1.0f, CurrentPolish + PolishIncrease);
|
||||
|
||||
PolishAmount->SetValue(VID, NewPolish);
|
||||
|
||||
// 同步到顶点颜色.G通道
|
||||
FColor VertexColor = Mesh.GetVertexColor(VID);
|
||||
FLinearColor LinearColor(VertexColor);
|
||||
LinearColor.G = NewPolish;
|
||||
Mesh.SetVertexColor(VID, LinearColor.ToFColor());
|
||||
|
||||
// 铁锈种子(顶点颜色.B)保持不变
|
||||
// 初始位置Z(已在.B中)保持不变
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 氧化层更新(可选)
|
||||
|
||||
```cpp
|
||||
void ADynamicIronBlock::UpdateOxideLayer(float DeltaTime)
|
||||
{
|
||||
if (!DynamicMesh->HasMesh())
|
||||
return;
|
||||
|
||||
// 简化的氧化模型:
|
||||
// - 高温(>300°C)时氧化加快
|
||||
// - 打磨区域氧化较慢(新鲜表面)
|
||||
|
||||
DynamicMesh->EditMesh([&](FDynamicMesh3& Mesh)
|
||||
{
|
||||
for (int32 VID : Mesh.VertexIndicesItr())
|
||||
{
|
||||
float Temperature = SampleTemperatureAtVertex(Mesh, VID);
|
||||
float Polish = PolishAmount->GetValue(VID);
|
||||
|
||||
// 氧化速率
|
||||
float BaseOxidationRate = 0.01f; // 基础速率
|
||||
float TemperatureFactor = FMath::Clamp((Temperature - 300.0f) / 1000.0f, 0.0f, 1.0f);
|
||||
float PolishProtection = 1.0f - Polish * 0.8f; // 打磨后氧化慢
|
||||
|
||||
float OxidationRate = BaseOxidationRate * (1.0f + TemperatureFactor * 10.0f) * PolishProtection;
|
||||
|
||||
// 更新氧化层厚度
|
||||
float CurrentOxide = OxideThickness->GetValue(VID);
|
||||
float NewOxide = FMath::Min(1.0f, CurrentOxide + OxidationRate * DeltaTime);
|
||||
OxideThickness->SetValue(VID, NewOxide);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 材质实现
|
||||
|
||||
### 5.1 材质图表结构
|
||||
|
||||
```
|
||||
[VertexColor] ────────┐
|
||||
├─> [Polish = VertexColor.g]
|
||||
[TexCoord1] ─────────┘
|
||||
|
||||
[Polish] ──> [Lerp]
|
||||
├─> [RustColor] (Polish=0)
|
||||
└─> [MetalColor] (Polish=1)
|
||||
|
||||
[Polish] ──> [Lerp]
|
||||
├─> [RustRoughness] (0.9)
|
||||
└─> [MetalRoughness] (0.2)
|
||||
|
||||
[Polish] ──> [Lerp]
|
||||
├─> [RustMetallic] (0.0)
|
||||
└─> [MetalMetallic] (1.0)
|
||||
|
||||
[TextureCoordinate1] ──> [重建初始位置]
|
||||
──> [噪声采样]
|
||||
──> [铁锈图案]
|
||||
```
|
||||
|
||||
### 5.2 HLSL实现(MF_SurfaceWear材质函数)
|
||||
|
||||
```hlsl
|
||||
// 输入参数
|
||||
float3 WorldPosition; // GetWorldPosition()
|
||||
float3 WorldNormal; // GetWorldNormal()
|
||||
float4 VertexColor; // GetVertexColor()
|
||||
float2 TexCoord1; // GetTexCoord(1)
|
||||
|
||||
// 材质参数(从材质实例传入)
|
||||
float3 RustColor; // 铁锈颜色
|
||||
float3 MetalColor; // 金属颜色
|
||||
float RustRoughness; // 铁锈粗糙度
|
||||
float MetalRoughness; // 金属粗糙度
|
||||
float RustMetallic; // 铁锈金属度(通常0)
|
||||
float MetalMetallic; // 金属金属度(通常1)
|
||||
|
||||
// ===== 主函数 =====
|
||||
|
||||
// 1. 读取打磨度
|
||||
float Polish = VertexColor.g; // 来自G通道
|
||||
|
||||
// 2. 重建初始位置(用于无漂移噪声采样)
|
||||
// 假设包围盒是 [-50, 50] cm
|
||||
float3 BoxMin = float3(-50.0, -50.0, -50.0);
|
||||
float3 BoxMax = float3(50.0, 50.0, 50.0);
|
||||
float3 BoxSize = BoxMax - BoxMin;
|
||||
|
||||
float InitialPosX = TexCoord1.x * BoxSize.x + BoxMin.x;
|
||||
float InitialPosY = TexCoord1.y * BoxSize.y + BoxMin.y;
|
||||
float InitialPosZ = VertexColor.b * BoxSize.z + BoxMin.z;
|
||||
|
||||
float3 InitialLocalPos = float3(InitialPosX, InitialPosY, InitialPosZ);
|
||||
|
||||
// 3. 基于初始位置采样噪声(空间连续,无漂移)
|
||||
float RustSeedNoise = frac(InitialLocalPos.x * 0.1 + InitialLocalPos.y * 0.1 + InitialLocalPos.z * 0.1);
|
||||
float RustMacro = PseudoRandom(RustSeedNoise * 100.0); // 宏观铁锈斑块
|
||||
float RustDetail = Noise(InitialLocalPos * 20.0); // 细节纹理
|
||||
float RustMicro = Noise(InitialLocalPos * 100.0); // 微观细节
|
||||
|
||||
// 组合多层噪声
|
||||
float RustPattern = RustMacro * 0.6 + RustDetail * 0.3 + RustMicro * 0.1;
|
||||
|
||||
// 4. 计算最终颜色、金属度、粗糙度
|
||||
float3 FinalRustColor = RustColor * (0.8 + RustPattern * 0.4); // 铁锈颜色变化
|
||||
float3 FinalBaseColor = lerp(FinalRustColor, MetalColor, Polish);
|
||||
|
||||
float FinalMetallic = lerp(RustMetallic, MetalMetallic, Polish);
|
||||
float FinalRoughness = lerp(RustRoughness, MetalRoughness, Polish);
|
||||
|
||||
// 5. 可选:添加划痕细节(基于初始位置的高频噪声)
|
||||
float ScratchNoise = Noise(InitialLocalPos * 200.0) * 0.1;
|
||||
FinalRoughness += ScratchNoise * (1.0 - Polish); // 划痕在未打磨区域更明显
|
||||
|
||||
// 6. 输出
|
||||
return float4(FinalBaseColor, FinalRoughness, FinalMetallic, 1.0);
|
||||
```
|
||||
|
||||
### 5.3 材质节点连接
|
||||
|
||||
```
|
||||
材质输出:
|
||||
Base Color ──> FinalBaseColor
|
||||
Roughness ──> FinalRoughness
|
||||
Metallic ──> FinalMetallic
|
||||
Normal ───> [默认法线]
|
||||
Ambient Occlusion ──> [可选]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 无漂移保证机制
|
||||
|
||||
### 6.1 问题示例
|
||||
|
||||
**错误做法(会漂移)**:
|
||||
```hlsl
|
||||
// 基于世界坐标采样噪声
|
||||
float3 WorldPos = GetWorldPosition();
|
||||
float Rust = Noise(WorldPos * 10.0);
|
||||
|
||||
// 问题:
|
||||
// 锻打前: 顶点A在(100, 50, 0),Noise → 0.3(铁锈)
|
||||
// 锻打后: 顶点A移动到(105, 52, 2),Noise → 0.8(变光亮?)
|
||||
// 铁锈"滑走"了!
|
||||
```
|
||||
|
||||
### 6.2 正确做法(无漂移)
|
||||
|
||||
**方案1:初始位置采样**
|
||||
```hlsl
|
||||
// 用初始的、不变的位置采样噪声
|
||||
float3 InitialLocalPos = ReconstructInitialPosition(TexCoord1, VertexColor.b);
|
||||
float Rust = Noise(InitialLocalPos * 10.0);
|
||||
|
||||
// 优点:
|
||||
// - 顶点移动后,InitialLocalPos不变
|
||||
// - 铁锈图案"锁定"在材料上
|
||||
// - 空间连续:相邻顶点采样相似的噪声值
|
||||
```
|
||||
|
||||
**方案2:种子采样**
|
||||
```hlsl
|
||||
// 用每个顶点的唯一种子采样
|
||||
float Seed = VertexColor.b; // 0-1范围
|
||||
float Rust = PseudoRandom(Seed * 1000.0);
|
||||
|
||||
// 优点:
|
||||
// - 完全无漂移
|
||||
// - 实现简单
|
||||
|
||||
// 缺点:
|
||||
// - 空间不连续:相邻顶点可能有跳变
|
||||
// - 需要高网格密度掩盖
|
||||
```
|
||||
|
||||
**推荐**:方案1(初始位置采样)
|
||||
|
||||
### 6.3 完整的无漂移采样函数
|
||||
|
||||
```hlsl
|
||||
/**
|
||||
* 从UV1和顶点颜色重建初始位置
|
||||
*/
|
||||
float3 ReconstructInitialPosition(float2 TexCoord1, float EncodedZ)
|
||||
{
|
||||
// 假设包围盒 [-50, 50] cm
|
||||
float3 BoxMin = float3(-50.0, -50.0, -50.0);
|
||||
float3 BoxSize = float3(100.0, 100.0, 100.0);
|
||||
|
||||
float InitialX = TexCoord1.x * BoxSize.x + BoxMin.x;
|
||||
float InitialY = TexCoord1.y * BoxSize.y + BoxMin.y;
|
||||
float InitialZ = EncodedZ * BoxSize.z + BoxMin.z;
|
||||
|
||||
return float3(InitialX, InitialY, InitialZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* 采样铁锈图案(无漂移版本)
|
||||
*/
|
||||
float3 SampleRustPattern(float2 TexCoord1, float EncodedZ, float3 WorldNormal)
|
||||
{
|
||||
// 重建初始位置
|
||||
float3 InitialLocalPos = ReconstructInitialPosition(TexCoord1, EncodedZ);
|
||||
|
||||
// 多层噪声
|
||||
float Rust1 = Noise(InitialLocalPos * 10.0 + float3(1.0, 2.0, 3.0)); // 低频
|
||||
float Rust2 = Noise(InitialLocalPos * 30.0 + float3(4.0, 5.0, 6.0)); // 中频
|
||||
float Rust3 = Noise(InitialLocalPos * 100.0 + float3(7.0, 8.0, 9.0)); // 高频
|
||||
|
||||
// 组合
|
||||
float RustPattern = Rust1 * 0.5 + Rust2 * 0.3 + Rust3 * 0.2;
|
||||
|
||||
// 基于法线的变化(侧面铁锈更多)
|
||||
float VerticalFactor = abs(WorldNormal.z); // z=0表示侧面
|
||||
RustPattern *= (0.7 + VerticalFactor * 0.3);
|
||||
|
||||
return RustPattern;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 高级特性
|
||||
|
||||
### 7.1 大马士革钢层
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* 标记大马士革层
|
||||
* 在初始化时分配"材料ID"
|
||||
*/
|
||||
void InitializeDamascusLayers(FDynamicMesh3& Mesh)
|
||||
{
|
||||
TDynamicMeshVertexAttribute<int32>* MaterialLayerID =
|
||||
new TDynamicMeshVertexAttribute<int32>(&Mesh);
|
||||
|
||||
for (int32 VID : Mesh.VertexIndicesItr())
|
||||
{
|
||||
FVector Pos = Mesh.GetVertex(VID);
|
||||
|
||||
// 简化的层状分布:基于Z坐标
|
||||
int32 LayerID = (int32)FMath::Floor(Pos.Z / 2.0f) % 2; // 每2cm一层
|
||||
|
||||
MaterialLayerID->SetValue(VID, LayerID);
|
||||
}
|
||||
}
|
||||
|
||||
// 材质中基于LayerID选择颜色
|
||||
float3 GetDamascusColor(float3 InitialLocalPos, float LayerID)
|
||||
{
|
||||
// 高碳钢层(深色)
|
||||
float3 HighCarbonColor = float3(0.15, 0.1, 0.08);
|
||||
|
||||
// 低碳钢层(浅色)
|
||||
float3 LowCarbonColor = float3(0.75, 0.7, 0.65);
|
||||
|
||||
// 混合过渡
|
||||
float Mix = SmoothLayerPattern(InitialLocalPos);
|
||||
return lerp(HighCarbonColor, LowCarbonColor, Mix);
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 划痕系统
|
||||
|
||||
```hlsl
|
||||
// 基于初始位置的各向异性噪声(模拟划痕)
|
||||
float3 InitialPos = ReconstructInitialPosition(TexCoord1, VertexColor.b);
|
||||
|
||||
// 方向性划痕(沿着锤击方向)
|
||||
float ScratchDirection = InitialPos.x * 50.0; // X方向划痕
|
||||
float ScratchPattern = Noise(float3(ScratchDirection, InitialPos.y * 20.0, 0.0));
|
||||
|
||||
float3 ScratchColor = lerp(float3(1.0, 1.0, 1.0), BaseColor, ScratchPattern);
|
||||
float ScratchRoughness = Roughness * (1.0 + ScratchPattern * 0.5);
|
||||
|
||||
// 仅在部分区域应用
|
||||
float ScratchMask = SmoothNoise(InitialPos * 15.0);
|
||||
FinalBaseColor = lerp(FinalBaseColor, ScratchColor, ScratchMask * 0.3);
|
||||
```
|
||||
|
||||
### 7.3 水渍和氧化
|
||||
|
||||
```hlsl
|
||||
// 淬火时的水渍痕迹
|
||||
float WaterStainMask = SampleWaterStainTexture(WorldPos);
|
||||
float3 PatinaColor = float3(0.3, 0.4, 0.5); // 蓝绿色氧化
|
||||
|
||||
// 仅在潮湿区域显示
|
||||
float HumidityFactor = 0.3; // 可调
|
||||
FinalBaseColor = lerp(FinalBaseColor, FinalBaseColor * PatinaColor,
|
||||
WaterStainMask * HumidityFactor);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 性能优化
|
||||
|
||||
### 8.1 材质优化
|
||||
|
||||
```hlsl
|
||||
// 早期退出优化
|
||||
float Polish = VertexColor.g;
|
||||
if (Polish > 0.95)
|
||||
{
|
||||
// 完全打磨:直接返回金属色,跳过噪声计算
|
||||
return MetalColor;
|
||||
}
|
||||
|
||||
// LOD:基于距离减少噪声层数
|
||||
float DistanceToCamera = length(View.WorldPosition);
|
||||
int32 NoiseOctaves = DistanceToCamera < 100.0 ? 3 : 1;
|
||||
```
|
||||
|
||||
### 8.2 顶点属性压缩
|
||||
|
||||
如果内存紧张,可以压缩属性:
|
||||
|
||||
```cpp
|
||||
// 打磨度:8位精度足够
|
||||
uint8 CompressedPolish = FMath::RoundToInt(PolishAmount * 255.0f);
|
||||
PolishAmount->SetValue(VID, CompressedPolish / 255.0f);
|
||||
|
||||
// 初始位置:16位量化
|
||||
FVector QuantizedPos(
|
||||
FMath::RoundToInt(LocalPos.X * 100.0f) / 100.0f,
|
||||
FMath::RoundToInt(LocalPos.Y * 100.0f) / 100.0f,
|
||||
FMath::RoundToInt(LocalPos.Z * 100.0f) / 100.0f
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 调试与可视化
|
||||
|
||||
### 9.1 可视化打磨度
|
||||
|
||||
```cpp
|
||||
void ADynamicIronBlock::DebugDrawPolishHeatmap()
|
||||
{
|
||||
if (!DynamicMesh->HasMesh())
|
||||
return;
|
||||
|
||||
for (int32 VID : DynamicMesh->VertexIndicesItr())
|
||||
{
|
||||
if (VID % 50 != 0) // 稀疏采样
|
||||
continue;
|
||||
|
||||
float Polish = PolishAmount->GetValue(VID);
|
||||
FVector WorldPos = GetActorTransform().TransformPosition(DynamicMesh->GetVertex(VID));
|
||||
|
||||
// 颜色映射:蓝色(0)→ 绿色(0.5)→ 红色(1.0)
|
||||
FColor DebugColor = FMath::Lerp(
|
||||
FColor::Blue,
|
||||
FColor::Red,
|
||||
Polish
|
||||
);
|
||||
|
||||
DrawDebugPoint(GetWorld(), WorldPos, 5.0f, DebugColor, false, 5.0f);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 材质调试模式
|
||||
|
||||
在材质中添加调试开关:
|
||||
|
||||
```hlsl
|
||||
// 材质参数
|
||||
int32 DebugMode; // 0=正常, 1=显示打磨度, 2=显示铁锈图案, 3=显示初始位置
|
||||
|
||||
float3 DebugOutput;
|
||||
switch (DebugMode)
|
||||
{
|
||||
case 1: // 打磨度热图
|
||||
DebugOutput = float3(Polish, 0.0, 1.0 - Polish);
|
||||
break;
|
||||
case 2: // 铁锈图案
|
||||
DebugOutput = RustPattern;
|
||||
break;
|
||||
case 3: // 初始位置可视化
|
||||
DebugOutput = frac(InitialLocalPos * 0.1);
|
||||
break;
|
||||
default:
|
||||
DebugOutput = FinalBaseColor;
|
||||
break;
|
||||
}
|
||||
|
||||
return DebugOutput;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 已知限制与未来改进
|
||||
|
||||
### 10.1 当前限制
|
||||
|
||||
1. **顶点密度瓶颈**: 20-50万顶点的细节仍不及2048²纹理
|
||||
2. **通道紧张**: 4个顶点颜色通道不够用,需要UV1补充
|
||||
3. **无物理反射**: 打磨效果仅通过粗糙度模拟,无真实光线追踪
|
||||
|
||||
### 10.2 未来改进方向
|
||||
|
||||
1. **混合方案**: 顶点属性(大区域) + 纹理细节(微观)
|
||||
2. **动态纹理**: 运行时更新高分辨率磨损纹理
|
||||
3. **程序化细节**: 材质中用噪声补充细节
|
||||
4. **多层材质**: 支持油污、涂层、水渍等多层效果
|
||||
5. **法线贴图**: 从打磨度生成动态法线贴图
|
||||
|
||||
---
|
||||
|
||||
## 11. 引用
|
||||
|
||||
- 对应需求: [../requirements/02-表面磨损与外观需求.md](../requirements/02-表面磨损与外观需求.md)
|
||||
- 总体架构: [00-锻造系统总体技术架构.md](00-锻造系统总体技术架构.md)
|
||||
- 原始设计文档(归档): [../_archive/plans/2026-01-22-表面磨损系统细节.md](../_archive/plans/2026-01-22-表面磨损系统细节.md)
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user