LoginSignup
12
12

More than 5 years have passed since last update.

UnityでLive2Dにモザイクしてみた

Posted at

Live2D Advent Calendar 2015の10日目の記事です!

前に「WebGLでエロゲー風に部分モザイクしてみた」をQiitaに書いたら、エロゲ界隈の人が「Unityでもモザイクしたいんじゃ〜!」と言ってたのでやってみました。
今回はステンシルバッファ(マスク処理)なしの簡単な方法で実装しています。
001.gif
ちなみにLive2DはアダルトコンテンツもOKと書いてあったりしますw
Live2D Manual - Q. Live2Dを使って、18禁などのアダルトコンテンツを作ることはNGですか?

開発環境

・Live2D_SDK_Unity_2.1.00_1_jpのMotionプロジェクト
・Unity 5.2.0(Macでの動作確認のみ)

プログラムの解説

今回の実装はLive2Dモデルを2回描画し、2回目の描画だけにモザイクシェーダーをかけています。
できるだけカメラを少なくしたかったので、RenderTexture(フレームバッファ)を切り替えて描画しています。
カメラはMainCameraとSubCameraの2つのみです。

・1回目の描画 = RenderTexture1
002.png
・2回目の描画 = RenderTexture2(モザイクシェーダーを適用)
003.png

あとはRenderTexture1と2の描画結果をブレンドします。
この実装方法を教えて貰った時「なんて無駄のない天才的な発想だっ!」と思いました。
ステンシルバッファとかLive2Dモデルの頂点情報の取得とかいらないですね。

実装の仕方

1)MotionプロジェクトからLive2DのGameObjectは削除
今回はOnRenderObject()でなく、OnPostRender()を使うのでカメラからLive2Dの描画をします。
スクリプトについては後述します。

2)モザイクとRenderTextureブレンドするシェーダーをインポート
モザイクのシェーダーは、テラシュールさんのブログからお借りしました。

テラシュールブログ - 【Unity】uGUIのシェーダーを改造してシェーダーを練習…のススメ
 ※ 14行目のfloat _Range = 256 → 30〜50に変更して使ってます。

2つのRenderTextureをブレンドするシェーダーは公式のものを参考にしました。

Unity DOCUMENTATION - ShaderLab:Texture Combiners
「2つのテクスチャのアルファブレンディング」そのままのコードだと透明部分が抜けなかったので、以下のようにBlend係数を1つ追記しました。

blendTexture.shader
Shader "Custom/blendTexture" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _BlendTex ("Alpha Blended (RGBA) ", 2D) = "white" {}
    }
    SubShader {
        Pass {
            Blend SrcAlpha OneMinusSrcAlpha // Alpha blending
            // Apply base texture
            SetTexture [_MainTex] {
                combine texture
            }
            // Blend in the alpha texture using the lerp operator
            SetTexture [_BlendTex] {
                combine texture lerp (texture) previous
            }
        }
    }
}

3)Sub Cameraを作成し、以下のように設定
OrthographicでOrthographicSize=1(Main Cameraも同様)
スクリーンショット 0027-12-08 13.21.04.png

4)Sub Cameraに以下のスクリプトをつける
ここでは体パーツのみ描画するかしないかを設定してます。

SimpleModel.cs
using UnityEngine;
using System;
using System.Collections;
using live2d;

[ExecuteInEditMode]
public class SimpleModel : MonoBehaviour
{
    public TextAsset mocFile ;
    public Texture2D[] textureFiles ;

    private Live2DModelUnity live2DModel;
    private Matrix4x4 live2DCanvasPos;
    private Live2DMotion motion;
    private MotionQueueManager motionMgr;
    public TextAsset motionFile;

    private Material mat_mozaiku;   // モザイクエフェクト
    private Material mat_blend;     // renderTex1と2をblendする
    private RenderTexture renderTex1;
    private RenderTexture renderTex2;
    private GameObject Live2D_Quad;
    private Renderer Quad_render;

