☰
Babylon.js 屏幕空间网格混合(Mesh Blending)架构详解:标签系统、质量等级与经典/FrameGraph 双接入方式
2026/10/1 8:08:51 网站建设 项目流程
  • 图形学
  • 游戏开发
  • 3D渲染

【免费下载链接】Babylon.js

Babylon.js is a powerful, beautiful, simple, and open game and rendering engine packed into a friendly JavaScript framework.

项目地址:https://gitcode.com/gh_mirrors/ba/Babylon.js
点击查看免费下载

网格混合(Mesh Blending)是 Babylon.js 中一个针对WebGL2 与 WebGPU的屏幕空间 SceneColor 后处理效果,用于柔化不透明或 alpha-test 网格之间可见的接触缝隙,让石头压进地面、植被插入山坡这类“假接触”场景不再出现生硬接缝。本文以 specs/mesh-blending/architecture.md 为骨架,结合packages/dev/core中的实现源码,完整讲解其标签编码规则、四档半径类、两种深度表示、四档质量等级、调试视图,以及经典后处理与 FrameGraph/NRGE 两种接入方式,读完即可在 Babylon.js 中配置并使用该效果。

效果定位与使用边界

从架构文档的定义看,Mesh Blending 是一个纯视觉(visual)效果:

  • 它只作用于 SceneColor,用于柔化不透明或 alpha-tested 网格之间可见的接触;
  • 不合并几何体(不改变顶点/网格结构);
  • 不改变碰撞形状、深度缓冲、法线缓冲、间接光照、阴影贴图与阴影几何;
  • 透明物体的渲染完全由调用方自行控制(caller-controlled)。

这一点在 MeshBlendingPostProcess 的类注释 中同样被强调:效果不会修改 geometry、collision queries、depth、normals 或 shadow geometry。因此它适合解决的是“接触处漏光/接缝”这一视觉问题,而不是几何布尔或物理碰撞问题。

网格标签(Mesh Tags)与半径类

标签编码:groupId 与 radiusClass 打包进一个字节

为网格开启混合,需要在网格上写入AbstractMesh.meshBlendingTag,其值由PackMeshBlendingTag(groupId, radiusClass)构造。源码中该函数位于 packages/dev/core/src/Meshes/meshBlendingTag.ts,编码规则如下:

  • groupId必须是 0~63 的整数(越界会抛出RangeError);
  • radiusClass必须是 0~3 的整数(对应MeshBlendingRadiusClass枚举);
  • 返回值(radiusClass << 6) | groupId,即组 ID 占用低 6 位,半径类占用高 2 位。

AbstractMesh.meshBlendingTag的 setter(见 abstractMesh.pure.ts)会校验:值必须为 0,或包含 1~63 之间的组 ID,否则抛出RangeError。同时提供逆操作UnpackMeshBlendingTag(tag)将打包值解码为{ groupId, radiusClass },并对超出 0~255 的输入抛错。

分组语义

架构文档定义了清晰的组规则,源码注释完全一致:

  • 组0禁用混合(标签为 0 即不参与);
  • 组1..63由应用自行分配,代表逻辑组;
  • 同一非零组内的像素互不混合——同组网格被当作一个逻辑物体,例如构成同一栋建筑的多个 mesh 之间不应出现接缝软化;
  • 不同非零组的像素在通过边界(boundary)、深度(depth)、跨度(span)与接触角(contact-angle)校验后可以混合。

源码中PackMeshBlendingTag的文档(meshBlendingTag.ts)与AbstractMesh.meshBlendingTag注释均确认了“同组视为一个逻辑表面”的语义。

四档半径类

MeshBlendingRadiusClass枚举(meshBlendingTag.ts)提供四档半径类:

枚举值数值含义
Small0小混合半径
Medium1中等混合半径
Large2大混合半径
ExtraLarge3超大混合半径

每个半径类组合了两个可配置量(定义见 thinMeshBlendingPostProcess.ts):

  • worldRadius:以 Babylon 世界单位编写的混合半径;
  • minimumProjectedRadius:以物理渲染目标像素为单位的最小投影半径。

CreateDefaultMeshBlendRadiusDefinitions()提供默认值(thinMeshBlendingPostProcess.ts):

