type
status
date
slug
summary
tags
category
icon
password

1.这是什么?
Decal 通过 “将 2D 纹理投射到 3D 表面” 来实现比如血迹、弹痕、地面污渍等。灵活且性能开销低,可以动态给场景添加临时或局部细节(比如丧尸喷血、家具血迹)
简单说就像现实中的“贴纸”或“喷漆”——把一张血迹/弹孔纹理“贴”到物体表面
传统单向平面投影
// 核心UV计算(单向投射)
uv_decal = saturate(pos_local.xz + float2(0.5)); // 直接用XZ平面投影,无任何修正
// 可选优化:表面朝向筛选(仅当宏开启时生效)
#if DECAL_MERGE && SYSTEM_DERIVATIVES && !NEOX_D3D9
// 1. 计算视图空间下贴花投射平面的法线(用导数ddx/ddy求切线空间法线)
float3 ddy_view = ddy(decal_view_pos).xyz;
#if SYSTEM_UV_ORIGIN_LEFT_BOTTOM
ddy_view = -ddy_view; // 适配UV原点(左下/左上)
#endif
float3 normal = normalize(cross(ddx(decal_view_pos).xyz, ddy_view));
// 2. 计算投射平面法线与物体表面法线的夹角
float angle = saturate(dot(normal, normalize(input.normal.xyz)));
// 3. 夹角>70度(cos70≈0.342)则丢弃(不显示血迹)
if (angle < 0.342)
discard;
#endif
投影仪 你懂的 会有切边

半球投射
float coef = length(pos_local.xyz) / length(pos_local.xz);
uv_decal = saturate(pos_local.xz * coef + float2(0.5));
- 以 Decal 的中心为球心,从 “半球形的所有方向” 向外投射纹理。
- 这样,这样可以解决上面投影仪的问题
但是,其实还是有奇奇怪怪的问题 (代码还需完善)



box投射(最牛方案)
// 1. 计算物体表面在贴花局部空间的法线(用导数求)
float3 decal_local_normal;
#if SYSTEM_UV_ORIGIN_LEFT_BOTTOM
decal_local_normal = normalize(cross(ddy(pos_local.xyz), ddx(pos_local.xyz)));
#else
decal_local_normal = normalize(cross(ddx(pos_local.xyz), ddy(pos_local.xyz)));
#endif
// 2. 构建投射法线(固定Y轴为1.0,适配垂直方向投射)
float3 decal_projected_normal = float3(decal_local_normal.x, 1.0, decal_local_normal.z);
if (length(decal_projected_normal) > 0.001) {
decal_projected_normal = normalize(decal_projected_normal);
}
// 3. 计算UV偏差(补偿不同朝向面的UV偏移)
float uv_bias_length = abs(pos_local.y); // 偏差强度与Y轴距离相关
float2 uv_bias;
uv_bias.x = -dot(decal_projected_normal, float3(1.0, 0.0, 0.0)) * uv_bias_length; // X方向偏差
uv_bias.y = -dot(decal_projected_normal, float3(0.0, 0.0, 1.0)) * uv_bias_length; // Z方向偏差
// 4. 最终UV(基础XZ投影 + 偏差修正)
uv_decal = saturate(pos_local.xz + float2(0.5) + uv_bias);
Box 映射是更彻底的解决方案 ——从 “上下左右前后 6 个方向” 分别投射纹理,形成一个 “立方体的投射范围”。
- 相当于用 6 个 “平板投影仪”,从立方体的六个面方向同时投射。
- 不用说的 这效果最好

说明上面代码还需要优化 遮挡判断啥的