WebGL: How to correctly blend alpha channel png
我是OpenGL的新手,甚至是WebGL的新手。 我正在尝试绘制带有alpha通道的纹理四边形。 但是我只是不能正确混合。
这是我正在寻找的结果
这就是WebGL结果的样子
如您所见,骰子边缘上有一个白色的轮廓,在原始图像中没有。
这就是我在WebGL中进行混合的方式
1 2 3 4 | gl.clearColor(0.5, 0.5, 0.5, 1); gl.disable(gl.DEPTH_TEST); gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); |
这是我的片段着色器:
1 2 3 4 5 6 7 8 9 10 | precision mediump float; varying vec2 vUV; uniform sampler2D texture; void main(void) { vec4 frag = texture2D(texture, vUV); gl_FragColor = frag; } |
知道为什么会这样吗? 我不会创建Mipmap,顺便说一句。
这已经在其他地方得到了解答,但是...
WebGL画布默认为要求预乘alpha。 WebGL画布在网页上混合(混合到页面上)。所以...
您是否不想将WebGL图像与网页混合?
如果不是,则您不想与网页融合,然后执行以下一项操作
-
关闭画布中的Alpha
1var gl = someCanvas.getContext("webgl", { alpha: false }); -
确保您的Alpha值保持在1.0
最简单的方法是在渲染后清除它
1
2
3gl.colorMask(false, false, false, true);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
如果是,您确实要与网页融合,然后执行以下一项操作
-
确保您写入画布的值是预乘的alpha值。
-
告诉浏览器画布中的值未预乘
1var gl = someCanvas.getContext("webgl", {premultipliedAlpha: false});
最重要的是,默认情况下,加载到WebGL中的图像使用未预乘的Alpha,这意味着您要么需要
-
将画布设置为不预乘
-
在着色器中自己进行乘法
1gl_FragColor.rgb *= gl_FragColor.a; -
告诉WebGL将纹理加载到WebGL中时预乘纹理
1gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
假设您的画布是预乘的,那么您希望混合功能为
1 | gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); |
那是因为您不需要将源乘以alpha,因为它已经被预乘了
这是通过在着色器中乘以alpha与页面混合的示例。紫色条纹是CSS背景。绘制图像两次,一次填充画布,一次绘制在1/2尺寸的顶部。您会看到它们全部正确混合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | var vs = ` attribute vec4 position; varying vec2 v_texcoord; uniform mat4 u_matrix; void main() { gl_Position = u_matrix * position; v_texcoord = position.xy * .5 + .5; } `; var fs = ` precision mediump float; varying vec2 v_texcoord; uniform sampler2D u_tex; void main() { gl_FragColor = texture2D(u_tex, v_texcoord); gl_FragColor.rgb *= gl_FragColor.a; } `; var gl = document.querySelector("canvas").getContext("webgl"); var m4 = twgl.m4; var programInfo = twgl.createProgramInfo(gl, [vs, fs]); var bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl); var tex = twgl.createTexture(gl, { src:"https://i.imgur.com/iFom4eT.png", crossOrigin:"", }, render); function render() { gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.useProgram(programInfo.program); twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); twgl.setUniforms(programInfo, { u_tex: tex, u_matrix: m4.identity(), }); twgl.drawBufferInfo(gl, bufferInfo); twgl.setUniforms(programInfo, { u_tex: tex, u_matrix: m4.scaling([0.5, 0.5, 1]), }); twgl.drawBufferInfo(gl, bufferInfo); } |
1 2 3 4 5 | canvas { background-color: purple; background-image: linear-gradient(45deg, rgba(255, 255, 255, 1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 1) 50%, rgba(255, 255, 255, 1) 75%, transparent 75%, transparent); background-size: 50px 50px; } |
1 2 | <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"> <canvas></canvas> |
这是将画布设置为不预乘的示例。唯一的区别是我将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | var vs = ` attribute vec4 position; varying vec2 v_texcoord; uniform mat4 u_matrix; void main() { gl_Position = u_matrix * position; v_texcoord = position.xy * .5 + .5; } `; var fs = ` precision mediump float; varying vec2 v_texcoord; uniform sampler2D u_tex; void main() { gl_FragColor = texture2D(u_tex, v_texcoord); } `; var gl = document.querySelector("canvas").getContext("webgl", { premultipliedAlpha: false, }); var m4 = twgl.m4; var programInfo = twgl.createProgramInfo(gl, [vs, fs]); var bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl); var tex = twgl.createTexture(gl, { src:"https://i.imgur.com/iFom4eT.png", crossOrigin:"", }, render); function render() { gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.useProgram(programInfo.program); twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); twgl.setUniforms(programInfo, { u_tex: tex, u_matrix: m4.identity(), }); twgl.drawBufferInfo(gl, bufferInfo); twgl.setUniforms(programInfo, { u_tex: tex, u_matrix: m4.scaling([0.5, 0.5, 1]), }); twgl.drawBufferInfo(gl, bufferInfo); } |
1 2 3 4 5 | canvas { background-color: purple; background-image: linear-gradient(45deg, rgba(255, 255, 255, 1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 1) 50%, rgba(255, 255, 255, 1) 75%, transparent 75%, transparent); background-size: 50px 50px; } |
1 2 | <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"> <canvas></canvas> |
基本问题是OpenGL使用后乘Alpha混合通道,这会导致各种有趣的伪像。
您需要:
-
修改纹理,以使褪色中没有白色内容
外区域(通常通过向外扩展不透明的彩色像素
进入透明区域-各种纹理工具将执行此操作
为了你) - 或切换为使用预乘Alpha混合,相应地调整纹理(作为警告可能会引入量化伪像)
大多数脱机工具,例如photoshop,GIMP等,默认情况下都使用预乘Alpha,因此,如果您在照片编辑套件中模拟场景,这是实时渲染的非常常见的"惊奇"。
这里的好博客:
https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre