Google Homeで変身する

この記事は、Tech KAYAC Advent Calendar 2017 の21日目の記事です。

こんにちは!17新卒HTMLファイ部の入江です。

技術の無駄遣いをモットーに日々最新技術を追っています。今回は今流行りのAIスピーカーGoogle Homeを使って変身できるようにしてみました。

できたもの

まずはこちらご覧ください。


Google Home と Firebase Realtime databaseを連携して顔を変える

使った技術

  • Dialogflow
  • firebase
  • Fusion 360

仕様

Google Homeに「変身〇〇」というと〇〇の部分をfirebaseのrealtime databaseに保存するAgentをつくる。realtime databaseをブラウザから監視し、変わった値に紐づいた画像を画面(iPad)に表示する。

f:id:kiyoshidainagon:20171220121825p:plain
システム図

DialogflowでAgentをつくる

Entitiesの設定

まずEntityを作ります。 「変身〇〇」の〇〇の部分に当たるやつですね。

f:id:kiyoshidainagon:20171220142709p:plain
Entity

右側が特定の単語。左側が特定の単語に対しての類義語になります。

Intentsの設定

続いてintentの設定していきます。 「変身〇〇」という単語を理解できるようにしてあげます。

f:id:kiyoshidainagon:20171220142724p:plain
Intent

User saysで認識してもらいたい文言を追加します。今回は変身したいので、変換が間違っても問題ないように「変身ブルー」と「返信レッド」を追加しました。レッド、ブルーの単語を選択して、先ほど作ったEntityを設定してあげると、黄色くなります。

FulfillmentのUse webhookにチェックを入れます。

Integrationの設定

f:id:kiyoshidainagon:20171220125932p:plain

一番上のGoogle AssistantのINTEGRATION SETTNIGSをクリックすると上図のようなポップアップが出ます。最近UI変わったみたいですね。 Additional triggering intentsの欄に先ほど作ったintentを設定して、TESTを押して問題ないようだったら、MANAGE ASSISTANT APPを押します。

Fulfillmentの設定

firebase cloud funcitonの作成

firebaseのcloud functionをローカル環境で作成します。手順としては以下です。

  1. yarn global add firebase-tools
  2. firebase login
  3. firebase int functions
  4. Select a default Firebase project for this directory: (Use arrow keys)と聞かれるので、Dialog flowで作成中のAgentを選択する
  5. JavaScript と TypeScriptどっちにするかとか聞かれますが、そこはお好みで
  6. できたらfirebase deploy --only functionsでデプロイする

今回書いたfunctionは↓になります。内容はFullfillmentのInline Editor内に書いてあるサンプルスクリプトに、FirebaseのRealtime Databaseにデータを追加するプログラムを書き足しただけです。

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const DialogflowApp = require('actions-on-google').DialogflowApp;

admin.initializeApp(functions.config().firebase);

