引言
在上一篇博客中,我们学会了如何使用Metal 的深度测试来调整绘制物体的可见性。除了深度测试,混合模式(Blend)也可以调整绘制物体的可见性,我们可以通过不同的混合算法来实现不同的视觉效果。接下来,我们将绘制两个三角形,来解释混合的原理。
Blend 原理
-
渲染管线的顺序
顶点着色器 → 光栅化 → 片段着色器 → 混合阶段 → 帧缓冲
-
混合阶段
- 读取帧缓冲区的颜色(目标颜色)
- 读取片段着色器输出的颜色 (源颜色)
- 根据混合方程计算最终颜色
- 将结果写回帧缓冲
实战
绘制背景颜色
metalView.clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
设置渲染管线
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertextFunc
pipelineDescriptor.fragmentFunction = fragmentFunc
pipelineDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
//开启混合
let colorAttachment = pipelineDescriptor.colorAttachments[0]
colorAttachment?.isBlendingEnabled = true
colorAttachment?.rgbBlendOperation = .add
colorAttachment?.alphaBlendOperation = .add
colorAttachment?.sourceRGBBlendFactor = .sourceAlpha
colorAttachment?.destinationRGBBlendFactor = .oneMinusSourceAlpha
colorAttachment?.sourceAlphaBlendFactor = .one
colorAttachment?.destinationAlphaBlendFactor = .oneMinusSourceAlpha
这里使用了一个标准的透明度混合设置
-
颜色混合
最终RGB = 源RGB × 源Alpha + 目标RGB × (1 - 源Alpha)
-
透明度混合
最终Alpha = 源Alpha × 1 + 目标Alpha × (1 - 源Alpha)
让我来举一个实际例子来说明应用标准混合公式
假设有两个颜色:
- 半透明红色(源颜色):RGBA(1.0, 0.0, 0.0, 0.5)
- 不透明蓝色(目标颜色):RGBA(0.0, 0.0, 1.0, 1.0)
计算过程:
R = 1.0 × 0.5 + 0.0 × (1 - 0.5) = 0.5
G = 0.0 × 0.5 + 0.0 × (1 - 0.5) = 0.0
B = 0.0 × 0.5 + 1.0 × (1 - 0.5) = 0.5
A = 0.5 × 1.0 + 1.0 × (1 - 0.5) = 1.0
最终颜色:
RGBA(0.5, 0.0, 0.5, 1.0),这是一个不透明的紫色。
绘制三角形
//绘制第一个三角形
private func drawOneTriangle(commandEncoder: MTLRenderCommandEncoder?) {
let vertices = [
Vertext(position: [-0.5, -0.5, 0.0, 1.0], color: [1.0, 0.0, 0.0, 0.5]),
Vertext(position: [ 0.5, -0.5, 0.0, 1.0], color: [1.0, 0.0, 0.0, 0.5]),
Vertext(position: [ 0.0, 0.5, 0.0, 1.0], color: [1.0, 0.0, 0.0, 0.5]),
]
let vertexBuffer = device.makeBuffer(bytes: vertices, length: MemoryLayout<Vertext>.stride * vertices.count)
commandEncoder?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
commandEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertices.count)
}
//绘制第二个三角形
private func drawAnotherTriangle(commandEncoder: MTLRenderCommandEncoder?) {
let vertices = [
Vertext(position: [ 0.0, -0.5, 0.0, 1.0], color: [0.0, 0.0, 1.0, 0.5]),
Vertext(position: [ 1.0, -0.5, 0.0, 1.0], color: [0.0, 0.0, 1.0, 0.5]),
Vertext(position: [ 0.5, 0.5, 0.0, 1.0], color: [0.0, 0.0, 1.0, 0.5]),
]
let vertexBuffer = device.makeBuffer(bytes: vertices, length: MemoryLayout<Vertext>.stride * vertices.count)
commandEncoder?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
commandEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertices.count)
}
Shader 函数
vertex RasterizerData vertexShader(uint vertexID [[vertex_id]],
constant Vertex *vertices [[buffer(0)]]) {
RasterizerData out;
out.position = vertices[vertexID].position;
out.color = vertices[vertexID].color;
return out;
}
fragment float4 fragmentShader(RasterizerData in [[stage_in]]) {
return in.color;
[}](url)
效果
- 混合前
- 混合后
结语
除了深度测试,我们还可以使用混合来调整物体的可见性,它通过将源颜色和目标颜色按照特定的混合方程进行计算,实现半透明效果、粒子特效、材质叠加等视觉效果。
欢迎大家点赞、收藏。谢谢大家!
本项目已开源。