简体   繁体   English

opengl es2预乘与直接alpha +混合

[英]opengl es2 premultiplied vs straight alpha + blending

Opengl es2 on the iphone. 在iphone上的Opengl es2。 Some objects are made with multiple layers of sprites with alphas. 一些对象由多层精灵与alphas组成。

Then I also have UI elements that are also composited together from various sprites that I fade in/out over top of everything else. 然后我也有UI元素,它们也是由各种精灵组合在一起的,我淡入/淡出其他一切。 I do the fading by adjusting the alpha in the shader. 我通过调整着色器中的alpha来淡化。

My textures are PNG with alpha made in photoshop. 我的纹理是PNG与alphahop中制作的alpha。 I don't premultply them on purpose. 我没有故意为他们做好准备。 I want them to be straight alpha, but in my app they're acting as if they're premultiplied where I can see a dark edge around a white sprite drawn over a white background. 我希望它们是直的alpha,但在我的应用程序中,它们表现得好像它们被预先乘以我可以看到在白色背景上绘制的白色精灵周围的黑色边缘。

If I set my blend mode to: 如果我将混合模式设置为:

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

The elements composite nicely together, no weird edges. 这些元素很好地融合在一起,没有奇怪的边缘。 But when elements are fading out they POP at the end. 但是当元素逐渐消失时,它们最终会成为POP。 They will start to fade but won't go all the way down to alpha zero. 它们将开始消退但不会一直下​​降到alpha零。 So at the end of the fadeout animation when I remove the elements they "pop off" cause they're not completely faded out. 所以在淡出动画结束时,当我删除元素时,它们“弹出”会导致它们没有完全消失。

If I switch my blend mode to: 如果我将混合模式切换为:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

The elements fade up and down nicely. 元素很好地淡化和淡化。 But any white on white element has a dark edge around it from what looks like alpha premultiplication. 但是白色元素上的任何白色都有一个看起来像alpha预乘的暗边缘。 See the white circle drawn over top of the white box: 查看白框顶部绘制的白色圆圈:

在此输入图像描述

But the other blends in the scene look good to me. 但是场景中的其他混合物对我来说很好看。 The other transparent objects blend nicely. 其他透明物体很好地融合在一起。

Another important note is that my shader handles opacity for elements and colorizing elements. 另一个重要的注意事项是我的着色器处理元素和颜色元素的不透明度。 For each thing that is drawn I multiply by an element color and the final alpha with an opacity value: 对于绘制的每个东西,我乘以元素颜色,最后的alpha乘以不透明度值:

void main(void)
{
    gl_FragColor = texture2D(u_textureSampler,v_fragmentTexCoord0);
    gl_FragColor = vec4(gl_FragColor.r*u_baseColor.r,gl_FragColor.g*u_baseColor.g,gl_FragColor.b*u_baseColor.b,gl_FragColor.a*u_baseColor.a * u_opacity);
}

This allows me to take a white object in my sprite sheet and make it any color I want. 这允许我在我的精灵表中拍摄一个白色物体,并使其成为我想要的任何颜色。 Or darken objects by using a baseColor of grey. 或者使用灰色的baseColor使对象变暗。

Every time I think I understand these blend modes something like this comes up and I start doubting myself. 每当我想到我理解这些混合模式时,我就会开始怀疑自己。

Is there some other blend mode combo that will have smooth edges on my sprites and also support alpha fading / blending in the shader? 是否有其他混合模式组合在我的精灵上有光滑的边缘,并且还支持着色器中的alpha淡化/混合?

I'm assuming the GL_SRC_ALPHA is needed to blend using alpha in the shader. 我假设需要GL_SRC_ALPHA在着色器中使用alpha进行混合。

Or is my problem that I need to use something other than PSD to save my sprite sheet? 或者我的问题是我需要使用除PSD以外的东西来保存我的精灵表吗? Cause that would be almost impossible at this point. 因此在这一点上几乎是不可能的。

UPDATE: 更新:

I think the answer might just be NO that there is no way to do what I want. 我认为答案可能只是否定,没有办法做我想要的。 The 2nd blend mode should be correct. 第二种混合模式应该是正确的。 But it's possible that it's double multiplying the RGB with the alpha somewhere, or that it's premultiplied in the source file. 但它有可能是将RGB与某个地方的alpha相乘,或者它在源文件中被预乘。 I even tried premultiplying the alpha myself in the shader above by adding: 我甚至尝试通过添加以下内容在上面的着色器中预先复制alpha:

gl_FragColor.rgb *= glFragColor.a;

But that just makes the fades look bad as things turn grey as they fade out. 但这只会使衰落看起来很糟糕,因为当它们逐渐淡出时,它们会变成灰色。 If I premultiply the alpha myself and use the other blend mode above, things appear about the same as in my original. 如果我自己预先设定alpha并使用上面的其他混合模式,那么事情就像我原来的一样。 They fade out without popping but you can still see the halo. 它们在没有弹出的情况下逐渐消失,但你仍然可以看到光环。

Here's a great article on how to avoid dark fringes with straight alpha textures http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/ 这是一篇关于如何避免带有直纹alpha纹理的黑条纹的精彩文章http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/

If you're using mip-maps, that might be why your straight alpha textures have dark fringes -- filtering a straight alpha image can cause that to happen, and the best solution is to pre-multiply the image before the mip-maps are created. 如果你正在使用mip-maps,这可能就是你的直线alpha纹理有黑条纹的原因 - 过滤直线alpha图像会导致这种情况发生,最好的解决方案是在mip-maps之前预先乘以图像。创建。 There's a common hack fix as well described in that article, but seriously consider pre-multiplying instead. 有一个常见的黑客修复,如该文章中所述,但认真考虑预乘。

Using straight alpha to create the textures is often necessary and preferred, but it's still better to pre-multiply them as part of a build step, or during the texture load than to keep them as straight-alpha in memory. 使用直线α来创建纹理通常是必要且首选的,但是在构建步骤的一部分或纹理加载期间将它们预先乘以比将它们保持为内存中的直线更好更好。 I'm not sure about OpenGL ES, but I know WebGL lets you pre-multiply textures on the fly during load by using gl.pixelStorei with a gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL argument. 我不确定OpenGL ES,但我知道WebGL允许你在加载期间通过gl.pixelPorei和gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL参数在运行中预加倍纹理。

Another possibility is if you're compositing many elements, your straight alpha blending function is incorrect. 另一种可能性是,如果你要合成许多元素,你的直接alpha混合函数是不正确的。 In order to do a correct "over operator" with a straight alpha image, you need to use this blending function, maybe this is what you were looking for: 为了使用直接alpha图像做一个正确的“over operator”,你需要使用这个混合函数,也许这就是你要找的东西:

gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

The common straight alpha blending function you referred to (gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)) does not correctly handle the destination alpha channel, you have to use separate blend functions for color and alpha when blending a straight alpha image source, if you intend to composite many layers using an "over operator". 您引用的常见直接alpha混合函数(gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA))无法正确处理目标Alpha通道,在混合直接alpha图像源时,必须使用单独的颜色和alpha混合函数,如果您打算使用“over运算符”合成多个图层。 (Think about how you probably don't want to interpolate the alpha channel, it should always end up more opaque than both the source & dest.) And take special care when you blend, because the result of the straight-alpha blend is a premultiplied image! (考虑一下你可能不想插入alpha通道,它应该总是比source和dest更不透明。)并且在混合时要特别小心,因为直alpha混合的结果是预乘图像! So if you use the result later, you still have to be prepared to do premultiplied blending. 因此,如果您稍后使用结果,您仍然必须准备好进行预乘混合。 For a longer explanation, I wrote about this here: https://limnu.com/webgl-blending-youre-probably-wrong/ 对于更长的解释,我在这里写了这篇文章: https//limnu.com/webgl-blending-youre-probably-wrong/

The nice thing about using premultiplied images & blending is that you don't have to use separate blend funcs for color & alpha, and you automatically avoid a lot of these issues. 使用预乘图像和混合的好处是你不必为颜色和alpha使用单独的混合函数,并且你自动避免了很多这些问题。 You can & should create straight alpha textures, but then pre-multiply them before or during load and using premult blending (glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)) throughout your code. 你可以&应该创建直的alpha纹理,但是在加载之前或加载期间预加倍,并在整个代码中使用复合混合(glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA))。

AFAIK, glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); AFAIK, glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); is for premultiplied alpha, and it should work well if you use colors as is. 是预乘的alpha,如果按原样使用颜色,它应该可以正常工作。 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); is for straight alpha. 是直的阿尔法。