exports.dialogflowWithRealtimeDetabase = functions.https.onRequest((request, response) => {
  if (!request.body.result) {
    console.log('Invalid Request');
    return response.status(400).end('Invalid Webhook Request (expecting v1 or v2 webhook request)');
  }

  let action = request.body.result.action;
  const parameters = request.body.result.parameters; 
  const requestSource = (request.body.originalRequest) ? request.body.originalRequest.source : undefined;
  const path = "/faceName";

  // realtime databaseにデータを突っ込む 
  admin.database().ref(path).set(parameters.change_name);

  const googleAssistantRequest = 'google'; 
  const app = new DialogflowApp({request: request, response: response});

  const actionHandlers = {
    'input.welcome': () => {
      if (requestSource === googleAssistantRequest) {
        sendGoogleResponse('Hello, Welcome to my Dialogflow agent!'); 
      } else {
        sendResponse('Hello, Welcome to my Dialogflow agent!'); 
      }
    },
    'input.unknown': () => {
      if (requestSource === googleAssistantRequest) {
        sendGoogleResponse('I\'m having trouble, can you try that again?'); 
      } else {
        sendResponse('I\'m having trouble, can you try that again?'); 
      }
    },
    'default': () => {
      if (requestSource === googleAssistantRequest) {
        let responseToUser = {
          speech: 'シャキーン', 
          text: 'シャキーン'
        };
        sendGoogleResponse(responseToUser);
      } else {
        let responseToUser = {
          speech: 'シャキーン', 
          text: `シャキーン`
        };
        sendResponse(responseToUser);
      }
    }
  }

  if (!actionHandlers[action]) {
    action = 'default';
  }

  actionHandlers[action]();

  function sendGoogleResponse (responseToUser) {
    if (typeof responseToUser === 'string') {
      app.ask(responseToUser); 
    } else {
      let googleResponse = app.buildRichResponse().addSimpleResponse({
        speech: responseToUser.speech || responseToUser.displayText,
        displayText: responseToUser.displayText || responseToUser.speech
      });
      if (responseToUser.googleRichResponse) {
        googleResponse = responseToUser.googleRichResponse;
      }
      if (responseToUser.googleOutputContexts) {
        app.setContext(...responseToUser.googleOutputContexts);
      }
      app.ask(googleResponse); 
    }
  }
  function sendResponse (responseToUser) {
    if (typeof responseToUser === 'string') {
      let responseJson = {};
      responseJson.speech = responseToUser; 
      responseJson.displayText = responseToUser; 
      response.json(responseJson); 
    } else {
      let responseJson = {};
      responseJson.speech = responseToUser.speech || responseToUser.displayText;
      responseJson.displayText = responseToUser.displayText || responseToUser.speech;
      responseJson.data = responseToUser.data;
      responseJson.contextOut = responseToUser.outputContexts;
      response.json(responseJson); 
    }
  }
});

DialogflowのFulfillmentの設定

  1. WebhookをENABLEDにします。
  2. URLに先ほどdeployしたcloud funcitonのURLを指定します。

フロント側でfirebase realtime databaseを監視する

単純にデータを監視するだけなんで、ちょっとの記述で実装できます。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <style>
            body {
              width: 100vw;
              height: 100vh;
              background-color: #000;
              background-repeat: no-repeat;
              background-position: center bottom;
              color: #fff;
            }
      </style>
    </head>
    <body>
        <p id="face-name">ホゲホゲ</p>

        <script src="https://www.gstatic.com/firebasejs/4.2.0/firebase.js"></script>
        <script>
            // Initialize Firebase
            var config = {
                apiKey: "<API_KEY>",
                authDomain: "<PROJECT_ID>.firebaseapp.com",
                databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
                storageBucket: "<BUCKET>.appspot.com",
                messagingSenderId: "<SENDER_ID>",
            };
            firebase.initializeApp(config);

            var db = firebase.database();
            var faceType = db.ref('faceName'); //取得したいデータのパスを指定
            faceType.on('value', function(snapshot) { 
                const value = snapshot.val(); //faceNameに入ってる値を取得
                document.getElementById("face-name").innerText = value;
                document.body.style.backgroundImage = 'url(img/' + value + '.png)';
            });
        </script>
    </body>
</html>

ウェアラブル化する

micro USBで給電してるし、戦隊モノのベルトのバックルに見えなくもないので、ウェアラブル化してみます。

ThingiverseというサイトからGoogle Home Miniのマウントをダウンロードしてきます。

今回ダウンロードしたものは↓ Google Home Mini Wall Mount by tilmansp - Thingiverse

続いて、先ほどダウンロードしたマウントに合うように、ベルトにかけれるフックをモデリングしていきます。 モデリングにはFusion 360を使いました。

f:id:kiyoshidainagon:20171219213531p:plain

Fusino 360でモデリング

このモデルを1個にまとめて3Dプリントで出力するのはうまく行かなそうなので、フックの部分とマウントの部分の2パーツに分けて出力し、出力後に合体させます。 接着剤はアクリサンデーがオススメです。http://www.acrysunday.co.jp/products/article/

合体させたマウントはこんな感じになります。 f:id:kiyoshidainagon:20171219214420j:plain

念のため、フックの部分もあげておきます。ベルトにつけたい方はご気軽にダウンロードしてください。 www.thingiverse.com

