KLabGames Creative Blog

KLabは、多くのスマートフォン向けゲームを開発・提供しています。このブログでは、KLabのクリエイターがスマートフォン向けのゲームを制作・運営していく中で培った様々な技術や挑戦とそのノウハウについて紹介していきます。

はじめに

この記事はKLab Creative Advent Calendar 2019 の 5日目の記事になります。

こんにちは。クリエイティブ部所属のUchiと申します。

主に3Dキャラクターや背景プロップ等のモデリングを行っています。

今回はMaya2018に付属しているArnoldレンダラーを使い、既存のモデルをアレンジして、簡単にそれっぽい作品を作るTipsを紹介したいと思います。

私自身そこまでハイエンドなモデルを作る機会がないため、記事を読んでくださる方の中には目に余る部分があるかと思いますが、その点ご容赦いただけたらと思います。またMayaの基礎操作を習得している方向けに記事を作成しており、細かな説明は飛ばしております。

今回の内容

今回はチューブ状にレンダリングしたカーブをモデル形状に合わせて作成し、30分でそれっぽいものを作成します。本日扱うのはこちらのモデルです。

image5
それっぽい作品を

image3

弊社のモバイルオンラインRPG『禍つヴァールハイト』で使用しているラコという名前の、3000ポリゴン程度のローモデルです。こちらをベースにアレンジしてみたいと思います。

モデルのトポロジーを変更する

それでは早速やってみましょう。まず適当なポーズをつけ、元のモデルをハイメッシュのモデルに変更します。今回は静止画を制作するのでウェイトは外しました。

image6

この段階でのメッシュの流れは汚くて構いません。あくまでベースとして使うので、後々の作業にあまり影響しないためです。

その後一度3dsMaxにモデルを持っていき、トポロジツールを使ってメッシュの分割を好きな形に整えます。3dsmaxのトポロジツールはオブジェクトのメッシュのサブディビジョンを手続き的に生成されたパターンに作り直すツールです。

image2

これで大体の形状を保ったままトポロジーを変更することが出来ます。

少し重そうなので、ポリゴン数をプロオプティマイザーモディファイヤで削減したり、メッシュの調整を手動で行います。大体1万以下くらいのポリゴン数で、できるだけガチャガチャした不揃いなメッシュ形状にします。今回は約6500ほどに調整しました。

こちらのモデルをMayaに戻します。作例では中身はありませんが、表面に蔦が這っているような表現にしたいなぁと思ったので、各フェースを分離し個別にスケールをかけます。これで図のような穴の空いたモデルが出来ます。

image4

こちらからエッジを抽出しArnordでレンダリングしたいのですが、普通にエッジ抽出するとMayaが強制終了してしまう危険性があります。そこでトゥーンシェーダを割り当て、ペイントエフェクトからカーブを抽出します。

作成したカーブをセット化し、以下のアトリビュートを追加します。

  • aiCurveShader

    • カーブ自体のシェーダになります。

  • aiCurveWidth

    • レンダリングするカーブの太さを設定します。

  • aiMode

    • レンダリング時の形状を設定します。

  • aiRenderCurve

    • レンダリングの有無になります。

image1

追加すると画像のように追加のアトリビュートの箇所に追加されるかと思います。

ここまでで約15分程度、あとの15分は好きなマテリアル設定およびライティングでレンダリングすれば完成です。今回の作例では30分以内が目標でしたので簡単な金属のマテリアルにスタジオ照明のようなライティングにしましたが、埃(ダスト)を追加したり、もっとライティングに凝ると面白いものが出来るかと思います。

image7

終わりに

いかがでしたでしょうか。元がローポリゴンのモデル、しかも30分足らずのアレンジでも情報量を足してやることでちょっとそれっぽいものに見えます。

ちょっとしたTipsを沢山自分の引き出しに入れることで、より良い作品作りに繋がります。こちらの記事が皆様のお役に立てれば幸いです。

最後まで読んでいただきありがとうございました。

この記事はKLab Creative Advent Calendar 2019の4日目の記事になります。

こんにちは、KLabGames 3D背景デザイナー兼RRRグループのnaoieです。

私はとある機会に岩石や植生等の汎用モデルを大量に取得できないかと悩んでおりました。

解決するためには色々な手段があるとは思いますが、その中でフォトスキャンソフトを利用して、工数を削減しつつ素材を取得出来ないかを試す事にしました。

進めるにあたり様々なフォトスキャンソフトにどのような利点や精度があるかも併せて知りたかったため、弊社”どぶろく制度”を利用し行った検証から、それぞれのソフトの仕様の違いや利点を今回まとめましたので、これからフォトスキャンを行うという方がいらしたら、参考にしていただけますと幸いです。

フォトグラメトリとは?

物体を様々な方向から撮影した写真をコンピューターで解析し
3Dモデルを作成する技術です。

フィギュアのような小さなものから建築や都市といった大きなものまで
3Dモデルにすることができます。

フォトグラメトリのソフトウェア

今回試したソフトは、以下の4つです。

手順や計算時間、アウトプットモデルを比べてみました。

1.Autodesk Recap Photo
2.Agisoft PhotoScan
3.RealityCapture
4.Alice Vision Meshroom

使用したカメラ

IPhone X

  • HDR設定で40~60枚撮影
  • 撮影時間はおよそ2~3分

