I'm having trouble setting up blending in Metal. Even when starting with the Hello Triangle example provided by Apple, using the following code
pipelineStateDescriptor.colorAttachments[0].blendingEnabled = YES;
pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorZero;
and the fragment function
fragment float4 fragmentShader(RasterizerData in [[stage_in]]) {
return float4(in.color.rgb, 0);
}
the triangle still draws completely opaque. What I want to achieve in the end is blending between two shapes by using different blending factors, but I thought I would start with a simple example to understand what is going on. What am I missing?
sourceAlphaBlendFactor and destinationAlphaBlendFactor are to do with constructing a blend for the alpha channel. i.e. they control the alpha that will be written into your destination buffer, which will not really be visible to you. You are probably more interested in the RGB that is written into the frame buffer.
Try setting values for sourceRGBBlendFactor and destinationRGBBlendFactor instead. For traditional alpha blending set sourceRGBBlendFactor to MTLBlendFactorSourceAlpha and set destinationRGBBlendFactor to MTLBlendFactorOneMinusSourceAlpha
Related
I have written a library for webgl, it has many tests and runs those tests perfectly fine including the one I have for blending.
I've set the test to do a standard blending of Src_Alpha, One_minus_Src_Alpha and the test properly renders the result:
This image is produced by intersecting 1000 lines all with an alpha value of 0.2. Notice how the image does not produce washed out white colors at any given point.
This is also the current blend state as produced by using the webgl Spector plugin for chrome:
Blend State
BLEND: true
BLEND_COLOR: 0, 0, 0, 0
BLEND_DST_ALPHA: ONE_MINUS_SRC_ALPHA
BLEND_DST_RGB: ONE_MINUS_SRC_ALPHA
BLEND_EQUATION_ALPHA: FUNC_ADD
BLEND_EQUATION_RGB: FUNC_ADD
BLEND_SRC_ALPHA: SRC_ALPHA
BLEND_SRC_RGB: SRC_ALPHA
Now I use this same library and render the same edges in a layout and they render like so:
These edges have an opacity of 0.2. And this is the Spector blend mode:
Blend State
BLEND: true
BLEND_COLOR: 0, 0, 0, 0
BLEND_DST_ALPHA: ONE_MINUS_SRC_ALPHA
BLEND_DST_RGB: ONE_MINUS_SRC_ALPHA
BLEND_EQUATION_ALPHA: FUNC_ADD
BLEND_EQUATION_RGB: FUNC_ADD
BLEND_SRC_ALPHA: SRC_ALPHA
BLEND_SRC_RGB: SRC_ALPHA
I am beating my head on a table trying to figure out what the difference between the two scenarios could be.
The shader logic simply hands a color on the vertex to the fragment shader, so there are no premultiplied alphas.
I just need any thoughts of what else can be affecting the blending in such murderous fashion. I can post any extra information needed.
EDIT: To show same exact test in this environment, here is the wheel rendering and adding to white washout somehow:
It seems there was potentially some bad undefined behavior in my library:
I was grabbing the gl context twice: canvas.getContext(...) and each one had the potential to have different properties for things like premultiplied alpha and alpha setting attributes for the context.
When I fixed that issue this bizarre blending error went away. So I will assume the premultiply alpha property between the two context grabs was somehow inconsistent.
I can't achieve correct color representation in my very basic Metal-project if its CAMetalLayer pixelFormat is setted to rgba8Unorm_srgb. By correct I mean that my UIColor's and UIImage's should appear the same in Metal like in UIKit. I have find out that it's exactly that if my CAMetalLayer pixelFormat is rgba8Unorm. But I really need to use rgba8Unorm_srgb.
I've attached two screenshot of this project. It has a squared CAMetalLayer. It's gray background color is its MTLRenderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1). It also have white and black quads and one another quad with red gradient above them, which actually uses standard alpha blending:
colorAttachments[0].rgbBlendOperation = .add
colorAttachments[0].alphaBlendOperation = .add
colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
The only difference between the two screenshots is their CAMetalLayer pixelFormat. But as you can see even the clear color is already different. (They actually different exactly by the gamma-correction). White and black are the same, but all the other colors are different too. Alpha blending does't work correctly too on rgba8Unorm_srgb: you can see, that its half on the white destination is desaturated, and it half on the black destination is oversaturated.
From my understanding I don't need to worry about gamma-correction, don't need to modify my shaders or colors. Metal should do all the math in linear space regardless of pixelFormat. So why is that happening?
Can anyone suggest why my low opacity painting does this weird blending, while the SketchBookX app does it perfect?
In both images attached the vertical strokes on the left are done at full opacity, the strokes on the right are done at low opacity. The top image is mine and as you can see the strokes on the right at low opacity turn a orange-red color and don't blend/mesh with the full opacity strokes. But the SketchBookX app blends perfectly and maintains the same color.
I'm using glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA) and have tried many variations with no luck, so I'm starting to think there are other things that are giving me this problem.
Do I need to handle this problem in the fragment shader? I currently have this,gl_FragColor = color * rotatedTexture; I'm using PNGs for brush textures.
UPDATE: Im getting the same results without using a texture. gl_FragColor = color;
I want it to be like mixing ink, not like mixing light :)
When layering multiple I have to combine them at least once with an "empty" image or background. But there is nothing like "empty". It's transparent black oder transparent white. Even being completely transparent has a color which is normally ignored when displaying.
I'd like to have the folloing:
Destination = Image1 + Image2
To do this I do in fact this:
Clear(Destination, 0); // Clear it with transparent black
Blend Image1 over Destination
Blend Image2 over Destination
Although Destination is transparent black the black contributes to the final result which is what I'd like to avoid. I could clear the destination with transparent white, but it would only work in this example and produces the same problems with darker images.
I understand why the result is like it is: The directx blend formular is set like that:
BlendOp = Add;
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
The alpha of the destination (DestAlpha) isn't accounted at all in this formular. What can I do to implement this everyday problem of blending two images together before rendering them somewhere else?
Afaik you can't achieve this with normal blending operations, but maybe you could write a appropiate shader to do the blending for you. You render on an extra texture as a rendertarget and set this texture in the pixelshader. Now you branch your writed alpha value, if the background-alpha is zero you write your imagecolor, otherwise there is already an image written on this pixel and you can do the normal alphablending in your shader. With this branch you detect, whether the pixel is empty or written before and then prevent the black color to have any impact. Hope this helps :)
A little example to visualize what I mean:
float4 targetColor = tex2D(backgroundSampler,screen.TexCoord);
float4 diffuse = tex2D(imageSampler,image.TexCoord);
// overwrite black background
if (targetColor.a == 0.0) {
return diffuse;
}
// do alphablending
return targetColor*(1-diffuse.a)+diffuse*(diffuse.a);
I'm just starting game development and I thought a game like Tank wars or Worms would be nice.
The hardest part I can think of so far is making the terrain destructible and I want to know how it's done before doing the easy parts.
I thought that explosion could have a mask texture which could be scaled for different weapons. Then using that mask I should make underlying terrain transparent (and optionally draw a dark border).
(source: mikakolari.fi)
How do I achieve that?
Do I have to change the alpha value pixel by pixel or can I use some kind of masking technique? Drawing a blue circle on top of the terrain isn't an option.
I have versions 3.1 and 4.0 of XNA.
This tutorial is what you are searching:
http://www.riemers.net/eng/Tutorials/XNA/Csharp/series2d.php
Capter 20: Adding explosion craters
In short:
You have 2 textures: 1 Color Texture (visible), 1 Collision Texture (invisible)
You substract the explosion image from your collision texture.
To get the dark border: expand the explosion texture and darken the color in this area.
Now you generate a new Color Texture (old color - collison = new color).
This is a difficult question to answer - because there are many ways you could do it. And there are pros and cons to each method. I'll just give an overview:
As an overall design, you need to keep track of: the original texture, the "darkness" applied, and the "transparency" applied. One thing I can say almost for sure is you want to "accumulate" the results of the explosions somewhere - what you don't want to be doing is maintaining a list of all explosions that have ever happened.
So you have surfaces for texture, darkness and transparency. You could probably merge darkness and transparency into a single surface with a single channel that stores "normal", "dark" (or a level of darkness) and "transparent".
Because you probably don't want the dark rings to get progressively darker where they intersect, when you apply an explosion to your darkness layer with the max function (Math.Max in C#).
To produce your final texture you could just write from the darkness/transparency texture to your original texture or a copy of it (you only need to update the area that each explosion touches).
Or you could use a pixel shader to combine them - the details of which are beyond the scope of this question. (Also a pixel shader won't work on XNA 4.0 on Windows Phone 7.)
You should Make a new Texure2D with the Color of desired pixels.Alpha = 0.
Color[] bits = new Color[Texture.Width * Texture.Height];
Texture.GetData(bits);
foreach(Vector2D pixel in overlapedArea)
{
int x = (int)(pixel.X);
int y = (int)(pixel.Y);
bits[x + y * texture.Width] = Color.FromNonPremultiplied(0,0,0,0));
}
Texture2D newTexture = new Texture2D(texture.GraphicsDevice, texture.Width, texture.Height);
newTexture.SetData(bits);
Now replace the new Texture2D with the Last Texture and you're good to go!
For more code about Collision, or changing texture pixels color go to this page for codes:
http://www.codeproject.com/Articles/328894/XNA-Sprite-Class-with-useful-methods