モバイルバッテリーも同様にフック付きのケースを用意して、完成したのがこちらになります。

f:id:kiyoshidainagon:20171220173834p:plain
Google Home 搭載ベルト

まとめ

いかがだったでしょうか。firebaseを使うことで、Google Homeでできることが格段に広がる気がしますね。

面白法人カヤックではAIスピーカーを使って家を便利にするだけじゃ飽き足らない人を募集しています。

www.kayac.com www.kayac.com

明日は先日の合宿で新人王を獲ったcommojun.comです。

カマクラシェーダーズついにリリース!

VA(ビジュアルアーツ)チーム リードアーティストのマツです。アートとテクノロジーを専門とするVA チームが、先日発表したカマクラシェーダーズをリリースしました。githubunitypackage による配布となります。

カマクラシェーダーズ とは

カマクラシェーダーズは、アニメ、ペイント、水彩、ハッチのようなアート表現をサポートするための Unity リアルタイム用 シェーダー集です。3D はもちろん 2D, UI にも活用できます。モバイル対応です。

  • Kamakura
  • Kamakura-Hair
  • Kamakura-2D

今回はカマクラシェーダーズのひとつ Kamakura シェーダーを例に配布用キャラクター Dreamy(ドリーミィ)のコスチュームでその特徴をみてみましょう。

Filter

HSB色空間(色相 : Hue、彩度 : Saturation、明度 : Brightness) 、コントラスト : Contrast Level によるフィルターです。Diffuse Color だけでは表現できない色調整が可能です。

Filter Hue : フィルター 色相

f:id:mu4ma6:20171213144907p:plain

Filter Saturation : フィルター 彩度

f:id:mu4ma6:20171213145033p:plain

Filter Brightness : フィルター 明度

f:id:mu4ma6:20171213145054p:plain

Filter Contrast Level : フィルター コントラスト

f:id:mu4ma6:20171213145118p:plain

Specular

スペキュラの色、スペキュラマスクで適用エリア、パワー、強度、滑らかさ を調整が可能です。

Specular Color : スペキュラ 色

f:id:mu4ma6:20171213145136p:plain

Specular Mask : スペキュラ マスク

f:id:mu4ma6:20171213145233p:plain

スペキュラの場所を限定することができます。

Specular Power : スペキュラ パワー

f:id:mu4ma6:20171213145251p:plain

Specular Intensity : スペキュラ 強度

f:id:mu4ma6:20171213145316p:plain

Specular Smoothness : スペキュラ 滑らかさ

f:id:mu4ma6:20171213145331p:plain

Light Ramp Texture

ランプテクスチャエディタを用意しました。 ramp-asset

f:id:mu4ma6:20171213145402p:plain

[Create]_[Ramp Texture] で新規ランプアセットを作成します。

f:id:mu4ma6:20171213145500p:plain

Texture Width でテクスチャ幅(px)を指定します。 Gradient Presets は、[+][-]ボタンで追加、ドラッグすることで順番を変えられます。

f:id:mu4ma6:20171213145512p:plain

Gradient Presets をクリックすると Gradient を編集することができます。

f:id:mu4ma6:20171213145526p:plain

複数 Gradient を追加します。

f:id:mu4ma6:20171213145537p:plain

ランプテクスチャが完成。

Light Ramp

ランプテクスチャエディタで制作したテクスチャで光と影のシャープ、ぼかしを調整します。別途 PhotoShop 等でつくったテクスチャでも大丈夫です。 光と影の適用エリアをVertexColor(G Channel)で指定可能です。

Light Ramp Preset : ライトランプ プリセット

f:id:mu4ma6:20171213145603p:plain

用意した Light Ramp Texture で効果を自由に変えることができます。

Light Ramp Offset : ライトランプ オフセット

f:id:mu4ma6:20171213145617p:plain

Light Ramp Shift Using VertexColor(G) : ライトランプ 影濃淡制御

f:id:mu4ma6:20171213145636p:plain

Maya VertexColorChannelSelector プラグインで、G チャンネルを使用して影の濃淡を指定します。 ヒップ上部 : 1、 ヒップ下部 : 0、それ以外 : 0.5 と VertexColor(G) をペイント