本来なら一眼レフカメラを利用しますが
スマホのカメラでどの程度の精度が出るか試してみました。

HDR設定で黒つぶれや白飛びを防ぐことで、よりよくモデル形状やテクスチャを抽出することが出来ます。

被写体

  • 弊社近くの公園のベンチ
  • 固形である程度大きく光沢の出ない物質として選定しました。

image12

撮影方法

  1. 被写体を中心に、ぐるりと周回するように撮影する
  2. 撮影した写真に被写体がすべて収まっている

撮影する枚数が多ければ多いほど、撮影するアングルの間隔が細かくなればなるほど認識率*の高い写真になりモデルやテクスチャの品質が高まります。

※認識率とは被写体の形状が、それぞれの写真で合致個所が多いことを指します。

▼撮影アングルイメージ                                        ▼撮影された写真 

Autodesk Recap Photo

▼工程

  1. 写真を取り込む
  2. クラウドサーバーが、自動でモデルとテクスチャを生成

▼特徴

  1. クラウドサーバーが、自動でモデルとテクスチャを生成してくれる
  2. 精度は被写体面に垂直に撮影した写真のピントとその枚数

▼計算時間

  • 約2時間 写真60枚ほど

Agisoft PhotoScan

▼工程

  1. 写真を取り込む
  2. カメラ位置検出
  3. ポイントクラウド生成
  4. テクスチャ生成

▼特徴

  1. ポイントクラウドを生成し、品質を調整できる
  2. レーザースキャンなどの高精細なデータも扱える

※ポイントクラウド(点群データ)
XYZの基本的位置情報や色などの情報を持つ3次元データです。
このデータから、ポリゴンとテクスチャを作成することが出来ます。

▼計算時間

  • 約2時間 写真60枚ほど
  • ポイントクラウドデータの作成に時間かかる
  • Pythonでのバッチ処理が可能

RealityCapture

▼工程

  1. 写真を取り込む
  2. カメラ位置の計算
  3. モデル作成
  4. テクスチャ生成

▼特徴

  1. 操作が簡単で精度が良い
  2. 計算時間も他のソフトに比べ、早い

Alice Vision Meshroom

▼工程

  1. 画面下部の任意のノードを右クリック
  2. Computeをクリック

▼特徴

  • 写真を入れた後の工程が全自動
  • 計算時間も今まで紹介したソフトより高速で処理する
  • オープンソースで無料でモデル/テクスチャまで確認できる

もしこれを手作業した場合

誰が行っても、簡単で品質が高くまとめることが出来ればよいですが
制作者によって多少、品質や作業時間に差が出てしまいます。

汎用的なモデルに時間をかけるより、その世界観固有のモデルや
特殊なフィールドの方に制作時間をかけるべきです。

フォトスキャンに向いているオブジェクト

〇向いている

  • 固形
  • 大きい/太い
  • 光を反射しない
  • 光沢が出ない

✖向いていない

  • 半透明/透明
  • 小さい/細い
  • 反射する
  • 光沢が出る

ゲームモデルの利用に向いているオブジェクト

〇向いている

  • 形が歪なもの
    → 手作業だと制作工数が掛かる為
  • たくさん配置できるもの
    → 汎用的な利用価値があるといろんな案件で使用機会が増える

✖向いていない

  • 使用することに許諾や承認が必要な物
    → 意匠や形状にネームバリューのあるものはトラブルの原因になるかも
  • 形状が幾何学的な物
    → 意図的ならよいが、いくらかノイズが載ってしまう
    モデルから作ったほうが、応用しやすいことがある為
  • 現実世界のどこにあるものか、特定できるもの
    → 特定できてしまうと、ゲームの世界観に没入できない為

自然物が無難だと思いますが、上記”向いている”要素が含まれているものであれば
工数削減と利用機会のある良いモデルになるかと思います。

スキャンモデルをゲームモデルにする

スキャンしたモデルのUVは、ゆがみが極力少ない状態を維持している為
UVアイランド多い状態で生成されています。

image11

これだとデータ量が余計に増してしまう為、ゲームモデルとして最適化を行います。


  1. ポリゴン削減
  2. ゲーム用UV展開
  3. ゲーム用UVへフォトスキャンテクスチャをベイクする

ポリゴン削減

任意のDCCでポリゴン削減を行います。

以下はHoudiniの”PolyReduce”を利用したものです。

image10

ゲーム用UVへテクスチャをベイクする

1.UV展開

ポリゴンを削減したモデルのUV展開を行います。

UVの切れ端は出来るだけ鋭角なモデル変化をした個所に設けることで
テクスチャの切れ目を目立たなくさせることが出来ます。

image9

2.テクスチャベイク

作成したゲームUVとフォトスキャンモデルUVをGameDev Simple Bakerに接続し
テクスチャをベイクします。

image1

image2

まとめ

1.写真を上手く撮影すれば品質に差はほとんどない
ソフトによって手順や計算速度が異なる

2.得意なもの
固形物 大きい/太い 反射しない/光沢が出ないもの

3.ポリゴン削減とUV展開をすればゲームモデルに早変わり
自動化できれば量産が可能


今回は触れませんでしたが、Python等で自動処理が可能です。

また、ポリゴン削減の定義なども精度の良いものが出来れば
ゲームモデル化まで量産が可能となります。

出来るだけ汎用モデルの工数を削減しより力を入れるところに
割く時間を捻出出来ればと思います。

参考文献

フォトグラメトリーとプロシージャルを用いた最新ハイエンドゲーム3DCG背景制作手法
~ハイエンドゲーム開発の経験がない会社がいかにしてそれらを生み出したか~

フォトグラメトリ入門 撮影方法~3Dモデル作成

[比較] Reality Capture vs. 3DF Zephyr

人柱系CGモデラーのTipsブログ

はじめに

初めまして。クリエイティブ部のがなさまと申します。
この記事はKLab Creative Advent Calendar 2019 の 3日目の記事になります。

技術書専門の同人誌即売会「技術書典6」において今回も当社エンジニア有志で「KLab Tech Book Vol.4」を発行しました。
この同人誌の表紙制作とポストカード6種の制作を、社内イラストレーター有志

  • キャライラストレーター5名(東京4名・大阪1名)

  • 背景デザイナー2名(東京2名)

総勢6名でKLabの「どぶろく制度」(標準労働時間の10%以内であれば、上司の承認なしで本当にやりたかった研究など、好きなことに時間を費やすことができる制度)を使い取り組みましたので、ご紹介いたします。

今回は1つのIPのような作品として非常に品質の高いアウトプットが出来たのではないかと感じており、通常業務以外で「気軽な本気」で「気軽なやりたい!」を実践でき、自分たちで「楽しい!」を生み出すことができたお話しが出来ればと思います。

image9

「自由に好きなもの作っていいよ」は楽しくなかった

「KLab Tech Book」は2019年4月で4冊目となり、表紙の制作取り組みは今回で3回目となります。
初回、vol1の表紙はエンジニアで用意していましたが、vol2から社内イラストチームが参加できないかという相談を受け、イラストレーターの有志を募って取り組み始めました。
そこから半年後にvol3も制作し、進行する上でイラストレーター達から「何描いて欲しいかのオーダーないの?」という質問と疑問をもらいました。

私はイラストレーター達の通常業務の息抜きになればと良いなという想いから「B5サイズのイラストにしてくれればOK!」「自由に好きに描いていいよ!」と、いわゆる”丸投げ”をしていました。
しかしイラストレーター達から「うーん…なんか、何やって欲しいのかが見えなくて、あまり遣り甲斐が見いだせない…」といった話が出てきたのです。

え?自由に描くの、楽しくないの?そうなの???

自由に描くなら趣味で描く。
会社で取り組む制作については、しっかりと一定のプロダクトを生み出したい!
だってプロですもの。
私はイラストレーター達にしっかりオーダーを提案しなければいけない、と気づきました。

楽しい!は自分達で生み出す

まず毎回「KLab Tech Book」を進行している、ほぼエンジニアしかいないチャット部屋でキャラクター設定募集を募り、エンジニア陣の妄想を聞き出しリスト化しました。
何人くらい興味を持ってくれるかな~と思いながらも、そのリストをつけて社内告知したところ、なんと5人も挙手してくれたのです!しかも内2名は前回も参加してくれたメンバーでした。

進行はそのエンジニアばかりのチャット部屋で行い、
リスト化された妄想がイラストレーター達によって具現化され、キャラデザイン案がどんどん投稿されてゆきました。
イラストが投稿されるたびに、そのチャット部屋にいるあらゆる職種のメンバーから、ダイレクトに感想が飛び交い、通常業務にはあまり見られないフランクなやりとりであっという間にキャラクターが生み出されていったのです。

イラストメンバー同士もバラバラのプロジェクトや拠点に所属する為、初期はお互いの自己紹介から始まり、互いに何が行えるのかを伺っていましたが、週次で30分ほど集まる形にして認識合わせなどを行いました。
私はスケジュールだけは都度切っておりましたが、メンバー間でどんどんと話が進んでゆき、イラストのテイストや誰が何を分担するや、目指すクオリティなどが決まってゆくではありませんか。

image11

image14

image8

少ない時間の中、みんなのやる気に「大丈夫?頑張り過ぎてない?」と心配しつつも、クオリティの高いアウトプットが続々と作られ、
盛り上がってゆくチャット部屋…!
途中から背景デザイナーも合流し学園のデザインまでも出来てきました。
(結局お蔵入りしてましたが、途中、なぜか3Dモデルも出来てました…)

image12

面白いことに、楽しんで作っていることは伝染してゆくのか、
社内で「何か面白いことやってるんだって?」と、
いろいろと聞かれるようにもなっていました。

アウトプットご紹介

執筆者募集から印刷データ納品まで約6週間。
制作したアウトプットのご紹介となります。

image10

image13

image1
image6
image5
image7
image2
image3
image4

まとめ

最後に、参加メンバーの感想をご紹介します。

  • ぶっつけ本番で作業分担&絵柄やクオリティ統一できたのは凄い…他案件の方々と技術協力もできて良い制作でした

  • キャラデザ等普段やらない作業をやりつつも、各々得意分野で分担してクオリティ高く制作できたので満足度高かったです

  • 背景アリのキャラメインのイラストが作成でき、KLabのイラストレーターの合作という点でとても良いものになってると思います!

  • 合作(表紙)と個人技(ポストカード)の両方があったのでプロジェクトの垣根を超えた作業が新鮮だった。あと個人のテイストを知れたのもかなり良かった

  • 案だしの段階から否定的な意見がなく、活発且つお互いを尊重して作業できたと思う

  • 今回は特にみなさんの意見を取り入れながらの作業だったのとキャラの設定もあり、レイアウトなどのイメージがしやすかったです

  • 他案件のデザイナーの方やエンジニアの方々など、普段あまり関わらないような方と一緒に作業ができて楽しかったです!良い機会になりました

