I’m trying to render an opaque object inside a semi-transparent one.
My approach is:
Depth compare function set to less
descriptor.depthCompareFunction = .less
First a draw call with the opaque object, with not blending enabled.
A second draw call with the semi-transparent object, with blending enabled.
if !opaque {
pipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
}
But I get unexpected result: example
all the code here: https://github.com/quaternionboy/Metal-Playground
I found, with the help of Caroline Begbie (raywenderlich), the solution: sort front to back the vertices...
https://forums.raywenderlich.com/t/rendering-opaque-object-inside-transparent-one/121942/2
Related
I'm using metal to draw some lines, my drawing canvas has a texture in MTLRenderPassDescriptor and when I draw inside it blending is enabled MTLRenderPipelineDescriptor and I'm using alphaBlendOperation = .max
renderPassDescriptor = MTLRenderPassDescriptor()
let attachment = renderPassDescriptor?.colorAttachments[0]
attachment?.texture = self.texture
attachment?.loadAction = .load
attachment?.storeAction = .store
let rpd = MTLRenderPipelineDescriptor()
rpd.colorAttachments[0].pixelFormat = .rgba8Unorm
let attachment = rpd.colorAttachments[0]!
attachment.isBlendingEnabled = true
attachment.rgbBlendOperation = .max
attachment.alphaBlendOperation = .max
I can change the properties in brush (size, opacity, hardness "blur"). However first two brushes are working really great as in the image bellow
But I have only one weird behavior when I use blurred brush with faded sides where lines are connected the faded areas is not blending as expected and an empty small line created between the connection. the image bellow described this issue, please check the single line and single point and then check the connections you can see this behavior very clear
MTLRenderPassDescriptor Should choose even the bellow alpha from down texture or brush alpha but when tap in the second and third point its making empty line instead of choosing a one of the alpha, Its like making alpha zero in these areas.
This is my faded brush you can see there is a gradian of color but i don't know if there is a problem with it
Please share with me any idea you have to solve it
For whatever reason I am having issues with alpha blending in metal. I am drawing to a MTKView and for every pipeline that I create I do the following:
descriptor.colorAttachments[0].blendingEnabled = YES;
descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
However for whatever reason that is not causing alpha testing to happen. You can even check in the frame debugger and you will see vertices with an alpha of 0 that are being drawn black rather than transparent.
One thought I had is that some geometry ends up on the exact same z plane so if alpha blending does not work on the same z plane that might cause an issue. But I dont think that is a thing.
Why is alpha blending not working?
I am hoping to blend as if they were transparent glass. Think like this.
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.)
Your blend state for alpha blending is correct:
// The blend formula is defined as:
// (source.rgb * sourceRGBBlendFactor ) rgbBlendOperation (destination.rgb * destinationRGBBlendFactor )
// (source.a * sourceAlphaBlendFactor) alphaBlendOperation (destination.a * destinationAlphaBlendFactor)
// <=>
// (source.rgba * source.a) + (destination.rgba * (1-source.a))
descriptor.colorAttachments[0].blendingEnabled = YES;
descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
I have used shader modifiers for Plane but its not working. Can anyone suggest me how to solve it?
let myShaderfragment = "#pragma transparent;\n" + "_output.color.a = 0.0;"
let myShaderSurface = "#pragma transparent;\n" + "_surface.diffuse.a = 0.0;"
material.shaderModifiers = [SCNShaderModifierEntryPoint.fragment : myShaderfragment, SCNShaderModifierEntryPoint.surface : myShaderSurface]
The SceneKit: What's New session from WWDC 2017 explains how to do that.
For the plane, use a material with constant as its lightingModel. It's the cheapest one.
This material will have writesToDepthBuffer set to true and colorBufferWriteMask set to [] (empty option set). That way the plane will write in the depth buffer, but won't draw anything on screen.
Set the light's shadowMode to deferred so that shadows are not applied when rendering the objects themselves, but as a final post-process.
There's a dedicated lighting model now (SCNLightingModelShadowOnly) to only render shadows
I have an odd problem implementing corner detection using GPUImage.
I'm trying to use the filter template from Brad's download as a starting point, but although I can generate a composite view of the image and the corner points (as single white pixels), when I try to add in the crosshair generator, the callback function is never called.
In my simplified example, I have an output view configured as RenderView and videoCamera defined as Camera?
do {
videoCamera = try Camera(sessionPreset:AVCaptureSessionPreset640x480, location:.backFacing)
videoCamera!.runBenchmark = true
} catch {
videoCamera = nil
print("Couldn't initialize camera with error: \(error)")
}
let filter = HarrisCornerDetector()
let crosshairGenerator = CrosshairGenerator(size: Size(width: 480, height: 640))
filter.cornersDetectedCallback = { corners in
crosshairGenerator.renderCrosshairs(corners)
}
videoCamera! --> filter
let blendFilter = AlphaBlend()
videoCamera! --> blendFilter --> renderView
//crosshairGenerator --> blendFilter // INPUT 1: if I add this line, the callback never happens
filter --> blendFilter // INPUT 2: with this input to blendFilter, the callback is good
As shown, the callback function is called as expected, and I see little white dots in the output.
If I remove comments to enable INPUT 1, and comment out INPUT 2, the display shows the camera only, and the corner detection callback is never called.
The sample project that Brad provides works on my device, so I know there's no hardware or iOS issue!
Any thoughts?
You forgot to input your crosshair width
crosshairGenerator.crosshairWidth = 15.0
and there is no threshold that you declared
Make the Harris-detector object an instance property.
I'm trying to implement transparent objects in D3D11. I've setup my blend state like this:
D3D11_BLEND_DESC blendDesc;
ZeroMemory(&blendDesc, sizeof (D3D11_BLEND_DESC));
blendDesc.RenderTarget[0].BlendEnable = TRUE;
blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; //0x0f;
// set blending
m_d3dDevice->CreateBlendState(&blendDesc, &blendState);
float blendFactor[4] = {1,1,1, 1 };
m_d3dContext->OMSetBlendState(blendState, blendFactor, 0xffffffff);
Rendering transparent object on top of nontransparent object looks fine. Problem is, when I draw transparent object, and another transparent object on top of it, their colors add up and are less transparent. How to prevent this? Thank you very much
Your alphablending follows the formula ResultingColor = alpha * BackbufferColor + (1-alpha) * RenderedColor. At the overlapping parts of your transparent objects this formula will be applied twice. For example if your alpha is 0.5, the first object will replace the backbuffercolor for 50%. The second object interpolates its color for 50% from the previous color, which is 50% background and 50% first object, leading to a total of 25% of your background. This is why overlapping transparent objects looks more oqaque.
If you want an equal transparency over the whole screen, you could render your transparent objects onto a offscreen texture. Afterwards you render this texture over the backbuffer with a fix transparency or encode the transparency in the texture if you need different values.