You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为片段着色器添加遮罩实现自拍相机背景虚化效果

方案可行性判断

你的思路完全可行,这也是目前移动端实时人像背景虚化的主流实现方案,只需要对现有流程做少量优化就能落地。

现有流程的优化调整建议

  1. 遮罩数据传参优化
    你现在的流程里通过ByteBuffer生成Bitmap再转纹理的方式可以正常运行,但实时性表现一般,生成Bitmap的过程会带来额外的内存拷贝和CPU开销,动态更新频率高的话容易掉帧。建议直接把ByteBuffer的分割数据上传为OpenGL纹理,省去中间Bitmap的转换步骤。
    你当前的maskColorsFromByteBuffer方法里实际只用到了背景置信度对应的alpha值,完全可以直接把backgroundLikelihood的值作为单通道灰度值存入纹理,不需要封装成ARGB颜色,能进一步减少数据传输量。
  2. 片段着色器修改
    不需要在着色器里判断坐标跳过模糊,直接用遮罩的权重值混合原图和模糊结果即可,还能实现边缘的自然过渡,避免硬边问题。修改后的着色器参考如下:
precision mediump float;

uniform sampler2D uSampler;
uniform sampler2D uMaskSampler; // 新增遮罩纹理
uniform float uBlur;
uniform float uRadius;

varying vec2 vTextureCoord;
// 注意要传入遮罩和相机纹理对齐后的纹理坐标,避免错位
varying vec2 vMaskTextureCoord;

void main() {
  // 先采样原图
  vec3 originColor = texture2D(uSampler, vTextureCoord).rgb;
  // 计算模糊结果
  vec3 blurColor = originColor;
  if (uBlur > 0.0) {
    vec3 sum = vec3(0);
    for (float i = -uBlur; i < uBlur; i++) {
      for (float j = -uBlur; j < uBlur; j++) {
        sum += texture2D(uSampler, vTextureCoord + vec2(i, j) * (uRadius / uBlur)).rgb / pow(uBlur * 2.0, 2.0);
      }
    }
    blurColor = sum;
  }
  // 采样遮罩获取背景权重
  float bgWeight = texture2D(uMaskSampler, vMaskTextureCoord).r;
  // 混合结果,bgWeight越高越偏向模糊效果
  gl_FragColor = vec4(mix(originColor, blurColor, bgWeight), 1.0);
}
  1. 坐标对齐注意事项
    MLKit输出的分割Mask的宽高比例、旋转方向可能和相机预览纹理不一致,需要先对Mask做对应的旋转、缩放、翻转处理,保证遮罩和人像位置完全对齐,否则会出现虚化错位的问题。

性能优化建议

你当前使用的盒式模糊是二维循环实现,模糊半径较大的时候性能消耗很高,建议做两个优化:

  • 先把相机输入纹理降采样到1/2或者1/4分辨率,再做模糊处理,最后上采样回原分辨率,模糊效果损失很小但性能提升非常明显
  • 把二维盒式/高斯模糊拆分为横向、纵向两个一维的 pass 实现,时间复杂度从O(n²)降到O(n),能大幅提升大半径模糊的运行效率

替代实现路径

如果你的渲染管线更方便分层渲染,也可以用双Pass的方式实现:

  1. 第一个Pass把整个相机画面做模糊,输出到离屏纹理
  2. 第二个Pass先绘制模糊后的全画面,再用遮罩的alpha值作为混合权重,把人像区域的原图绘制上去,最终效果和单Pass混合完全一致

内容的提问来源于stack exchange,提问作者3akat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 23:54:05