こういうことやろうよ!という提案に、とても前向きに取り組んでくれる当社のクリエイティブメンバーが私は大好きです。
これからも通常業務以外にも、もっと「気軽な本気」で「気軽なやりたい!」を実践でき、自分たちで「楽しい!」を生み出してゆくKLabでありたいと思います。

そして今年の秋にはKLabtechbook vol5も無事発行しております。
こちらの取り組みはまた別の機会できっと触れることができると思いますので、どうぞ
お楽しみに!

明日の記事はnaoieさんのフォトスキャンのお話です!

はじめに

この記事は KLab Creative Advent Calendar 2019 の 2日目の記事になります。

こんにちは。テクニカルアーティストグループの fo-ta です。
去年のアドベントカレンダー では Shader のことを書かせて頂きました。
今回は半透明の前後関係(描画順)について書かせて頂きます。

概要

半透明のオブジェクトはなぜ見た目の破綻が起こるのか、具体的に Unity ではどう改善できるのかを紹介します。

クリエイティブブログですので、デザイナーさん、アーティストさん向けにわかり易さを重視しています。そのため、省略した書き方も取り入れています。ところどころ「正確には違う」点があるかと思いますが、ご了承ください。

難しい話はいいから実例だけ知りたい!という方はここまでスキップしちゃいましょう。

ざっくり座学編

深度

そもそも、なぜ3DCGは前後関係が正しく見えるんでしょうか。

「手前にあるんだから手前に見えて当たり前」。現実世界ではそうですが、CGである以上はちゃんと仕組みがあります。

その仕組とは、深度情報です。

image15

Z深度、深度マップ、デプスマップなどの呼び方をされる、上図の様な白黒画像を見たことがある人も多いでしょう。映像制作で被写界深度をかける時などにも使いますよね。この情報を利用します。
すでに手前にレンダリングされている場合はレンダリングしない、そうでなければレンダリングして深度情報も更新する、と言う流れで正確な前後関係になっています。

このように、深度情報を用いて正確な前後関係でレンダリングする手法をZバッファ法と呼びます。

Zバッファ法の弱点

そんなZバッファ法にも弱点があります。

手前にレンダリングされた半透明オブジェクトの深度情報により、奥のオブジェクトがレンダリングされない、ということが起こる点です。

image11

この様な理由から、半透明オブジェクトは奥のオブジェクトから順番にレンダリングすることで前後関係を解決しようとします。

この順番のことを「描画順」と呼びます(レンダリング=描画)。

描画順の決定方法

描画順はいくつかの要素を元に決定されます。ここでは代表的な例を紹介します。

オブジェクトの位置

オブジェクトの位置がよりカメラから遠いと先、近いと後の描画順になります。つまりより遠くにあるオブジェクトから順に描画されます。
ここでいう「オブジェクトの位置」は、オブジェクトの中心座標が基準になります。ツールハンドルの切り替えボタンを Center にするとその位置がわかります。

image3

image16

通常の Mesh の場合は中心位置が自動的に算出されます。
SkinnedMesh の場合は Bounds の Center の位置が中心座標です。これは Unity 上で編集することができます。

image4

image12

Render Queue

マテリアルの Render Queue の数字が小さいと先、大きいと後の描画順になります。

image1

これはオブジェクトの位置よりも優先されるため、奥にあるのに手前に描画する、と言うトリッキーなことも可能です。

※ Render Queue に使用する値はゲーム全体を考慮して設計すべきです。必ず担当のエンジニアさんと相談しましょう。

頂点番号

上記2つはオブジェクト単位ですが、1つのオブジェクト内のではポリゴンの頂点番号が小さいと先、大きいと後の描画順になります。

image8

Maya ではコンポーネントエディタ、 Houdini では SceneView や GeometrySpreadsheet で確認することができます。

実践編

実際に制作の現場で遭遇しそうな例を2つほどご紹介します。
(今回は Unity 2018.4 を使用しています。)

斬撃エフェクト編

image2

剣で攻撃する時などに使用する斬撃エフェクトです。

このように厚みのあるエフェクトを作成する際、ポリゴンの両面を描画する Shader を使用すると破綻が発生します。

角度を変えて見た際に、奥にあるはずのポリゴンが手前に描画されてしまうのです。

このような場合は、両面にポリゴンを用意し、 片面描画の Shader を使用すると解消できます。
両面にポリゴンを用意する際の注意として、前述の頂点番号を整える必要があります。頂点番号が小さい方が先にレンダリングされるので、内側が小さく、外側が大きくなるようにします。
複製&法線反転で内側と外側のメッシュを用意したら、 Maya では内側、外側の順メッシュを選択して結合、 Houdini であれば内側、外側の順で Merge ノードに接続すると意図した順の頂点番号になります。

Unity でポリゴンを片面のみ描画する Shader を設定します。

image5

例えばプリセットの Particle/Standard Ulit Shader の場合、 Two Sided のチェックを外すと片面描画になります。

