在MTKView上重复冲压纹理的透明度问题 [英] transparency issues with repeated stamping of textures on an MTKView

查看:93
本文介绍了在MTKView上重复冲压纹理的透明度问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一种支持金属的绘图应用程序,该应用程序通过沿路径重复冲压纹理正方形来在MTKView上绘制笔触.我遇到的问题是,尽管每个笔刷图章都能正确显示纹理的不透明度,但是重叠的正方形不会建立值,而是会相互覆盖.在下面的标题中,每个邮票都是带有alpha成分的带纹理的圆圈

I am trying to implement a metal-backed drawing application where brushstrokes are drawn on an MTKView by stamping a textured square repeatedly along a path. The problem I'm having is that, while each brush stamp properly shows the texture's opacity, overlapping squares do not build value, but rather override each other. In the caption below, each stamp is a textured circle with an alpha component

我有一种感觉,因为所有图章都被一次渲染,所以渲染器无法积累"价值.但是,我对我的金属知识不甚了解,因此我希望有人可以指出正确的方向.

I have a feeling that because all the stamps are being rendered at once, there is no way for the renderer to "build up" value. However, I'm a little out of my depth with my metal know-how, so I'm hoping someone can point me in the right direction.

以下是其他相关信息:

对于单个笔触,所有几何图形都存储在一个数组vertexArrayBrush3DMesh中,该数组包含所有正方形图章(每个正方形由2个三角形组成).每个顶点的坐标的z值均为0.0,这意味着它们都占据相同的3d平面".这可能是个问题吗? (我测试了放置随机的z值,但没有发现行为上的视觉差异)

For a single brush stroke, all geometry is stored in an array vertexArrayBrush3DMesh that contains all the square stamps (each square is made up of 2 triangles). The coordinates for each vertex have a z-value of 0.0 which means they all occupy the same 3d 'plane'. Could this be an issue? (I tested putting randomized z-values, but I saw no visual difference in behavior)

下面是我的renderPipeline设置.请注意,.isBlendingEnabled = true"和".alphaBlendingOperation = .add"都被注释掉了,因为它们对解决我的问题没有任何作用

Below is my renderPipeline set up. Note that ".isBlendingEnabled = true" and ".alphaBlendingOperation = .add" are both commented out, as they had no effect in solving my problem

// 5a. Define render pipeline settings
    let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
    renderPipelineDescriptor.vertexFunction = vertexProgram
    renderPipelineDescriptor.sampleCount = self.sampleCount
    renderPipelineDescriptor.colorAttachments[0].pixelFormat = self.colorPixelFormat
    //renderPipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
    //renderPipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
    renderPipelineDescriptor.fragmentFunction = fragmentProgram

请注意,将以下属性添加到renderPassDescriptor确实在设置整个画布的透明度上有作用

Note that adding the following properties to renderPassDescriptor did have an effect in setting transparency for the entire canvas

// ensure canvas is transparent
        renderPassDescriptor?.colorAttachments[0].loadAction = .clear
        renderPassDescriptor?.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0)

下面是我的代码中用于渲染的部分.

Below is the portion of my code that does the rendering.

func metalRenderColoredMesh(){
// the key to this routine is that it operates on a prepopulated array of points stored in vertexArrayBrush3DMesh
// whenever we want to render the current mesh, this routine gets called from draw()
if vertexArrayBrush3DMesh.count > 1 { // we must have more than 2 points to be able to draw a line
  // 6. Set buffer size of objects to be drawn
  let dataSize = vertexArrayBrush3DMesh.count * MemoryLayout<Vertex3DColor>.stride // apple recommendation size of the vertex data in bytes
  let vertexBuffer: MTLBuffer = device!.makeBuffer(bytes: vertexArrayBrush3DMesh, length: dataSize, options: [])! // create a new buffer on the GPU
  let renderPassDescriptor: MTLRenderPassDescriptor? = self.currentRenderPassDescriptor
  let samplerState: MTLSamplerState? = defaultSampler(device: self.device!)
  let texture = MetalTexture(resourceName: "opaqueRound", ext: "png", mipmaped: true)

  texture.loadTexture(device: device!, commandQ: commandQueue, flip: true)
  // If the renderPassDescriptor is valid, begin the commands to render into its drawable
  if renderPassDescriptor != nil {
    // ensure canvas is transparent
    renderPassDescriptor?.colorAttachments[0].loadAction = .clear
    renderPassDescriptor?.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0)

    // Create a new command buffer for each tessellation pass
    let commandBuffer: MTLCommandBuffer? = commandQueue.makeCommandBuffer()
    // 7a. Create a renderCommandEncoder four our renderPipeline
    let renderCommandEncoder: MTLRenderCommandEncoder? = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
    renderCommandEncoder?.label = "Render Command Encoder"      
    renderCommandEncoder?.setRenderPipelineState(renderPipeline!)
    renderCommandEncoder?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
    renderCommandEncoder?.setFragmentTexture(texture.texture, index: 0)
    renderCommandEncoder?.setFragmentSamplerState(samplerState, index: 0)

    // most important below: we tell the GPU to draw a set of triangles, based on the vertex buffer. Each triangle consists of three vertices, starting at index 0 inside the vertex buffer, and there are vertexCount/3 triangles total
    renderCommandEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertexArrayBrush3DMesh.count)

    ///////////renderCommandEncoder?.popDebugGroup()
    renderCommandEncoder?.endEncoding() // finalize renderEncoder set up

    commandBuffer?.present(self.currentDrawable!) // needed to make sure the new texture is presented as soon as the drawing completes

    // 7b. Render to pipeline
    commandBuffer?.commit() // commit and send task to gpu

  } // end of if renderPassDescriptor

} // end of if vertexArrayBrush3DMesh.count > 1   }// end of func metalRenderColoredMesh()