Many texture loading frameworks implicitly convert images into premultiplied-alpha format. 许多纹理加载框架隐式地将图像转换为预乘的alpha格式。 This is because many of them are doing image re-drawing into a new image, and CGBitmapContext doesn't support straight-alpha (non-multiplied) image. 这是因为他们中的许多人正在将图像重新绘制成新图像,而CGBitmapContext不支持直接(非倍增)图像。 Consequently, they will usually generate premultiplied-alpha image. 因此,它们通常会生成预乘的α图像。 So, please look in your texture loading code, and check whether it was converted into premultiplied format. 因此,请查看纹理加载代码,并检查它是否已转换为预乘格式。

Also, Photoshop (of course Adobe's) implicitly erases color informations on fully transparent (alpha=0) pixels when exporting to PNG. 此外,Photoshop(当然是Adobe的)在导出到PNG时隐式擦除完全透明(alpha = 0)像素上的颜色信息。 If you use linear or other texture filtering, then GPU will sample over neighboring pixels, and colors in transparent pixels will affect pixels at edges. 如果使用线性或其他纹理过滤,则GPU将对相邻像素进行采样,透明像素中的颜色将影响边缘处的像素。 But Photoshop already erased the color information so they will have some random color values. 但Photoshop已经删除了颜色信息,因此它们会有一些随机颜色值。

Theoretically, this color bleeding can be fixed by keeping correct color values on transparent pixels. 从理论上讲,可以通过在透明像素上保持正确的颜色值来修复这种颜色渗色。 Anyway, with Photoshop, we have no practical way to export a PNG file with keeping correct color value because Photoshop doesn't respect invisible stuffs. 无论如何,使用Photoshop,我们没有实用的方法来导出PNG文件并保持正确的颜色值,因为Photoshop不尊重隐形的东西。 (it's required to write a dedicated PNG exporter Photoshop plug-in to export them correctly, I couldn't fine existing one which support this) (需要编写一个专用的PNG导出器Photoshop插件来正确导出它们,我不能很好地支持这个)

Premultiplied alpha is good enough just to display the image, but it won't work well if you do any shader color magics because colors are stored in integer form, so it usually doesn't have enough precision to restore original color value. 预乘alpha只是为了显示图像,但如果使用任何着色器颜色魔法它将无法正常工作,因为颜色以整数形式存储,因此它通常没有足够的精度来恢复原始颜色值。 If you need precise color magic stuffs, use straight alpha — avoid Photoshop. 如果您需要精确的颜色魔法物品,请使用直接alpha - 避免使用Photoshop。


Update 更新

Here's my test result with @SlippD.Thompson's test code on iPhone simulator (64-bit/7.x) 这是我在iPhone模拟器上的@ SlippD.Thompson测试代码的测试结果(64位/ 7.x)

 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 24 bits/pixel; 3-component color space; kCGImageAlphaNone; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 0: (null)
    cgContext with CGImageAlphaInfo 1: <CGContext 0x1092301f0>
    cgContext with CGImageAlphaInfo 2: <CGContext 0x1092301f0>
 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaLast; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 3: (null)
 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaFirst; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 4: (null)
    cgContext with CGImageAlphaInfo 5: <CGContext 0x1092301f0>
    cgContext with CGImageAlphaInfo 6: <CGContext 0x1092301f0>
 <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 24 bits/pixel; 0-component color space; kCGImageAlphaOnly; 2048 bytes/row.
    cgContext with CGImageAlphaInfo 7: (null)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM