LoginSignup
14
8

More than 3 years have passed since last update.

Swift+Metalで発光エフェクト

Last updated at Posted at 2018-01-11

Swift+Metalで発光エフェクト

Swift+Metalで発光エフェクト(Unityで言う所のBloom)をお手軽に表現する手法。
完成形↓
IMG_5725.PNG

発光エフェクトをする手順

1.通常のレンダリングをする。
IMG_5728.PNG

2.発光させたい部分と色のマスクを作る。
IMG_5726.PNG

3.マスクにガウスボカシをかける。
IMG_5727.PNG

4.通常のレンダリングにボカした画像を足し合わせる。
IMG_5725.PNG

発光エフェクトを表現するプログラム

全てのプログラムはココ↓
https://github.com/fuziki/metalBloom

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

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つのテクスチャにそれぞれ、レンダリングとマスクを書き込む。

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があるので、需要があれば、そのうちまとめます(多分)

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8