    void Start ()
    {

        // モザイクエフェクト用
        mat_mozaiku = new Material(Shader.Find("UI/Nega"));
        // renderTex1と2をblend用
        mat_blend = new Material(Shader.Find("Custom/blendTexture"));

        // RenderTextureを生成
        renderTex1 = new RenderTexture(Screen.width, Screen.height, 16/*depth*/ , RenderTextureFormat.ARGB32 );
        renderTex1.Create();
        renderTex2 = new RenderTexture(Screen.width, Screen.height, 16/*depth*/ , RenderTextureFormat.ARGB32 );
        renderTex2.Create();

        // Quadを生成(RenderTexture描画用)
        Live2D_Quad = GameObject.CreatePrimitive(PrimitiveType.Quad);

        // Live2Dモデルの座標とスケールをセット
        Live2D_Quad.transform.position =  new Vector3(0.0f, 0.0f, 0.0f);
        Live2D_Quad.transform.localScale = new Vector3(2.0f, 2.0f, 2.0f);

        // QuadのMaterialと名前をセット
        Quad_render = Live2D_Quad.GetComponent<Renderer>();
        Quad_render.material = mat_blend;
        Quad_render.name = "Live2D_Quad";


        if (live2DModel != null) return;
        // Live2D初期化
        Live2D.init();

        // mocファイルのロード
        live2DModel = Live2DModelUnity.loadModel(mocFile.bytes);
        // テクスチャをセット
        for (int i = 0; i < textureFiles.Length; i++)
        {
            live2DModel.setTexture(i, textureFiles[i]);
        }

         // Live2Dの描画座標
        float modelWidth = live2DModel.getCanvasWidth();
        live2DCanvasPos = Matrix4x4.Ortho(0, modelWidth, modelWidth, 0, -50.0f, 50.0f);

        // モーション設定
        motionMgr = new MotionQueueManager();
        motion = Live2DMotion.loadMotion(motionFile.bytes);
    }


    void Update()
    {
        if (live2DModel == null) return;
        // OnPostRenderだとカメラ位置の影響を受けないようにコメント
//        live2DModel.setMatrix(transform.localToWorldMatrix * live2DCanvasPos);
        live2DModel.setMatrix(live2DCanvasPos);

        if (!Application.isPlaying)
        {
            live2DModel.update();
            return;
        }

        // モーション終了してたら再生
        if (motionMgr.isFinished())
        {
            motionMgr.startMotion(motion);
        }

        // Live2Dのモーションを更新
        motionMgr.updateParam(live2DModel);
        // Live2Dのパラメーター更新
        live2DModel.update();
    }


    void OnPostRender()
    {
        if (live2DModel == null) return;

        //***** アクティブのフレームバッファを切り替える *****//
        RenderTexture.active = renderTex1;
        // フレームバッファをクリア
        GL.Clear (true, true, Color.clear);

        // 全てのパーツの透明度調整(0.0 → 非表示、 1.0 → 表示)
        var partList = live2DModel.getModelImpl().getPartsDataList();
        foreach (var item in partList) {
            live2DModel.setPartsOpacity(item.getPartsDataID().ToString(), 1.0f);
        }
        // 体のみ透明にする
        live2DModel.setPartsOpacity("PARTS_01_BODY_001", 0.0f);
        // Live2Dを描画
        live2DModel.draw();


        //***** アクティブのフレームバッファを切り替える *****//
        RenderTexture.active = renderTex2;
        // フレームバッファをクリア
        GL.Clear (true, true, Color.clear);
        // 全てのパーツの透明度調整(0.0 → 非表示、 1.0 → 表示)
        foreach (var item in partList) {
            live2DModel.setPartsOpacity(item.getPartsDataID().ToString(), 0.0f);
        }
        // 体のみ表示する
        live2DModel.setPartsOpacity("PARTS_01_BODY_001", 1.0f);
        // Live2Dを描画
        live2DModel.draw();

        //***** アクティブのフレームバッファをデフォルトに戻す *****//
        RenderTexture.active = null;

        // レンダーテクスチャをセットしブレンド
        mat_blend.SetTexture("_MainTex", renderTex1);
        mat_blend.SetTexture("_BlendTex", renderTex2);
    }

    // ポストプロセスエフェクト用の便利な関数
    void OnRenderImage( RenderTexture src, RenderTexture dest)
    {
        // 第1引数にエフェクトかけて、第2引数でその結果を返す。第3引数はエフェクト
        Graphics.Blit(renderTex2, renderTex2, mat_mozaiku);
    }
}

これにモデルとテクスチャ、モーションファイルをアタッチします
004.png

5)Gameビューのサイズを変えるとモデルのアスペクト比がずれるので修正
005.gif

アスペクト比を固定するスクリプトをSubCameraにつけるとこの問題が解決です。
・テラシュールブログ - Unityでアスペクト比率を固定する

テラシュールブログに感謝しつつ、これで一応完成しました!

最後に

今回はチャレンジ的なテスト実装なので、ゲームなどで使う場合はもう少し修正が必要になります。
たぶん、フレームバッファ切り替え方式でなく複数カメラに複数Live2D表示してブレンドする方が良いかもです。
また、Windowsでは以下のようになっていまいちなかんじに(なぜかMacだとちゃんと動く)
006.png

需要があれば修正してまたブログに追記しようと思います。

明日はnoshipuさん、またよろしくお願いします〜!

12
12
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
12
12