半径类worldRadiusminimumProjectedRadius
Small0.061.5
Medium0.13
Large0.23
ExtraLarge0.35

在接触处,两个参与表面中较小的半径类生效。最小投影半径按物理像素计量,作用是让远处的接缝依然可见(近大远小投影后世界半径变小,像素下限兜底);世界半径则保证近处宽度稳定。透视相机与正交相机都支持,包括反转深度(reverse-depth)渲染。

从源码可进一步看到半径的投影逻辑:_ProjectMeshBlendWorldRadiusToPixels使用0.5 * renderTargetHeight * |projectionYScale|将世界半径换算为物理像素,正交相机不除以深度、透视相机除以视深;随后_CalculateMeshBlendSearchRadius取max(投影半径, minimumProjectedRadius)再乘以质量等级的radiusScale(thinMeshBlendingPostProcess.ts)。半径定义对象还带有运行时校验 setter,拒绝NaN、负数等非法值(见 thinMeshBlendingPostProcess.ts)。

实例的限制

实例(Instances)与薄实例(Thin Instances)使用其源网格的标签,不支持逐实例的 mesh-blending 标签。这属于架构文档明确声明的限制,接入时需要将标签设置在源网格上。

几何输入与后端支持

输入纹理清单

效果消费以下**单采样(single-sampled)**几何纹理:

  1. SceneColor(场景颜色);
  2. 打包的 mesh-blending 标签纹理——R8UI格式、最近邻采样、无 mipmap;
  3. 视空间或屏幕空间深度(View / Screen depth);
  4. 可选的线性基色/Albedo 纹理,用于阴影估计(shadow estimation)。

架构文档与源码双重确认:SceneColor 与所有提供的几何输入必须具有相同的物理尺寸和采样覆盖(sample coverage)。经典后处理的_ValidateInputTextureDimensions会逐一校验 tag/depth/baseColor 的宽高与 samples 是否一致,不一致即抛错(见 meshBlendingPostProcess.ts)。

各输入纹理的格式约束在经典包装的校验函数中非常明确(meshBlendingPostProcess.ts):

  • 标签纹理:必须是 2D、TEXTURETYPE_UNSIGNED_BYTE+TEXTUREFORMAT_RED_INTEGER、最近邻采样、无 mipmap、单采样;
  • 深度纹理:2D,格式为 RED/RG/RGBA,类型为 FLOAT 或 HALF_FLOAT;仅当使用MeshBlendDepthType.Screen时才允许归一化无符号字节(UNSIGNED_BYTE);无 mipmap、单采样;
  • 基色纹理(可选):2D、非整数的 RGB/RGBA 颜色格式(UNSIGNED_BYTE / HALF_FLOAT / FLOAT)、无 mipmap、单采样。

alpha-tested 片元只有在通过材质配置的 alpha 截止值(alpha cutoff)后才会写入标签,因此被剔除的片元不会留下“幽灵接缝”。当省略基色/Albedo 时,阴影估计与所有基色采样全部编译剔除(compiled out),不需要回退纹理或额外的几何附件。

后端范围:WebGL2 与 WebGPU

  • 支持WebGL2 与 WebGPU;
  • Native 被拒绝,原因是其当前纹理格式映射无法创建所需的R8UI标签渲染目标;
  • WebGL1 同样不支持。

支持判断逻辑集中在_IsMeshBlendingSupported(meshBlendingTag.ts):要求shaderPlatformName !== "NATIVE"且(isWebGPU或webGLVersion === 2)。单元测试也覆盖了这两种拒绝场景(meshBlendingPostProcess.test.ts)。

透明网格的注意事项

透明网格被允许,但应用必须自行保证它们不会让 SceneColor 与单层的深度、Albedo、标签输入不一致。具体约束:

  • WebGL2 需要每目标混合参数(per-target blend-parameter)支持:混合须对颜色输出保持开启,同时对整数标签附件关闭;不支持该能力的配置若请求透明标签渲染,会被拒绝。从 geometryRendererTask.ts 可见,当renderTransparentMeshes || renderSprites || renderParticles且非 WebGPU、无blendParametersPerTarget能力时会触发校验/拒绝逻辑;
  • 与参与混合的表面**在屏幕空间不相交(screen-space-disjoint)**的透明网格可以安全渲染;
  • 重叠的透明表面会独立于 SceneColor 覆盖或混合几何输入,可能产生空洞、假边界或错误颜色;
  • 推荐配置:在网格混合之后合成透明内容(compositing transparent content after mesh blending)。

