问题
I am working on a metal-backed drawing application where brushstrokes are drawn on an MTKView by stamping a textured square repeatedly along a path. I am having color-accumulation issues as illustrated with the picture below:
For alpha values [0.5 - 1.0] the results are more or less what I expect. However, for small alpha values, the result looks patchy and never achieves the uniform/correctly saturated value of the original fully opaque brush color (i.e. like the top brushstroke in above picture).
One key concept of my approach, is the following (perhaps flawed) implementation: Say, the desired brushColor is blue, as above:
brushColor = (r: 0.0, g: 0.0, b:1.0, a: 1.0)
The way I set each stampColor is by dividing brushColor by the number of overlapping stamps:
overlapStampCount = n
stampColor = (r: 0.0/overlapStampCount, g: 0.0/overlapStampCount, b:1.0/overlapStampCount, a:1.0/overlapStampCount)
.
The idea being that n overlapping stamps will cumulatively add up to brushColor (blue). Each stamp uses this stampColor at each vertex to multiply a white texture of a round blob with an alpha setting. Below is my fragment shader:
fragment float4 basic_fragment(VertexOut interpolated [[stage_in]], texture2d<float> tex2D [[ texture(0) ]],
sampler sampler2D [[ sampler(0) ]]) {
float4 color = interpolated.color * tex2D.sample(sampler2D, interpolated.texCoord); // texture multiplied by vertex color
return color;
}
.
Thus, based on this concept I set out to find a blending mode that would give me the desired result. Through trial and error, I arrived at the following blending settings to achieve the result in the picture above:
renderPipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .one
renderPipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
renderPipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .one
renderPipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
.
On a related post, I had been advised to use "Source Over Compositing"
cdst′ = αsrc * csrc + (1 - αsrc) * cdst
...which translates to:
renderPipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
renderPipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
renderPipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
renderPipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
.
but the results are even farther from the desired outcome.
the texture I'm using is built from a png file (with transparency) as follows:
let image = UIImage(contentsOfFile: path)!.cgImage!
let colorSpace = CGColorSpaceCreateDeviceRGB()
width = image.width
height = image.height
let rowBytes = width * bytesPerPixel
let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: rowBytes, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
let bounds = CGRect(x: 0, y: 0, width: Int(width), height: Int(height))
context.clear(bounds)
if flip == false {
context.translateBy(x: 0, y: CGFloat(self.height))
context.scaleBy(x: 1.0, y: -1.0)
}
context.draw(image, in: bounds)
let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.rgba8Unorm, width: Int(width), height: Int(height), mipmapped: isMipmaped)
texture = device.makeTexture(descriptor: texDescriptor)
.
To sum up, my question is, how do I set up my textures/blending mode in a way that, for any stamp transparency value, the accumulated result of drawing in a region will eventually result in full color opacity? Any pointers would be appreciated.
来源:https://stackoverflow.com/questions/53788939/mtkview-texture-correct-color-build-up