無事、破綻を解消できました。

竜巻編

より多くのポリゴンが重なる場合、もう一歩工夫が必要です。

左が破綻したもの、右が整理したものです。

破綻している方では、大きな竜巻の内側ポリゴンが小さな竜巻よりも手前に描画されてしまっています。また、記事冒頭の画像のように視点を変えると前後関係も変わってしまうこともあります。
大きい竜巻の内外と小さい竜巻の内外、複数のポリゴンがちぐはぐな順番で描画されて起こる破綻です。

では、どのような順番で描画されればうまくいくでしょうか。

image18

①大きい竜巻の内側、②小さい竜巻の内側、③小さい竜巻の外側、④大きい竜巻の外側の順番で描画されれば大丈夫そうです。

内側の竜巻は斬撃の際と同様に内側、外側の順番で頂点番号を整理します。
外側の竜巻は、 Render Queue で順序付けします。メッシュを分割し、それぞれ別のマテリアルをあてましょう。

image7

負荷の増加について

描画順を整えるためにアセット数や描画負荷が上がってしまうことがままあります。
例えばポリゴンを両面作ると、当然ながらポリゴン数が倍増します。マテリアルを増やすことでドローコールが増えることもあります。

見た目のためなら何を犠牲にしても構わない!…なんてことはありません
見えなかったり目立たない部分は妥協し、目立つ場所は整えると言った、ほど良い落とし所を探ることが重要です。

その他

今回は詳細な説明を省略しますが、この他の半透明描画に関係するトピックスをいくつか紹介します。機会があれば別の記事で詳しくご紹介しようと思います。

複数マテリアル

image6

1つのメッシュにQueueが同じ複数のマテリアルを割り当てた場合、上から順に描画されます。これを利用して、竜巻の例でメッシュを1つにすることができます。

ZTest

ZTest という仕組みを利用すると、そのオブジェクトを描画する際、すでに書き込まれている深度情報を無視することができます。全画面エフェクトなど、最前面に表示したい場合に有効です。
ON / OFF の単純な切り替えではないことと、他の要素との兼ね合いがやや難しいため、今回は省略しています。

加算合成

半透明(アルファ合成)と比べ、加算合成同士は前後関係が破綻していてもバレにくいです。加算はその名の通り色の計算がシンプルな足し算のため、どちらが先でも結果が同じになるためです。

ディザ合成

不透明を格子状にパンチ抜きして描画することで擬似的に半透明にする技術です。不透明で描画するので深度情報を利用できるため、前後の破綻が起きません。
解像度が低いと不透明だとバレること、パンチ抜きの処理がモバイル端末と相性がわるいことなどから今回は省略しています。

最後に

エンジニアさん向けのこの手の情報は結構あるんですが、アーティストさん、デザイナーさん向けの記事ってあまり見ないな、と思ったことが今回のきっかけです。
描画順の破綻と日々戦っている人達に、少しでも解決のヒントになれれば幸いです。

なお、もし要望があれば、他の実践例もご紹介したいと考えています。

image10

この記事はユニティちゃんライセンス条項の元に提供されています。

こんにちは、atsushiです。

この記事はKLab Creative Advent Calendar 2019 の1日目の記事になります。

アドベントカレンダーとは、12月1日からクリスマスまでの25日間、毎日一人ずつブログ記事を書いてバトンを繋いでいくという技術界隈のちょっとしたお祭りのようなものです。

2018年に引き続き、今年もなんとか作成することが出来ました!

このカレンダーでは、KLabの3D・2D・UI/UX・エフェクト・サウンドから、TA・R&D・マネジメントに至るまで様々なクリエイティブの話題について触れて行ければと思います。
無事完走できるように暖かく見守っていてください!

さて、まず私からは「KLabの”情報発信”」について、自分なりの目線でお話いたします。

KLabの”情報発信”について

KLabには情報発信をしたいスタッフを積極的に支援しよう!という文化があります。

そして、その文化をより推進するために「情報発信促進制度」「クリエイティブ広報グループ・技術広報グループ」という二つの存在が大きな役割を果たしています。

これら2つについて簡単にご紹介します。

情報発信促進制度

情報発信促進制度とはその名の通り、従業員による情報発信活動を促進し、成長を支援する制度です。

この制度を使うことで、CEDECやUnite等のカンファレンスで発表・登壇をしたい場合や、勉強会やイベントを企画・開催・運営したいとなった場合に、それを実現するために掛かった時間は業務をしたものとみなされ、活動費用も支援してもらえるようになります。

クリエイティブ広報グループ・技術広報グループ

クリエイティブ広報グループ・技術広報グループとは、アーティスト・エンジニアそれぞれの分野に特化した広報組織のことです。

この組織は今年設立されたばかりで、特徴は所属しているメンバーの殆どが普段現場で実際にゲーム開発をしている現役のアーティストやエンジニアということです。

現場の目線で「こういうのがあったらいいのにな」と思うような勉強会・イベント等の企画を自分たちの手でそのまま実現でき、さらにそれを広報活動に繋げることが出来るという強みがあります。

制度・組織が新設されてどう変わった?

これらの制度や組織が出来たことで、KLabは情報発信をしたい・興味がある人にとってとても活動がしやすい環境になったと感じており、実際に社内の情報発信に対する熱量は明らかに上がってきております。

とくに今年はCEDEC、Unite、GDCなど数多くのカンファレンスに当社から多くのスタッフが登壇することができ、社内でも有志による勉強会が月に1回以上開催されるようになりました。

また、昨年末のアドベントカレンダーを始める為に立ち上げたこのKLabGames Creative Blogも、実はあれから今日に至るまで毎月1回以上は必ず更新されていたりします。

(ふと思い出したときにでもいいので是非覗いてみてください!)

まとめ

ありがたいことにこれらの活動がきっかけでKLabのお仕事に興味を持ってくださる方が増えたり、実際に入社してくださる方も増えてきたように感じてます。

情報発信による効果はアクセス数等の数値だけでは測れない部分もあるので、こういった実際のリアクションがあるとやっててよかったなと思えますね!

ここまでご覧いただきありがとうございました。明日の記事はfo-taによるデザイナーのための半透明描画順です!お楽しみに!

こんにちは。はじめまして。
KLabのテクニカルアーティストGに所属している、こかろまと申します。

去年の KLab Creative Advent Calender 2018 以来のブログ執筆となります。

今回の内容はMaya+Pythonでツールを書き始めた方を想定しています。

はじめに

ユーザーにとって「扱いやすいツール」とはどういった要素があげられるでしょうか。

作る方によって意見は変わってくるかと思いますが、私の場合は特に「ツールがユーザーにとって直観的であるかどうか(=ツールの直観性)」が重要だと考えています。

ここでいう「ツールの直観性」とは、使用するユーザーが普段利用しているツールの挙動に合わせることであったり、アプリケーションに最低限備わっているユーザーインターフェイスにできる限り合わせることとして位置づけています。

ユーザーインターフェイスの部品の一例としては、普段オペレーティングシステムで
使っている「ダイアログ」や...

image1

アプリケーションによって異なりますが、「Ctrl+Z」のundo(元に戻す)・
「Ctrl+Y」のredo(やり直し)などのショートカットであったり...

そこで、今回は社内のツール実装方針として共有する目的も兼ねて、
Maya+Pythonにおける「undo(元に戻す)」「redo(やり直し)」についてお話ししたいと思います。

Maya+Pythonにおけるundo・redoの挙動

Maya+Pythonでツールを実装するにあたって、undo・redoで躓く点は
大きく分けて下記の二つになると思います。

  • ユーザーが求めるundoができないケース
  • そもそもundo・redoできないケース

それぞれの対処方法についてお話ししていきます。

ユーザーが求めるundoができないケース

Mayaでツールを実装する際に考慮していないと陥りやすいケースです。

「シーン上にあるpCube1オブジェクトを選択する」だけのツールを例に挙げてみます。分かりやすいようにバリデータなども一切組んでいません。image2

# select_tool.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import maya.cmds as cmds

class SelectTool(object):

   def __init__(self, select_object_name=’’):
       self.select_object = select_object_name

   def execute(self):
       cmds.select(self.select_object, r=True)
       return

def main():
    _select_tool = SelectTool('pCube1')
    _select_tool.execute()

Maya+PythonにおいてMayaのdagノードを触るようなツールを実装する際には
方針によって様々ですが、基本以下のいずれかを利用するかと思います。

  • maya.cmds
  • maya.mel
  • pymel
  • openmaya(1.0 / 2.0)

このうち、「maya.cmds」「maya.mel」は各コマンドにundo・redo機能が実装されており、「一つのコマンドごとに」mayaのコマンド履歴の待ち行列に溜まっていきます。

「pymel」も内部的に「maya.cmds」「maya.mel」を利用しているため、同様の挙動となります。

つまり、上記のような「シーン上にあるpCube1オブジェクトを選択する」だけのツールの場合、特にツール側に考慮は必要なくundo(pCube1選択前に戻る)・redo(pCube1選択後に戻る)することができます。

image5

ですが、ツールは往往にして複雑になっていくものです。

たとえば、選択しているオブジェクトすべてを「Object_」というPrefixを付け、
後ろに数字を3桁の連番ごとにリネームするようなツールが必要になったとします。

その場合、大雑把ですが下記のような実装になると思います。

# rename_tool.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import maya.cmds as cmds


class RenameTool(object):

   def __init__(self):
       pass

   @staticmethod
   def validate():
       if not cmds.lockNode(q=True, l=True):
           return False

       return True
  
   def execute(self):
       _selection_dag_nodes = cmds.ls(sl=True, sn=True, fl=True, dag=True, tr=True)

       for _index, _value in enumerate(_selection_dag_nodes ):
           if self.validate() is False:
               continue

           _rename = 'object_{0:03d}'.format(_index)
           cmds.select(_value, r=True)
           cmds.rename(_rename)

       return

ツール自体は問題なく、シーン上の選択可能なdagノードがリネームされます。

また、undo・redoも行うことができます。

ただし、一点このツールには問題があります。

選択しているノードが大量にある場合のundo・redoの挙動です。

たとえば、50個くらいリネームすることができるdagノードを選択している場合に
上記のツールを実行します。

その後「あ、間違えてツール実行しちゃった!戻さなきゃ」とユーザーがundoを行うとします。

発行したコマンド回数分undoを行わないとツール実行前に戻れません。