两种接入方式:Classic 渲染器与 FrameGraph

Classic 渲染器(经典后处理)

经典路径的接入步骤:

  1. 启用GeometryBufferRenderer所需的输出,将samples设为1,并开启 mesh-blending 标签输出;
  2. 仅在需要阴影估计时提供对齐的基色纹理;
  3. 用这些调用方自有的纹理构造MeshBlendingPostProcess。

关键语义:后处理绝不会启用、重配置、调整尺寸或销毁 GeometryRenderer 及其提供的纹理,因此调用方创建的输入必须随 SceneColor 输入一起 resize。单元测试中"does not dispose caller-owned input textures"明确验证了这一点(meshBlendingPostProcess.test.ts)。

构造参数定义在IMeshBlendingPostProcessOptions(meshBlendingPostProcess.ts):

  • meshBlendTagTexture(必填):打包的R8UI标签纹理;
  • depthTexture(必填):与标签纹理对齐的视深或屏幕深度纹理;
  • baseColorTexture(可选):与 SceneColor 对齐的线性基色/Albedo 纹理,省略则编译剔除阴影估计;
  • 继承自IMeshBlendConfiguration的quality、radiusClasses、slopeFactor、depthType、debugMode;
  • effectWrapper(可选):调用方自有的ThinMeshBlendingPostProcess包装,同一时刻只能附着到一个经典后处理上(_ClaimMeshBlendingEffectWrapper用 WeakMap 强制独占,见 thinMeshBlendingPostProcess.ts)。

经典包装是纯运行时对象:因其几何输入是调用方自有的运行时资源,它不能被序列化、解析或克隆。源码中serialize()直接抛错、clone()返回null,并在构造时设置doNotSerialize = true(meshBlendingPostProcess.ts)。需要可序列化配置时应使用 FrameGraph/NRGE 路径。

Frame Graph 接入

FrameGraph 路径的接入步骤:

  1. 从FrameGraphGeometryRendererTask请求屏幕/视空间深度输出与mesh-blending 标签输出;仅在需要阴影估计时才请求并连接 Albedo;
  2. 标签可以位于任意颜色附件位置,但必须使用TEXTURETYPE_UNSIGNED_BYTE+TEXTUREFORMAT_RED_INTEGER;混合的浮点与整数附件会按各自的标量类型被声明与清除;
  3. 将这些句柄与匹配的 SceneColor 连接到FrameGraphMeshBlendingTask,或在 NodeRenderGraph 中连接到NodeRenderGraphMeshBlendingPostProcessBlock;
  4. 不覆盖renderTransparentMeshes,按场景的重叠与合成需求自行设置。

FrameGraphMeshBlendingTask(meshBlendingTask.ts)在record()阶段会对标签/深度/基色/源纹理逐一做格式、维度、采样数校验,并自动设置最近邻采样、绑定标签/深度(可选基色)采样器、加入纹理依赖。NRGE 节点块NodeRenderGraphMeshBlendingPostProcessBlock(meshBlendingPostProcessBlock.pure.ts)提供了 Quality、Small/Medium/Large/ExtraLarge 世界半径与最小像素、Slope factor、Debug mode 等可在属性面板编辑的项,并支持serialize/_deserialize(默认值回退到CreateDefaultMeshBlendRadiusDefinitions、slopeFactor默认 2、debugMode默认 Off、quality默认 Medium),因此FrameGraph/NRGE 配置可以序列化。

共享核心:ThinMeshBlendingPostProcess

两条路径都基于ThinMeshBlendingPostProcess(thinMeshBlendingPostProcess.ts),所以 quality、radius、slope、depth、debug 配置通过同一份实现完成编译与绑定。该类负责:

  • 管理相机(投影半径与视空间位置重建)与projection/inverseProjection/inverseViewuniform;
  • 绑定四个半径类的blendWorldRadii与minimumProjectedRadii(setFloat4);
  • 绑定meshBlendIsOrthographic(依据相机模式判断)、slopeFactor;
  • 绑定内部稳定的蓝噪声搜索纹理_stableBlueNoiseTexture(128×128、RG 格式、最近邻、wrap 寻址,详见测试 meshBlendingPostProcess.test.ts),并随dispose()销毁。