2017/01/17更新

实施@warrenm提供的建议后,笔触看起来无限地更有希望.

After implementing the suggestion provided by @warrenm, my brushstrokes are looking infinitely more promising.

但是,结果出现了一些新的问题/问题.

However, some new questions/issues have arisen as a result.

  1. 我不确定Metal如何处理> 1的加色值.它们会被限制为1吗?这是我在笔触的饱和部分看到振铃的原因吗?

  1. I'm not sure how Metal treats additive color values that are > 1. Do they get clamped at 1? Is this the cause of the ringing I see on the saturated portions of the stroke?

由于我实施的贝塞尔曲线采样的不规则特性,笔触的某些区域部分显得有些不规则.为了使这种印章方法起作用,我必须找出一种在整个笔划中均匀分配印章的方法.

Due to the irregular nature of bezier curve sampling I've implemented, there are areas portions of the brushstroke that appear somewhat patchy. In order for this method of stamping to work, I have to figure out a way to evenly distribute the stamps across the entire stroke.

推荐答案

您的混合因子需要一些工作.默认情况下,即使启用了混合,片段着色器的输出也会替换 颜色缓冲区的当前内容(请注意,这里我忽略了深度缓冲区,因为这可能无关紧要).

Your blend factors need some work. By default, even with blending enabled, the output of your fragment shader replaces the current contents of the color buffer (note that I'm ignoring the depth buffer here, since that's probably irrelevant).

您当前拥有的混合方程为:

The blend equation you currently have is:

c dst ′ = 1 * c src + 0 * c dst

cdst′ = 1 * csrc + 0 * cdst

对于经典的基于源的合成,您想要的更像是:

For classic source-over compositing, what you want is something more like:

c dst ′ =α src * c src +(1-α src )* c dst

cdst′ = αsrc * csrc + (1 - αsrc) * cdst

renderPipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
renderPipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
renderPipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha    
renderPipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha

对于您的特定用例,您可能想使用加法混合,将新的片段值简单地添加到已经存在的值中

For your particular use case, you might instead want to use additive blending, where the new fragment value is simply added to what's already there:

c dst ′ = 1 * c src + 1 * c dst

cdst′ = 1 * csrc + 1 * cdst

renderPipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .one
renderPipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .one
renderPipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .one  
renderPipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .one

您是否真正想要添加添加剂取决于您所追求的确切效果.值小于1时,将创建一种累积"的绘画效果,但是一旦累积颜色值超过1,也将达到饱和,这可能并不令人满意.另一方面,加性混合是可交换的,这意味着您不必关心绘制笔触的顺序.

Whether or not you actually want additive blending depends on the exact effect you're after. With values less than 1, it will create a sort of "accumulating" paint effect, but it will also saturate once the cumulative color values exceed 1, which may not be pleasing. On the other hand, additive blending is commutative, which means you don't have to care about the order in which you draw your brush strokes.

(在前面的讨论中,我已经忽略了预乘alpha,在绘制非透明图像时您绝对必须考虑该乘积.您可以阅读有关所有粗糙细节的所有信息

(In the preceding discussion, I've ignored premultiplied alpha, which you absolutely must account for when drawing to a non-opaque image. You can read all about the gnarly details here.)

这篇关于在MTKView上重复冲压纹理的透明度问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