Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What is going on with this article?

More than 1 year has passed since last update.

@fuziki

Swift+Metalで発光エフェクト

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

8
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
8
Help us understand the problem. What is going on with this article?