着色器片段位于 packages/dev/core/src/Shaders/meshBlending.fragment.fx,WebGPU 侧对应 packages/dev/core/src/ShadersWGSL 中的 meshBlending 片段(见_gatherImports的动态导入)。

质量等级(Quality)与开销

MeshBlendQuality.Medium是默认质量等级。四档质量是编译期着色器变体,每个等级由一组精确的采样/搜索参数定义(_MeshBlendQualitySettings,见 thinMeshBlendingPostProcess.ts):

参数LowMediumHighCinematic
搜索方向数 directionCount3338
径向采样数 radialSampleCount2336
方向细化采样数1234
方向细化步数2455
精确边界采样数 exactEdgeSampleCount581050
半径缩放 radiusScale0.50.910.95
全随机旋转 fullRandomRotation否否是是
搜索抖动因子 searchJitterFactor0.50.50.51
四近邻回退否否是是
小物体保护否否是是
多目标次级混合否否是是
颜色插值sRGBOKLabOKLabOKLab

架构文档的定性描述与源码完全对应:

  • Low:更少的搜索,sRGB 插值;
  • Medium:增加 OKLab 插值;
  • High:增加完整搜索旋转(full search rotation)、一像素回退(one-pixel fallback)、小物体保护(tiny-object protection)与次级接缝目标(secondary junction target);
  • Cinematic:增加方向与边界搜索工作量。

这些参数通过_GetMeshBlendQualityDefines生成为着色器宏(如MESH_BLEND_DIRECTION_COUNT 8、MESH_BLEND_FULL_RANDOM_ROTATION、MESH_BLEND_TINY_OBJECT_SAFEGUARD、MESH_BLEND_COLOR_INTERPOLATION_OKLAB等,见 thinMeshBlendingPostProcess.ts)。单元测试逐项断言了四档等级的精确设置与生成的宏(meshBlendingPostProcess.test.ts)。

开销随屏幕覆盖面积与质量增长,因为每个参与像素都要在打包的标签纹理上执行搜索。架构文档的建议很明确:在目标硬件上对开启/关闭及各质量档分别做 profile,而不是依赖一个通用的毫秒阈值。

颜色插值:sRGB 与 OKLab

Low 使用 sRGB 域插值;Medium 及以上使用OKLab感知颜色空间插值。源码在 thinMeshBlendingPostProcess.ts 提供了完整的线性 sRGB ↔ OKLab 转换与插值函数,HDR 分量被保留(不做 gamut/HDR 钳制)。单元测试验证了 OKLab 往返精度与 Low/Medium 插值差异(meshBlendingPostProcess.test.ts)。该方案引用自 Björn Ottosson 的 OKLab 论文。

时间稳定性

内部搜索纹理是确定性空间噪声:它旋转并抖动搜索方向,但帧间不变。效果没有时间累积,不需要 TAA。对应测试还验证了蓝噪声的低频功率被抑制(meshBlendingPostProcess.test.ts)。

接触校验与 slope 因子

除了质量等级,IMeshBlendConfiguration还包含两个影响接触表现的配置项:

  • depthType(MeshBlendDepthType.View默认 0 /Screen1):选择视空间有符号相机 Z(GeometryRenderer 的 view-depth 输出)或归一化屏幕深度(硬件深度)。经典包装校验:Screen 深度才允许 UNSIGNED_BYTE 类型;
  • slopeFactor:接触斜率收窄因子,1 表示禁用收窄,默认值为 2。_CalculateMeshBlendSlopeScale在slopeFactor > 1时把余旋对齐度映射到[0.25, 1]的乘数区间(见 thinMeshBlendingPostProcess.ts),对陡峭接触收窄混合半径、避免过宽的过度混合。校验要求slopeFactor为 ≥1 的有限数(thinMeshBlendingPostProcess.ts)。

接触处像素通过边界、深度、跨度、接触角四类校验后才会混合;_CalculateMeshBlendFade将候选边界距离归一化后输出0~0.5 的混合权重(thinMeshBlendingPostProcess.ts)。

