I have 3 texture shapes that I want to overlap with each other but I haven't been able to figure out how to do it. I can easily make additive blending happen through:
color1 + color2 + color3;
But I cannot figure out how to do it without the additive effect. I understand I need to do "alpha blending" somehow but I've failed to get past the stage of only finding formulas or code snippets that I cannot apply properly in a DX9 pixel shader.
There are other blending modes, but the basic alpha blending in a shader program is of the form
blended = lerp(bottom_layer, top_layer, value)
Where blended will returns top_layer when value equals 1and mix between the two when value is between 0 and 1, and returns the bottom layer when value is 0.
Simply repeat the operation again for blending any additional layer.
The choice for the value depends on the application. For overlapping shapes without mixing colors, use masking shape for the value. For example:
blended = lerp( background, color_green, step(length(uv-0.5),0.5));
Will draw a green circle on top of background color without color mixing.
Related
I have a metal view that displays some textured quads. The textures are loaded from PNGs so are premultiplied. Some of the textures have transparent pixels.
When I enable blending and draw in the right order, the transparency works and you can see quads beneath other quads through the transparent parts of the textures. However, I'm having to calculate the right draw order by sorting which is expensive and slowing down my rendering a lot.
When I've tried to use depth stencils and draw in any order, I can get the order working correctly using z position, but then the blending stops working. The transparent parts of the texture reveal the background color of the metal scene rather than the quad below.
What am I doing wrong? Is there a way to get this working and could someone provide some example code?
The other option I see is to try and do the sorting on the GPU, which would be fine as the GPU frame time is significantly smaller than the CPU frame time. However, I'm also not sure how to do this.
Any help would be greatly appreciated. :)
Alpha blending is an order-dependent transparency technique. This means that the (semi-)transparent objects cannot be rendered in any arbitrary order as is the case for (more expensive) order-independent transparency techniques.
Make sure your transparent 2D objects (e.g., circle, rectangle, etc.) have different depth values. (This way you can define the draw ordering yourself. Otherwise the draw ordering depends on the implementation of the sorting algorithm and the initial ordering before sorting.)
Sort these 2D objects based on their depth value from back to front.
Draw the 2D objects from back to front (painter's algorithm) using alpha blending. (Of course, your 2D objects need an alpha value < 1 to actually see some blending.)
And you need to setup pipelineStateDescriptor correctly:
// To have depth buffer.
pipelineStateDescriptor.depthAttachmentPixelFormat = .depth32Float
// To use transparency.
pipelineStateDescriptor.colorAttachments[0].isBlendingEnabled = true
pipelineStateDescriptor.colorAttachments[0].rgbBlendOperation = .add
pipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = .add
pipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
Hope this helps. From here
I'm working on a WebGL project that resembles a particle system. For aesthetic purposes, my single rendering pass is configured to blend additively, and I've disabled depth testing. I'm also clearing my viewport buffer to 50% gray, for the sake of argument.
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE);
gl.disable(gl.DEPTH_TEST);
gl.clearColor(0.5, 0.5, 0.5, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
I've uploaded a vertex buffer and index buffer to the GPU representing two partially overlapping triangles. Their vertices have a vec3 color attribute. I've assigned each vertex a color of 50% gray (0.5, 0.5, 0.5).
When I draw the triangles with my shaders, I'm relieved to report that my viewport buffer now looks 50% gray with two overlapping triangular regions that are white. The triangles are white because their fragments' color values were additively blended with those already in the color buffer.
Now, I re-upload the vertex buffer with the following change: the color of the vertices of the second triangle are now -50% gray (-0.5, -0.5, -0.5).
What I hope to accomplish is that my viewport buffer would look 50% gray with two overlapping triangular regions– one white, one black– which intersect, and produce 50% gray at their intersection. After all, adding a negative number should be the same as subtracting a positive number of the same magnitude.
What I see instead is a 50% gray viewport with only one triangular region– the white one.
I assume that this is because the output of my fragment shader is being clamped to a range whose lower bound is zero, before it's blended with the color buffer. I would like to know how to circumvent this clamping– ideally in WebGL, without requiring multiple render passes.
I'll be testing solutions in the source of the page at this URL: http://rezmason.net/scourge/issues/positive_negative_fragments.html
UPDATE
As an investigation, I've experimented with performing my additive blending in a frame buffer, and then drawing the frame buffer to the viewport buffer by texturing it to a unit quad– that's two separate draw calls, of course, which ideally I'd like to avoid.
That said, because I can explicitly set the format of the frame buffer to floating point, no clamping occurs with any value while I perform operations within that buffer. When I draw the buffer to the viewport, I assume that clamping finally occurs, but by then all the blending is already done.
My question is now substantially simpler: is there any way to initialize a WebGL or OpenGL context, so that its viewport is formatted as a floating point buffer?
Use gl.blendEquation( gl.FUNC_SUBTRACT ). Then use positive values in your shader.
If you want do something in the middle, you can make some hacky things:
gl.enable(gl.BLEND);
gl.blendFuncSeparate(gl.ONE_MINUS_SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
gl.blendEquation(gl.FUNC_ADD);
It will give you this equation:
You can now draw white triangle if you set color to (0.5, 0.5, 0.5, 0) and black triangle with color (0.5, 0.5, 0.5, 1).
If you want different colors I hope you get the point. You can compare different blending functions here: http://www.andersriggelsen.dk/glblendfunc.php
Edit:
Sorry, my mistake. You should change
gl.blendFuncSeparate(gl.ONE_MINUS_DST_ALPHA, gl.ONE_MINUS_DST_ALPHA, gl.ONE, gl.ONE);
to
gl.blendFuncSeparate(gl.ONE_MINUS_SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
I've forgotten which one is source and which one is destination. I've updated my answer.
Does directional lighting affect objects that are NOT textured?
All examples/tutorials i see/read about always have texture mapped objects.
No, WebGL does what you program it to do.
Directional lighting is usually computed as a multiplier (0 = black, 1 = white) and is then multiplied by whatever color you want to output. If that color is a solid color instead of a texture you don't need textures.
But it's up to you to write shaders that use the lighting the way want it to do the calculations and combine the results.
Opengl es2 on the iphone. Some objects are made with multiple layers of sprites with 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. I do the fading by adjusting the alpha in the shader.
My textures are PNG with alpha made in photoshop. 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.
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. They will start to fade but won't go all the way down to alpha zero. 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. 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:
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.
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?
I'm assuming the GL_SRC_ALPHA is needed to blend using alpha in the shader.
Or is my problem that I need to use something other than PSD to save my sprite sheet? 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. I even tried premultiplying the alpha myself in the shader above by adding:
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. 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/
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. 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.
Another possibility is if you're compositing many elements, your straight alpha blending function is incorrect. 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:
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". (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! 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/
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. 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.
AFAIK, glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); is for premultiplied alpha, and it should work well if you use colors as is. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); is for straight alpha.
Many texture loading frameworks implicitly convert images into premultiplied-alpha format. 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. 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. 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. But Photoshop already erased the color information so they will have some random color values.
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. (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)
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. If you need precise color magic stuffs, use straight alpha — avoid Photoshop.
Update
Here's my test result with #SlippD.Thompson's test code on iPhone simulator (64-bit/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)
I'm building a thick line from triangles. The problem i'm having is that when the curve is semi transparent and some triangles overlap i get the effect in the picture. I would like for the triangles alphas to not get added.
I'm using this blend function:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_CONSTANT_ALPHA);
You may render the curve to separate render target with full opacity and then draw that target with custom alpha. Otherwise you should avoid overlapping.
you can use stencil test for blocking the drawing of already drawn fragments (which prevent blending from the first place)