Swift+Metalで発光エフェクト

#Swift+Metalで発光エフェクト
Swift+Metalで発光エフェクト（Unityで言う所のBloom）をお手軽に表現する手法。
完成形↓
![IMG_5725.PNG](https://qiita-image-store.s3.amazonaws.com/0/214313/dc813f18-37e5-001c-6a69-f73e930ec60c.png)

##発光エフェクトをする手順
1.通常のレンダリングをする。
![IMG_5728.PNG](https://qiita-image-store.s3.amazonaws.com/0/214313/b14ef0b5-60c5-87da-b620-80b2cdebad3c.png)

2.発光させたい部分と色のマスクを作る。
![IMG_5726.PNG](https://qiita-image-store.s3.amazonaws.com/0/214313/2dc4d908-8fac-5066-4bd3-0b77347b9e7f.png)

3.マスクにガウスボカシをかける。
![IMG_5727.PNG](https://qiita-image-store.s3.amazonaws.com/0/214313/e181d1d5-9f99-6651-2223-e5263c4c3773.png)

4.通常のレンダリングにボカした画像を足し合わせる。
![IMG_5725.PNG](https://qiita-image-store.s3.amazonaws.com/0/214313/0bea7895-f588-6714-9eab-ff5f86ff07de.png)


##発光エフェクトを表現するプログラム
全てのプログラムはココ↓
https://github.com/fuziki/metalBloom

・手順1&2
レンダリングテクスチャを2枚用意する。
（通常のレンダリングとマスク用）

```swift:MetalEz.swift 
func makeRenderTexture() -> MTLTexture {
        let texDesc = MTLTextureDescriptor()
        texDesc.width =  (mtkView.currentDrawable?.texture.width)!
        texDesc.height =  (mtkView.currentDrawable?.texture.height)!
        texDesc.depth = 1
        texDesc.textureType = MTLTextureType.type2D
        
        texDesc.usage = [MTLTextureUsage.renderTarget, MTLTextureUsage.shaderRead]
        texDesc.storageMode = .private
        texDesc.pixelFormat = .bgra8Unorm
        
        texDesc.usage = .unknown
        
        return device.makeTexture(descriptor: texDesc)!
}
```

```MetalEz.swift 
        viewRenderTexture = makeRenderTexture()        
        bloomRenderTexture = makeRenderTexture()
```

フラグメントシェーダーで2つのテクスチャにそれぞれ、レンダリングとマスクを書き込む。

```cpp:Shaders.metal
struct FragmentOut {
    half4 color0 [[ color(0) ]];
    half4 color1 [[ color(1) ]];
};
fragment FragmentOut fragmentShader(VertexOut in [[stage_in]],
                                    texture2d<half>  diffuseTexture [[ texture(0) ]],
                                    texture2d<half>  bloomTexture [[ texture(1) ]])
{
    constexpr sampler defaultSampler;
    FragmentOut out;
    float lt = saturate(dot(in.normal, lightDirection));
    if (lt < 0.5) lt = 0.5;
    half4 color =  half4(diffuseTexture.sample(defaultSampler, float2(in.texcoord))*lt);
    out.color0 = color;
    if (is_null_texture(bloomTexture)) {
        out.color1 = half4(0,0,0,0);
    } else {
        out.color1 = bloomTexture.sample(defaultSampler, float2(in.texcoord));
    }
    return out;
}
```

・手順3
マスクにガウスボカシをする。MetalPerformanceShaderを使ってボカす。
書き込み元のテクスチャと書き込み先のテクスチャは同一

```MetalEz.swift 
            var myTexture: MTLTexture? = bloomRenderTexture
            let kernel = MPSImageGaussianBlur(device: device, sigma: 20.0)
            kernel.encode(commandBuffer: commandBuffer!,
                          inPlaceTexture: &myTexture!, fallbackCopyAllocator: nil)
```


・手順4
通常のレンダリングとエフェクトを加算合成する。
手順3と同じく、MetalPerformanceShaderを使う。
計算結果は'view.currentDrawable?.texture)!'に書き込む。
(MTLTextureは=演算子を使って渡せないので注意)

```MetalEz.swift 
            let addKernel = MPSImageAdd(device: device)
            addKernel.encode(commandBuffer: commandBuffer!,
                             primaryTexture: viewRenderTexture,
                             secondaryTexture: bloomRenderTexture,
                             destinationTexture: (view.currentDrawable?.texture)!)
```

#おわりに
MTLTextureまわりが割とTipsがあるので、需要があれば、そのうちまとめます（多分）