寒霜的破坏系统是在运行时,通过直接抛弃完整模型中,被破坏区域的三角面片实现的。引擎内部不存在预制的破损模型替换部件,因此寒霜编辑器中无法直接导出破损的墙体或模型直接使用。为确保破坏后的模型边缘在视觉上流畅,模型已在内部提前埋设可能暴露在外的三角面。同时在边缘断裂处添加细碎模型,增加细节。

(左:顶点着色器几何输入 右:最终输出)
模型的分片
为实现场景模型的分区破坏,寒霜为每个可破坏的模型划分了破损分区,并且提前为可破坏的物体的每一个三角面标注了分区ID,作为可见性标记提交进入顶点缓冲区。

(寒霜顶点缓冲的最终定义)
一旦模型受到破坏,需要改变形态时,遭受破坏的分区ID将通过常量缓冲提交至着色器端,在顶点着色阶段,归属于破坏分区的三角面的一切顶点属性,包括SV_Postion将被置0,这会使三角形退化成一个点,被光栅器直接丢弃。寒霜依此实现对模型的动态修改。
模型的内部面
对于一般的网格模型,若直接简单地移除其中部分面片,剩余部分的网格将无法完全闭合。为适应分区破坏的需求,寒霜要求可破坏的模型在导入时提前埋设内部面,以便于在破坏之后暴露在外侧,形成全新的破坏之后闭合网格模型。

(去掉外侧面的墙体模型,其中已为破坏埋设内部面)
边缘细节模型
破损墙体边缘处面数过低,需要添加额外细节模型用以掩盖过于整齐的切口。在墙体受到不同区位的破坏,隐去部分三角面片后,细碎模型将被贴附在切口周围,为破损后的模型添加几何细节。

(左:原始 右:移除切口处的额外模型)
以下是使用在墙体边缘处的细节模型,由数个相同的细碎模型组合后贴附于切口处。类似的细节模型存在多种,引擎会根据破损模型类型(墙体、屋檐)选择合适的细节模型进行贴附。以下是适用于墙体的其中一种细节模型。

(左:细节模型 右:细节模型的组合)
世界空间破损纹理坐标
此处解决破损后的纹理贴图问题:当墙面碎裂露出内部结构(如砖块、钢筋、木片)时,由于没有为破损处额外预先制作 UV,寒霜根据其在世界空间的位置决定最终的纹理坐标,以保证纹理在切口处两侧模型上的连续性。

(纹理采样器使用环绕warp模式)
此处的世界空间纹理 UV 同时使用在主模型的切口附近,与边缘细节模型两处。无论墙面怎么破损,只要世界坐标不变,算出来的 UV 就不变。因此主模型的切口处与贴附其上的细节模型,无论相对位置的变化,均可保证切口两侧破损纹理 UV 连续。
破损纹理遮罩
破坏效果是由一个独立的 3D 空间体积控制的,寒霜内称其为距离场纹理(256*256*256)。在使用这张 3D 纹理之前,将当前像素的世界坐标转换到了破坏体积的局部坐标系,并根据采样结果判断使用自身纹理或是破损纹理
如果采样值 < 阈值:当前像素位于这个 3D 形状的内部 -> 渲染破损的砖块(if 分支)
如果采样值 > 阈值:当前像素位于这个 3D 形状的外部 -> 渲染完好的墙皮(else 分支)

(纹理遮罩的分支逻辑)
这使得破损处的纹理自然连续,如果墙角被炸坏,由于 3D 体积是连续覆盖在此区域的,破损纹理会自然包裹住拐角。
破损纹理
寒霜为场景内的破损模型预备了包含多张纹理,如红砖、混凝土、钢筋等在内的 2D 纹理图集,一切模型的破损状态,在其切口附近均使用了以下纹理数组中其中一张。

(texture_diffuseAtlas)
着色器中,此处的顶点属性 v6 使用 nointerpolation 属性非插值读入,确保一个三角面内的所有像素都使用相同的破损纹理ID,不会出现半砖半水泥的奇怪渐变。