调试视图(Debug Views)

MeshBlendDebugMode枚举(thinMeshBlendingPostProcess.ts)提供 13 种确定性调试可视化,均通过编译期宏开关(MESH_BLEND_DEBUG_*)启用:

枚举值可视化内容
Off渲染最终混合后的场景颜色
PackedTag打包的组与半径类标签
CandidateDirectionDistance精化后的候选方向与归一化边界距离
SeamFade选中的半径类与最终接缝淡化
RejectionReason接触校验接受/拒绝的原因
StageWork每个像素近似执行的着色器工作量
Continuation目标延续成功、回退使用与拒绝
TinyObject对薄投影物体施加的有效半径缩减
MultiTarget多网格接缝处主/次级目标选择
TargetColor更远处、边界+1、边界+2 与构造的目标颜色采样
ShadowAttenuation基色阴影传递启发式的淡化衰减(无 Albedo 输入时为中性)
ColorInterpolation目标颜色构造后的活动颜色插值模式
WorldPosition重建的世界位置

测试对每个调试模式断言了对应宏的生成(meshBlendingPostProcess.test.ts)。注意切换quality/debugMode/depthType/hasBaseColorTexture会触发 shader 重编译(updateEffect),而仅修改slopeFactor等非编译期参数不会(测试 meshBlendingPostProcess.test.ts 已验证该行为)。

官方示例与性能基准

架构文档提供了两个 Playground 片段(注意:需要包含 mesh-blending API 的 Babylon.js 构建;在该构建部署到 Playground Preview 之前,请通过本地 Playground 运行):

  • Classic、FrameGraph、NRGE 对等演示:#O05LI8#6。场景使用反转深度、把标签放在 MRT 槽位 0,并包含不带 Albedo 的 NRGE 路径;在 hash 前追加?engine=webgpu即可切换到 WebGPU;
  • 功能与性能演示:#XVZTSI#3。包含岩石与地面接触、共享组与不同组、远处接缝、全部四档半径类、小道具、三网格接缝、alpha-tested 植被、质量/调试控件与相对 GPU 计时。

部署后,相同片段 ID 可在 Playground Preview 上以?version=preview#O05LI8#6与?version=preview#XVZTSI#3访问。

性能演示暴露了全局函数window.runMeshBlendBenchmark():它使用EngineInstrumentation.captureGPUFrameTime,报告同一设备上 disabled、Low、Medium、High、Cinematic 五种模式的比率。这些结果属于诊断性测量而非通过/失败阈值,与架构文档“在目标硬件上 profile 而非依赖通用毫秒阈值”的建议一致。

设计来源与实现独立性

架构文档明确指出:该实现是为 Babylon.js 独立编写的,没有使用或复现 MeshBlend 插件的源码,但其行为目标与参数模型受到以下公开资源的启发:

  • MeshBlend(行为目标与参数模型参考);
  • Jack Tollenaar 的《Screen space mesh seam blending》(屏幕空间接缝检测、镜像颜色采样与后处理混合);
  • Jack Tollenaar 的《Fast mesh seam blending》(性能分析与优化考量,当前实现并未复现其 indirect-compute 流水线);
  • Björn Ottosson 的《A perceptual color space for image processing》(OKLab 转换与插值)。

在 Babylon.js 中体验完整能力时,建议依次:用PackMeshBlendingTag为网格分组 → 按接触尺度挑选四档半径类并调整默认半径 → 选择质量等级并在目标硬件上 profile → 借助MeshBlendDebugMode定位接缝/拒绝原因 → 需要可序列化配置时走 FrameGraph/NRGE 路径,否则使用经典MeshBlendingPostProcess。

  • 图形学
  • 游戏开发
  • 3D渲染

【免费下载链接】Babylon.js

Babylon.js is a powerful, beautiful, simple, and open game and rendering engine packed into a friendly JavaScript framework.

项目地址:https://gitcode.com/gh_mirrors/ba/Babylon.js
点击查看免费下载
上一篇:StarRocks array_agg 聚合函数完全指南:多行聚合为数组与 ORDER BY 排序实战
下一篇:使用 OctoPack 为 Squirrel.Windows 自动化构建 NuGet 安装包

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询