为何当alpha为0时sampler2D始终返回vec4(0.0)?
问题原因
你遇到的这个问题,其实是2D Canvas默认的预乘Alpha模式在搞鬼。
当你用2D Canvas生成纹理数据时,默认的上下文设置会让putImageData把你传入的非预乘颜色(RGB值独立于Alpha)自动转换为预乘格式——也就是每个RGB通道的值都会乘以对应的Alpha值。
对于Alpha=0的像素来说,RGB=2550=0,自然会被清零;而Alpha=1的像素,RGB=2551=255,看起来和原始值一样;Alpha=128的像素,RGB=255*(128/255)=128,理论上会显示灰色,但可能因为颜色差异较小你没注意到,或者浏览器的渲染让这个变化不明显,这才让你误以为“只有Alpha=0时出问题”。
解决方案
这里有两种简单的修复方式:
1. 禁用2D Canvas的预乘Alpha转换
在创建用来生成纹理的2D Canvas上下文时,显式添加premultipliedAlpha: false配置,这样putImageData会完全保留你设置的原始像素值:
// 修改获取2D上下文的代码,添加配置项 const context = image.getContext("2d", { premultipliedAlpha: false });
修改后,你设置的Alpha=0、RGB=255的像素会被原样存储,WebGL采样时就能正确拿到RGB=255的值了。
2. 直接上传像素数组,跳过Canvas
如果不想依赖Canvas,你可以直接创建包含像素数据的Uint8Array,然后直接上传给WebGL纹理,彻底避免格式转换问题:
// 替换原来的Canvas像素设置代码 const pixelData = new Uint8Array([ // 2x2纹理,RGBA格式,按行排列 255, 255, 255, 0, // 左上角:RGB=255, Alpha=0 255, 255, 255, 1, // 右上角:RGB=255, Alpha=1 255, 255, 255, 128, // 左下角:RGB=255, Alpha=128 255, 255, 255, 255 // 右下角:RGB=255, Alpha=255 ]); // 上传纹理时使用这个数组替代Canvas gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixelData);
这种方式更直接,也不会受Canvas上下文设置的影响,适合需要精确控制纹理像素的场景。
验证效果
无论用哪种方案,修改后你的片元着色器都能正确采样到Alpha=0像素的RGB值,左上角区域会显示为白色(因为你强制设置了gl_FragColor.a = 1.0),而不是原来的黑色。
内容的提问来源于stack exchange,提问作者Spongman