f:id:mu4ma6:20171213145654p:plain

Light Ramp Offset が -1 1 間で表現するのを VertexColor(G) 0 1 間でペイントして表現します。VertexColor(G)を使用する場合は、Light Ramp Offset を 0 で調整することをおすすめします。 VertexColor(G) を使用することで部位ごとに影の濃淡を表現可能です。ライティングに関わらずVertexColor(G) 0 ペイントすることで必ず影を入れることができ、VertexColor(G) 1 ペイントで影を入れない表現が可能です。

Shadow

影の色、強度、テクスチャによる調整が可能です。

Shadow Color : 影 色

f:id:mu4ma6:20171213145710p:plain

影を指定の色で塗りつぶします。

Shadow Intensity : 影 強度

f:id:mu4ma6:20171213145729p:plain

Shadow Intensity : 1 で塗りつぶし、0 に近づくことで影を透過させ Diffuse Color(Texture) に近づけることができます。

Shadow Texture : 影 テクスチャ

f:id:mu4ma6:20171213145806p:plain f:id:mu4ma6:20171213145820p:plain

Shadow Color と Shadow Intensity の他に Shadow Texture を使用することでより自由に表現できます。

Shadow Texture Use Filter : 影 テクスチャ フィルター

f:id:mu4ma6:20171213145833p:plain

フィルターを使用時、影テクスチャにもフィルターを適用可能です。

Ambient

環境光の色と強度の調整が可能です。

Ambient Color : アンビエント 色

f:id:mu4ma6:20171213145847p:plain

黄色とピンクの環境光の例

Ambient Intensity : アンビエント 強度

f:id:mu4ma6:20171213145916p:plain

ピンクの環境光の強度を変えた例

Unity's Ambient Intensity : アンビエント 強度 (Unity)

f:id:mu4ma6:20171213145943p:plain f:id:mu4ma6:20171213145957p:plain

Unity の環境光の強度を制御します。

Outline

アウトラインの色、太さ、VertexColor(R Channel)による入り抜き調整が可能です。

Outline Color : アウトライン 色

f:id:mu4ma6:20171213150015p:plain

赤色と黄色のアウトライン色の例

Outline Blend Color Texture Value : アウトライン ブレンドカラーテクスチャ

f:id:mu4ma6:20171213150127p:plain

テクスチャ色とアウトライン色のブレンドを調整します。

Outline Thickness : アウトライン 太さ

f:id:mu4ma6:20171213150145p:plain

Outline Adapt to Camera Distance Value : アウトライン カメラ距離に適用

f:id:mu4ma6:20171213150202p:plain

カメラの距離に関わらずアウトラインの太さを一定にする調整が可能です。

Outline Enable InnerSide Outline : アウトライン インナーサイド

f:id:mu4ma6:20171213150214p:plain

インナーサイドのアウトラインをオンオフが可能です。

Outline Adjust Using VertexColor(R) : アウトライン 入り抜き

f:id:mu4ma6:20171213150233p:plain

Maya VertexColorChannelSelector プラグインで、R チャンネルを使用してアウトラインの入り抜き調整を指定します。 アウトラインをそのまま表示したいときは赤、細く表現したいときは、明度を下げてペイントします。 ペイントが完了したら Combine して、FBX ファイルをエクスポートします。 ペイントを画像エクスポート/インポートも可能です。画像から頂点カラーを再現することも可能です。

f:id:mu4ma6:20171213150251p:plain

Hatch

Hatch Texture : ハッチ テクスチャ

f:id:mu4ma6:20171213150330p:plain

Hatch in Screen Space : ハッチ スクリーン スペース

f:id:mu4ma6:20171213150343p:plain

Hatch Level : ハッチ レベル

f:id:mu4ma6:20171213150406p:plain f:id:mu4ma6:20171213150418p:plain

ハッチングテクスチャ RGB それぞれのチャンネルで調整が可能です。 R : Hatch Level 1 G : Hatch Level 2 B : Hatch Level 3

