通过 webgl 加载 PNG 图像并不完美
Loading the PNG Image by webgl is not perfect
当使用canvas.getContext('2d')
加载具有透明部分的png文件时,它看起来与png文件本身完全一样。但是用canvas.getContext('webgl')
加载时,透明部分会显示为白色。然后如果你在着色器中添加 discard
,它会更好但仍然不如 png 文件完美。如何解决这个问题?
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.img);
void main() {
vec4 color = texture2D(u_image, v_texCoord);
if(color.a < 0.5) {
discard;
}
gl_FragColor = color;
}
听起来您可能需要激活混合。
gl.enable(gl.BLEND);
然后将混合函数设置为使用预乘 alpha(默认)
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
透明度其实有点复杂
有
#1 canvas 本身需要什么
默认 canvas 需要预乘 alpha。换句话说,它希望您提供 RGBA 值,其中 RGB 已乘以 A.
您可以设置 canvas,这样在通过传入 premultipledAlpha: false
创建 webgl 上下文时它不会期望预乘 alpha,如
const gl = someCanvas.getContext('webgl', {premultipliedAlpha: false});
注意:IIRC 这不适用于 iOS。
#2 你加载图片的格式
在 WebGL 中加载图像的默认值是未预乘的 alpha。换句话说,如果图像的像素是
255, 128, 64, 128 RGBA
它将完全像这样加载 (*)
您可以通过设置
告诉WebGL在加载图像时为您预乘
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
调用前 gl.texImage2D
.
现在上面的相同像素将最终成为
128, 64, 16, 128 RGBA
每个RGB都乘以A(上面的A是128,其中128代表128/255或0.5019607843137255)
#3 你在着色器中写了什么
如果您加载了未预乘的数据,您可能会选择在着色器中进行预乘
gl_FragColor = vec4(someColor.rgb * someColor.a, someColor.a);
#4 你如何融合
如果您想将正在绘制的内容混合到已绘制的内容中,则需要打开混合
gl.enable(gl.BLEND);
但是您还需要设置混合的发生方式。有多种功能会影响混合。最常用的一种是 gl.blendFunc
,它设置 src 像素(由着色器生成的像素)和 dst 像素(在 canvas 中绘制在顶部的像素)在被渲染之前如何受到影响合并。 2 个最常见的设置是
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // unpremultiplied alpha
和
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); // premultiplied alpha
第一个参数是如何乘以 src 像素。上面我们要么乘以 src 的 alpha (SRC_ALPHA),要么乘以 1 (ONE)。第二个参数是如何乘以 dst 像素。 ONE_MINUS_SRC_ALPHA 正是它所说的 (1 - alpha)
如何将所有这些组合在一起取决于您。
This article and This one 稍微涵盖了这些问题
(*) 图片可能有 color conversion applied。
当使用canvas.getContext('2d')
加载具有透明部分的png文件时,它看起来与png文件本身完全一样。但是用canvas.getContext('webgl')
加载时,透明部分会显示为白色。然后如果你在着色器中添加 discard
,它会更好但仍然不如 png 文件完美。如何解决这个问题?
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.img);
void main() {
vec4 color = texture2D(u_image, v_texCoord);
if(color.a < 0.5) {
discard;
}
gl_FragColor = color;
}
听起来您可能需要激活混合。
gl.enable(gl.BLEND);
然后将混合函数设置为使用预乘 alpha(默认)
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
透明度其实有点复杂
有
#1 canvas 本身需要什么
默认 canvas 需要预乘 alpha。换句话说,它希望您提供 RGBA 值,其中 RGB 已乘以 A.
您可以设置 canvas,这样在通过传入 premultipledAlpha: false
创建 webgl 上下文时它不会期望预乘 alpha,如
const gl = someCanvas.getContext('webgl', {premultipliedAlpha: false});
注意:IIRC 这不适用于 iOS。
#2 你加载图片的格式
在 WebGL 中加载图像的默认值是未预乘的 alpha。换句话说,如果图像的像素是
255, 128, 64, 128 RGBA
它将完全像这样加载 (*)
您可以通过设置
告诉WebGL在加载图像时为您预乘gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
调用前 gl.texImage2D
.
现在上面的相同像素将最终成为
128, 64, 16, 128 RGBA
每个RGB都乘以A(上面的A是128,其中128代表128/255或0.5019607843137255)
#3 你在着色器中写了什么
如果您加载了未预乘的数据,您可能会选择在着色器中进行预乘
gl_FragColor = vec4(someColor.rgb * someColor.a, someColor.a);
#4 你如何融合
如果您想将正在绘制的内容混合到已绘制的内容中,则需要打开混合
gl.enable(gl.BLEND);
但是您还需要设置混合的发生方式。有多种功能会影响混合。最常用的一种是 gl.blendFunc
,它设置 src 像素(由着色器生成的像素)和 dst 像素(在 canvas 中绘制在顶部的像素)在被渲染之前如何受到影响合并。 2 个最常见的设置是
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // unpremultiplied alpha
和
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); // premultiplied alpha
第一个参数是如何乘以 src 像素。上面我们要么乘以 src 的 alpha (SRC_ALPHA),要么乘以 1 (ONE)。第二个参数是如何乘以 dst 像素。 ONE_MINUS_SRC_ALPHA 正是它所说的 (1 - alpha)
如何将所有这些组合在一起取决于您。
This article and This one 稍微涵盖了这些问题
(*) 图片可能有 color conversion applied。