今回の例では1つのdagノードあたりの実行順にすると「cmds.lockNode」「cmds.ls」「cmds.select」「cmds.rename」の4回分コマンドを発行しているので、ツール実行前の状態に戻す場合、1つのdagノードあたり合計4回undoを行う必要がありますが、mayaで設定されている待ち行列以上にundoすることはできません。

※正確にはコマンド単位ではなく、用意されている関数によって異なります。
Autodesk社のMayaコマンドリファレンスサイトに『「元に戻す」が可能』と記載があるものが待ち行列追加対象のものとなります。下記はlsコマンドのリファレンスです。

image6-1

出典:Autodesk Maya Help:Python コマンド:ls 

余談ですが、mayaの待ち行列の回数はMayaの『プリファレンス』⇒『元に戻す』項目より設定することができます。

インストール時点ではデフォルトで50に設定されています。

image4

ツールによってコマンドが発行される回数次第で、ツール実行前の履歴を消してしまうこともありえます。

こういったツールは、「ユーザーにとって扱いやすいツール」とはいえません。

解決策

昨今のmayaでは待ち行列に登録する範囲を設定するチャンクが用意されています。

maya.cmds.undoInfoのopenChunk, closeChunkがそれにあたります。

openChunk関数でチャンクを開き、closeChunkでチャンクを閉じる間に処理を書くことで、その間の処理を1回の待ち行列として定義することができます。

今回のツールでは、シーン上のオブジェクトを回すイテレータを実行している「execute関数」に上記のチャンク範囲を適用することで、シーン上のオブジェクト数分のコマンド発行から「ツールを実行する」単位となり、undo・redoの回数を1回に抑えることができます。

# rename_tool.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import maya.cmds as cmds


class RenameTool(object):

   def __init__(self):
       pass

   @staticmethod
   def validate():
       if not cmds.lockNode(q=True, l=True):
           return False

       return True
   
   def execute(self):
        cmds.undoInfo(openChunk=True)

        _selection_dag_nodes = cmds.ls(sl=True, sn=True, fl=True, dag=True, tr=True)
        for _index, _value in enumerate(_selection_dag_nodes ):
            if self.validate() is False:
                continue

            _rename = 'object_{0:03d}'.format(_index)
            cmds.select(_value, r=True)
            cmds.rename(_rename)

        cmds.undoInfo(closeChunk=True)

        return

ツールを実行し、リネームが確認できた後、Ctrl+Zでundoしてみます。

今度は1回ですべて戻す(=ツール実行前に戻る)ことができました!

image3

上記でもチャンクを一つにまとめることはできますが、せっかくPythonを使っているので、本処理をデコレータとして定義しておき、共通ライブラリとして扱えるようにしておくとツールや関数ごとに定義する必要がないので便利です。

# undo_wrapper.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import maya.cmds as cmds

def undo_chunk(function):
   def wrapper(*args, **kwargs):
       cmds.undoInfo(ock=True)
       function(*args, **kwargs)
       cmds.undoInfo(cck=True)

   return wrapper
# rename_tool.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import maya.cmds as cmds

from . import undo_wrapper


class RenameTool(object):

   def __init__(self):
       pass

   @staticmethod
   def validate():
       if cmds.lockNode(q=True, l=True):
           return False

       return True
   
   @undo_wrapper.undo_chunk
   def execute(self):
       _selection_dag_nodes = cmds.ls(sl=True, sn=True, fl=True, dag=True, tr=True)
       for _index, _value in enumerate(_selection_dag_nodes ):
           if self.validate() is False:
               continue

           _rename = 'object_{0:03d}'.format(_index)
           cmds.select(_value, r=True)
           cmds.rename(_rename)

       return

そもそもundo・redoできないケース

頂点すべてに処理を加える、UVを展開する、スキンウェイトを調整するなど、
処理自体が重いツールを実装する場合、maya.cmdsやpymelでは実行速度に期待ができないのもあって、弊社では基本的にopenmaya for Python APIを利用しています。

openmaya for Python API では、undo・redo機能は提供されていません。

そもそも処理したことが待ち行列にスタックされないため、undoを行っても
openmaya for Python APIを実行する前の履歴が実行され、処理したことはそのまま残り続けます。

そのため、undo・redoが必須な場合はすべて自前で実装する必要があります。

解決策

openmayaでundo・redoを考慮に入れる場合、MPxCommandのプロキシクラスを継承したクラス単位で処理を実装する必要があります。

また、一種のコマンドとして実装する必要があるため、プラグイン化する必要があります。

プラグイン化については今回は省きますが、Autodesk社公式リファレンスが非常に参考になります。

選択しているオブジェクトの頂点カラーをすべて赤(1.0, 0.0, 0.0, 1.0)で
塗りつぶす簡単なプラグインを例に挙げます。

今回は簡単に説明するためにdagノードの名前単位で頂点カラー情報を保持していますが、
厳密にアンドゥ・リドゥを実装する際にはMTypeID等で制御するべきです。

 # vertex_color_fill_plugin.py
# coding: utf-8
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import re
import _ctypes

import maya.cmds as cmds
import maya.api.OpenMaya as OpenMaya


