跳到主要内容

透明度

PlayCanvas 提供多种渲染非完全不透明表面的方式。它们的开销、对绘制顺序的依赖和伪影类型不同,应根据渲染内容选择。

所有方式都由材质不透明度驱动,来源为 StandardMaterial#opacity、opacityMap 或顶点颜色。

Alpha 混合​

将 blendType 设为 BLEND_NORMAL 等混合模式,会将表面与帧缓冲区已有内容混合。

material.blendType = pc.BLEND_NORMAL;
material.opacity = 0.5;
material.update();

它提供最平滑的结果,支持任意不透明度,但依赖顺序。混合几何体在不透明几何体之后的透明通道绘制,按各图层的 Layer#transparentSortMode 从后到前排序。排序以网格实例为单位,无法解决单个网格自重叠,这常导致植被、头发和玻璃的伪影。混合材质通常也禁用深度写入,因此彼此不会通过深度缓冲遮挡。

混合状态​

blendType 是若干常用配置的简写。要完全控制,可将 BlendState 赋给 Material#blendState,分别指定颜色和 alpha 的混合方程、源和目标因子,以及逐通道颜色写入遮罩。赋予混合状态会覆盖此前通过 blendType 的设置。

// equivalent to BLEND_NORMAL, written out in full
material.blendState = new pc.BlendState(
true,
pc.BLENDEQUATION_ADD, pc.BLENDMODE_SRC_ALPHA, pc.BLENDMODE_ONE_MINUS_SRC_ALPHA
);
material.update();

预定义常量包括 BlendState.NOBLEND、BlendState.ALPHABLEND、BlendState.ADDBLEND 和 BlendState.NOWRITE。为获得最佳性能,应预先创建所需状态,再按需赋值,避免创建后修改状态。

注意,读取属性返回的是只读视图,修改混合必须通过赋值操作,以保持材质透明与排序状态同步:

const state = material.blendState.clone();
state.setColorWrite(true, true, true, false);
material.blendState = state;
material.update();

逐附件混合​

默认混合状态适用于目标的所有颜色附件。使用多渲染目标时,可通过 BlendState#setAttachment 为索引 1–7 的附件设置独立混合状态和写入遮罩。附件 0 通过该类其他属性配置,未指定独立状态的附件跟随附件 0。

// attachment 1 keeps the blending of attachment 0, but writes no channels
const state = material.blendState.clone();
const noWrite = state.clone();
noWrite.setColorWrite(false, false, false, false);
state.setAttachment(1, noWrite);
material.blendState = state;
material.update();

这要求 GraphicsDevice#supportsIndependentBlending。不支持时,所有附件都使用附件 0 的状态。

混合状态使用第二源混合因子时,还会启用双源混合,让片元着色器输出第二种颜色作为混合因子。

Alpha 测试​

alphaTest 丢弃不透明度低于阈值的片元。

material.alphaTest = 0.5;
material.update();

结果是二元的:片元要么完全不透明,要么不存在,无需排序,材质保留在不透明通道并正常写入深度。开销低且完全与顺序无关,代价是镂空边缘生硬且有锯齿。常用于不需要半透明的密集植被和其他镂空对象。

不透明度抖动​

opacityDither 将不透明度转换为屏幕空间抖动图案,按比例丢弃片元,而非混合。

material.blendType = pc.BLEND_NONE;
material.opacity = 0.5;
material.opacityDither = pc.DITHER_BAYER8;
material.update();

可用图案包括 DITHER_BAYER2、DITHER_BAYER4、DITHER_BAYER8、DITHER_BAYER16、DITHER_BLUENOISE 和 DITHER_IGNNOISE。与 alpha 测试一样,它与顺序无关且保留在不透明通道,但支持连续不透明度。代价是可见噪点,配合时间抗锯齿或高输出分辨率可形成平滑透明效果。opacityShadowDither 将同一技术用于对象投射的阴影。

Alpha 转覆盖率​

alphaToCoverage 根据片元 alpha 构建 MSAA 样本覆盖遮罩。硬件不进行混合,而是按 alpha 值保留相应比例的多重采样覆盖。

material.blendType = pc.BLEND_NONE;
material.opacity = 0.5;
material.alphaToCoverage = true;
material.update();

无需启用混合,alpha 像 alpha 测试一样由覆盖遮罩消费。材质仍在不透明通道并写入深度,因此结果与顺序无关。

质量受渲染目标采样数限制。4x MSAA 将不透明度量化为 0%、25%、50%、75% 和 100%,因此适合柔化镂空边缘,但不适合大面积均匀半透明,因为量化会很明显。

要求​

Alpha-to-coverage 需要多重采样目标,在单采样目标中会被静默忽略。发布构建不记录日志也不报错,表面直接完全不透明。如果启用后没有变化,请检查是否真正开启抗锯齿:

const device = await pc.createGraphicsDevice(canvas, {
deviceTypes: [deviceType],
antialias: true
});

WebGPU 还有一个要求:首个颜色附件必须采用有 alpha 通道且可混合的格式。这很重要,因为 CameraFrame 的 HDR 目标优先使用没有 alpha 的 PIXELFORMAT_RG11B10F。因此,默认格式下通过 CameraFrame 渲染的几何体会忽略此功能,调试构建会警告。请求带 alpha 的格式即可解决:

cameraFrame.rendering.renderFormats = [pc.PIXELFORMAT_RGBA16F];
cameraFrame.update();

WebGL 没有此限制,无论目标是否存储 alpha,都使用着色器输出的 alpha。因此 PIXELFORMAT_RG11B10F 等格式在 WebGL 下仍可使用该功能。这是两个后端有意保留的差异,并非错误。

选择方式​

技术不透明度依赖顺序通道主要缺点
Alpha 混合连续是透明排序伪影,无法处理自身排序
Alpha 测试二元否不透明边缘生硬且有锯齿
不透明度抖动连续否不透明没有 TAA 时有可见噪点
Alpha 转覆盖率按采样数量化否不透明需要 MSAA,阶梯较粗

一般而言,玻璃等真实透光且画质比排序更重要的表面使用 alpha 混合;密集镂空用 alpha 测试;淡入淡出和 LOD 过渡,尤其已启用时间抗锯齿时,用不透明度抖动;已承担 MSAA 开销时,用 alpha-to-coverage 柔化镂空边缘。