Hatch Mask : ハッチ マスク

f:id:mu4ma6:20171213150439p:plain

ハッチマスクテクスチャでハッチの適用エリアを調整が可能です。

Hatch Threshold : ハッチ しきい値

f:id:mu4ma6:20171213150453p:plain

Hatch Intensity : ハッチ 強度

f:id:mu4ma6:20171213150505p:plain

Hatch Range Stretch : ハッチ レンジ ストレッチ

f:id:mu4ma6:20171213150522p:plain Hatch Threshold と組み合わせて使用します。

Hatch Rotation : ハッチ 回転

f:id:mu4ma6:20171213150534p:plain

Rim

リムのブレンドモードは、Additive, Normal の2つが用意されています。 リムノイズテクスチャでリムの細かな形状表現が可能です。

Rim Color & Blending Mode : リム 色、ブレンドモード

f:id:mu4ma6:20171213150557p:plain

ブレンドモード Additive は、Diffuse Color(Texture) に加算します。 Normal は、Color と Intensity そのままの色で表現します。

Rim Size : リム サイズ

f:id:mu4ma6:20171213150616p:plain

Rim Intensity : リム 強度

f:id:mu4ma6:20171213150629p:plain

Rim Softness : リム 柔らかさ

f:id:mu4ma6:20171213150642p:plain

Rim Noise : リム ノイズ

f:id:mu4ma6:20171213150653p:plain

Emission

Emission Map : エミッション マップ

f:id:mu4ma6:20171213150708p:plain

エミッションマップで適用エリアの調整が可能です。

Emission Color : エミッション 色

f:id:mu4ma6:20171213150720p:plain

青色とオレンジ色のエミッション色の例

Emission Power : エミッション パワー

f:id:mu4ma6:20171213150730p:plain

Cube Color

Rim, Ambient に Cube Color ( Left, Right, Front, Back, Top, Down ) を指定できます。 cube-color

Cube Color Use For Rim : キューブカラー リム

f:id:mu4ma6:20171213150741p:plain

Cube Color Use For Ambient : キューブカラー アンビエント

f:id:handlename:20171220140844p:plain f:id:mu4ma6:20171213150753p:plain

視認しやすいように Diffuse Texture を外してみました。

Stencil

ステンシルは、描画のエリアを調整します。例えば、キャラクターの目が髪にかくれないように使用します。

f:id:mu4ma6:20171213150835p:plain

Point Light & Spot Light

Directional Light の他に Point Light, Spot Light に対応しています。

f:id:mu4ma6:20171213150852p:plain

Local Light

Unity の Light GameObject を使用しない Local Light コンポーネントです。 local-light

f:id:mu4ma6:20171213150903p:plain

Local Light コンポーネントを GameObject に使用することで、それぞれにライト効果が可能です。

f:id:mu4ma6:20171213150913p:plain

Dreamy_Anime と Head と Costume にそれぞれ Local Light を使用している例です。Child が優先になります。絵作りのためにうその光の演出を行います。キャラクターだけでなく建物などに使用することで効果的な演出が楽しめます。

GI ( Global Illumination )

グローバル イルミネーション対応で、間接光の影響を表現可能です。 f:id:mu4ma6:20171213150926p:plain

Unity の Light Probe Group を使用します。壁のマテリアルは Standard シェーダーの Emission を使用しています。

f:id:mu4ma6:20171213150937p:plain

Unity の Ambient Intensity を設定します。

f:id:mu4ma6:20171213150948p:plain

間接光に照らされた表現ができます。

カマクラシェーダーズ ライセンス

カマクラシェーダーズは無料です。

シェーダー : MITライセンス
キャラクター : CC BY-SA 4.0

カマクラシェーダーズ ダウンロード

github
unitypackage

メンバー募集

f:id:mu4ma6:20171213151018p:plain

VA(ビジュアルアーツ)チームは、アーティストテクニカルアーティストを募集しています。 アートでテクノロジーに挑戦したいひと、テクノロジーでアートにインスピレーションを与えたいひと のご応募まってます。ブログをみた!VAチームに入りたいとお伝えください。