class VertexColorFillTool(OpenMaya.MPxCommand):
   kPluginCmdName = 'VertexColorFillTool'

   def __init__(self):
       OpenMaya.MPxCommand.__init__(self)
       self.before_calculate_vertex_color_dict = {}
       self.selection_list = []

   @staticmethod
   def initialize():
       return VertexColorFillTool()

   @staticmethod
   def validate():
       if not cmds.ls(sl=True):
           print('error : no selection')
           return False

       _color_set_list = cmds.polyColorSet(q=True, acs=True)
       if not _color_set_list:
           print('error : color set no exists')
           return False

       return True

   def get_active_vtx_selection_list(self):
       _vertex_list = self.get_select_vtx_list()
       if not _vertex_list :
           cmds.error(u'not selected dag object or vtx')
           return

       cmds.select(_vertex_list , r=True)

       return OpenMaya.MGlobal.getActiveSelectionList()

   @staticmethod
   def get_select_vtx_list():
       _vtx_list = cmds.polyListComponentConversion(tv=True)
       _vtx_list = cmds.ls(_vtx_list, fl=True)
       return _vtx_list

   def doIt(self, args):
       """
       コマンド実行時(関数実行) 関数
       :param args:
       :return:
       """
       if not self.validate():
           return

       self.selection_list = self.get_active_vtx_selection_list()

       for _selection_object in xrange(self.selection_list.length()):
           _vertex_colors = []
           _dag_path, _ = self.selection_list.getComponent(_selection_object)
           _target_mesh = OpenMaya.MFnMesh(_dag_path)

           # 頂点カラーの実行前の状態を保持
           self.before_calculate_vertex_color_dict[_selection_object] = _target_mesh.getVertexColors()
           _vertex_list = cmds.ls(self.selection_list.getSelectionStrings(_selection_object), fl=True)

           _vertex_colors = [(1.0, 0.0, 0.0, 1.0) for i in _vertex_list]

           _target_mesh.setVertexColors(_vertex_colors, xrange(_target_mesh.numVertices))
       return

   def redoIt(self):
       """
       待ち行列の実態(リドゥ) 基本処理を再度実行すればOK
       :return:
       """
       self.doIt()
       return

   def undoIt(self):
       """
       待ち行列の実態(アンドゥ) 実行前の値を書き込むことで元に戻す(ように見せる)
       :return:
       """
       for _selection_object in xrange(self.selection_list.length()):
           _dag_path, _ = self.selection_list.getComponent(_selection_object)
           target_mesh = OpenMaya.MFnMesh(_dag_path)

           target_mesh.setVertexColors(self.before_calculate_vertex_color_dict[_selection_object],
                                       xrange(target_mesh.numVertices))
       return

   def isUndoable(self):
       return True


def maya_useNewAPI():
   """
   プラグインに渡されるオブジェクトの型を示すための定義。openmaya for Python API 2.0以降のみ必須。
   :return:
   """
   pass


def initializePlugin(mobject):
   _open_maya_plugin = OpenMaya.MFnPlugin(mobject)
   try:
       _open_maya_plugin.registerCommand(VertexColorFillTool.kPluginCmdName, VertexColorFillTool.initialize)
   except TypeError:
       print('error initiliaze plugin')


def uninitializePlugin(mobject):
   _open_maya_plugin = OpenMaya.MFnPlugin(mobject)
   try:
       _open_maya_plugin .deregisterCommand(VertexColorFillTool.kPluginCmdName)
   except:
       print('error uninitiliaze plugin')

# 実行側のコード(スクリプトエディタなど)
import maya.cmds as cmds
cmds.loadPlugin(’vertex_color_fill_plugin’)
cmds.VertexColorFillTools()

プラグインとしてロードし、実行した結果が下記のとおりです。

各dagノードが元々持っていた頂点カラーに戻せていることが確認できます。

image7

あくまで上記のクラスを継承した際にはundoやredo等、ユーザ操作に対応した
継承元のコールバック関数が呼び出されるだけなので、

  • doIt関数「実行前の状態を記憶しておき、目的の処理を行い、場合に応じて実行後の処理状態を記憶する」
  • undoIt関数「doIt関数の際に記憶した、実行前の状態に戻すための処理を行う」
  • redoIt関数「実行後の処理状態に戻すための処理を行う」
    =今回のツールではdoIt関数を再度呼び出すことと結果が同じとなるため、doIt関数を呼び出すことでredo処理とする

上記3つの実装方針に基づいてundo・redoを実装することになります。

そのため、処理次第ではundo・redoではない処理を実装することもできてしまいます。

細心の注意を払って実装しないと、うっかりデータを破壊するような処理になりかねません。

また、プラグインの都合上Maya上でunInitialize(プラグインマネージャでプラグインのロードを外す等)されてしまうと当然そのツールで保持していた待ち行列は破棄されてしまうため、注意が必要です。

まとめ

ツールやユーザーのニーズによってundo・redoの範囲の要件は変わります。

また、undo・redoがそもそも必要ないケースもあるかと思います。

openmaya for Python API を扱う必要があるツールにundo・redoが必須要件としてツール完成後に加わる場合、単なるPythonのパッケージ群のツール設計』から『Mayaでのプラグイン設計が必須のツール設計』と、実装方式や再設計に係る工数が大きく変わることもあるため、必ず初期のツール設計段階の要件の一つとして洗い出しを行うことを強く推奨します。

今回は「ユーザーにとって扱いやすいツール」の一例として、undo・redoについて
お話しさせていただきました。

本記事がMayaでツールを作る人の手助けになれば幸いです。