読者です 読者をやめる 読者になる 読者になる

しゅみぷろ

プログラミングとか

雨の日のカメラを作る

はじめに

カメラのレンズに雨が付着した感を出してみました。

ささっと作ったのですが、ちょっといい感じになったので自分で驚いてます。 雨粒が付着した部分は背景の映り込みが歪むようになっています。

Unity Standerd Assetに含まれるガラスシェーダーを使いまわしたので、10分くらいで実装することが出来ました。

実装方法

UnityTexturePaintを使います。

github.com

まずは以下の画像のように Assets -> Import Package -> Effectsを選択して、Glass Refractionをインポートします。 Glass Refractionでは、GrabPassで取得したテクスチャと法線マップを使い、写り込んだ背景を歪ませる処理が行われています。 このシェーダーをそのまま利用し、透明な状態で法線情報だけを更新していき、水滴が付着したように見せるという寸法です。

f:id:es_program:20170403212428p:plain:w600

次に、カメラ正面にQuadを置き、Quadのマテリアルを先程インポートしたGlassRefractiveに変更し、テクスチャを真っ白なものに差し替えます。QuadのコンポーネントにDynamicCanvasとHeightFluidをアタッチしたら準備は完了です。

f:id:es_program:20170403213207p:plain:w600

あとはTexturePaintを使って、Quadの適用な位置にHeight(液体の付着量)情報をペイントしてやります。

具体的には

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Es.TexturePaint;

public class RainPainter : MonoBehaviour
{
    [SerializeField]
    private DynamicCanvas canvas;

    [SerializeField]
    private PaintBrush brush;

    [SerializeField]
    private float minSize;

    [SerializeField]
    private float maxSize;

    [SerializeField, Range(0, 100)]
    private float aboutCallPercentOnFrame = 10;

    private void OnWillRenderObject()
    {
        if(Random.Range(0f, 100f) > aboutCallPercentOnFrame)
            return;

        for(int i=Random.Range(1, 10);i>0;--i){
            brush.Scale = Random.Range(minSize, maxSize);
            canvas.PaintUVDirect(brush, new Vector2(Random.Range(0f, 1f), Random.Range(0f, 1f)));
        }
    }
}

こんなスクリプトを書いてQuadに取り付けて、BrushTextureとBrushHeightTextureにこんな感じの画像を与えてやります(透過済みの画像です)。

f:id:es_program:20170403213616p:plain:w200

サンプルの動画では設定はこんな感じに。

f:id:es_program:20170403213745p:plain:w400

あとは実行すれば、適当な位置に水滴がついたような効果が断続的に出現します。

最後に

1粒1粒の水滴がつくたびに描画命令を発行するため、今回のままではパフォーマンス的によろしくありません。 改善する方法としては、水滴がたくさん付いているようなテクスチャを入力として、1回で複数の雨粒を書き込んでやる、などが挙げられます。