ろうでんのブログ

ただのブログ

アカツキのインターンに行ってきた話

久しぶりの投稿です。
株式会社アカツキの就業型インターンシップで10営業日働いてきたので、そこでやったこと・学んだこと・感じたことについて書きます。
某配信中のスマートフォンゲームのプロダクトチームに所属して、クライアントサイドのプログラム(Unity)を触りました。

目次

お前のスペック

理系の国立大学の大学院の修士課程1年生で、2020年新卒の就職活動の最中です。
Unity 歴は半年くらいなので Unity 独自の技術についてはペーペーですが、C# 歴は7年くらいで、かつ業務(アルバイト等)にも使っているのでそれなりにできると自負しています。

与えられたタスクとやったこと

  • 「Profiler を使ってパフォーマンスを改善できそうなところ見つけて!」
    • 一瞬だけ重くなる箇所を見つけて原因を調べた
      • Unity のログ出力が瞬間的に大量に呼ばれていただけだった(なのでリリース版では生じない問題)
      • UnityEngine.Debug.Log() について改めて学んだ →後述
  • コード自動整形ツールの導入 →後述
  • プロダクトで実際に使われている Shader ファイルを見ながら Shader の勉強 →後述(メインの話)

UnityEngine.Debug.Log() について改めて学んだ

Unity でデバッグ出力をしたいとき、UnityEngine.Debug.Log() を使います。
この UnityEngine.Debug.Log() によるデバッグ出力は、リリースビルド時には実行されません。
しかし、IL 上でのメソッドの呼び出し自体は消えないため、引数の評価コストとメソッドの呼び出しコストは掛かってしまいます。
そのため、下記のページでも紹介されている手順で新しいメソッドを作ってあげます。 qiita.com noracle.jp

簡単にまとめると、

  1. Conditional 属性 [Conditional("UNITY_EDITOR")] をつけたメソッドを作り、UnityEngine.Debug.Log() をラップする
public static class Logger
{
    [Conditional("UNITY_EDITOR")]
    public static Log(string arg)
    {
        Debug.Log(arg);
    }
}
  1. UnityEngine.Debug.Log() を使用している箇所を、上記メソッドに置き換える
public class Hoge : MonoBehaviour
{
    public Update()
    {
        // Time.deltaTime の評価や、string.Format() の処理が入る
        // UnityEngine.Debug.Log($"Delta Time: {Time.deltaTime}");

        Logger.Log($"Delta Time: {Time.deltaTime}");
    }
}

コード自動整形ツールの導入

Artistic Style というツールがあるので、プロダクトチームで共有して使うためのバッチを組んでほしい!」という要望があったので、Artistic Style を呼ぶためのシェルスクリプトと、コーディング規約の設定ファイルと、利用者向けのドキュメントを書きました。
(こういう、明確な期限がないものってインターン生の課題に向いてますよね)

Artistic Style とは、C, C++, C++/CLI, Objective-C, Java, C#ソースコードインデントや改行、スペースの挿入位置などのコーディング規約を揃えたり、タブ文字→スペースの置換や改行文字の統一を自動でやってくれるすごいやつです。
「どのルールに統一するか?」を設定ファイルに記述することができるので、チームでのコーディング規約統一にも使えます。
個人的には、コーディング規約を自然言語で記述されるよりも、こういう設定ファイルの方が分かりやすいんじゃないかと思ってます。

astyle.sourceforge.net

トゥーンレンダリングの勉強

プロダクトで実際に使われている Shader ファイルを見ながら、Shader とトゥーンレンダリングを学びました。
主に トゥーンレンダリング(3D グラフィックをアニメ塗りっぽく出す技法)について学びました。

トゥーンレンダリングでは、主に下記の2つのことをやります。

  1. アウトライン(輪郭線)を出す
  2. シェードの計算をいじって、シェードラインがパキッっとなるようにする(アニメ塗り)

詳しく説明していきます。

1. アウトライン(輪郭線)を出す

アウトラインを出すには下記のようなやり方があり、それぞれ特性が異なります。

参考↓

light11.hatenadiary.com

  1. 少し大きくしたモデルをアウトライン色で描画してから、元のモデルで上書き
    • 最も単純なやり方
    • 1つのモデル内の凹な部分にもアウトラインが出る
    • 描画処理は2回走る
  2. 少し大きくしたモデルのステンシルバッファの値と、元のモデルのステンシルバッファの値を別々にして、前者の部分をアウトライン色で描画し直す
    • ステンシルバッファが同じ値のモデル同士(凹な部分や、体と腕の重なりなど)にはアウトラインが出ない
    • 描画処理は3回走る
  3. ポストエフェクトで、隣接する箇所との深度が大きく異なるところにアウトラインを描画
    • 深度情報を付与してあげる必要がある
    • 凹な部分にアウトラインを出すかどうかを決められる
  4. ポストエフェクトで、隣接する箇所との法線ベクトルの方向が大きく異なるところにアウトラインを描画
    • 法線情報を付与してあげる必要がある
    • 凹な部分にアウトラインを出すかどうかを決められる
    • シワや折り目などの部分にもアウトラインを出すことができる
  5. シェーダを使わずに、モデルやテクスチャ自体にアウトラインをつける
    • モデリング担当者の思い通りの箇所にアウトラインをつけられる
    • 後からアウトラインの太さや色を変更することは困難

1について詳しく調べてみたので下記にまとめます。

最も単純なアウトラインシェーダ

Pass を2つ使い、下記の流れでレンダリングします。

  1. 1つ目の Pass で、少し大きくしたモデルをアウトライン色で描画
    • 「少し大きくする」を実現するために、頂点シェーダ内で各頂点を法線方向に移動させる
    • このときの移動させる量がアウトラインの太さになる
    • このままだと 2. のモデルを覆い隠してしまうので、Cull Front を指定する
  2. 2つ目の Pass で、元のサイズのモデルを元の色で通常通りに描画

以上の処理でアウトラインを出せますが、もうちょっと手を加えることでより良くなります。

  • 近くのアウトラインが太く、遠くのアウトラインが細くなるのをなんとかしたい!

    • 法線方向への移動量にカメラとの距離に応じた係数を掛けてあげれば、ある程度太さが一定になる
    • 「スクリーン座標系で 2px の太さにしたい」といった場合には、より重たい処理が必要になる
  • テクスチャの色に応じてアウトラインの色を変えたい!(2D CG イラストでいうところの「色トレース」という技法)

    • テクスチャの色を暗くした色で描画してあげればOK
    • 厳格にアウトライン色を指定したい場合は、モデルの頂点カラーなどにメタ情報としてアウトライン色を埋め込む必要がある

2. シェードの計算をいじって、シェードラインがパキッっとなるようにする(アニメ塗り)

通常のシェーダ(Unity でいうところの Standard シェーダ)では、シェードにはグラデーションがかかったようになります。
ですが、アニメ塗りのノーマル色/1号影のようにシェードライン(シェードのある部分とない部分の境界線)でパキッと塗り分けたい場合があると思います。
そのような場合、下記のようなやり方があります。

  1. ディフューズの計算時に、変化を大きく(係数をかける)して clamp(範囲内に収める)する
    • clamp して1になったところをノーマル色、0になったところを1号影にする
    • 1号影の色を厳密に指定したい場合、テクスチャにメタ情報をつける
  2. テクスチャに全部描いて、シェーダは Unlit(シェードをつけない)にする
    • モデルの向きに応じて陰が変わらない(逆さまになっても)

マップオブジェクトなど、向きが変わらないモデルに対しては 2. でも問題ありませんが、プレイヤキャラクタのように向きが変わるモデルに対しては 1. が望ましいです。

さて、1. の処理でアニメ塗りを再現できるわけですが、もうちょっと手を加えることでより良くなります。

  • 2号影をつける
    • 上記 1. の処理をもう一度やってあげる
  • ハイライトをつける
    • Specular の計算時に、上記 1. と似たように clamp してあげる
  • リムライトをつける
    • モデルの奥のやや横に光源があると仮定して Diffuse を計算

インターンシップを通して感じたこと

私は他社でのアルバイトや就業型インターンの経験があります。
それと比較して最も感じたことは、社員と同様の流れで仕事をできたことだと思います。
他社のインターンでは、インターン専用のカリキュラム(?)に沿って以下のような流れで仕事を進めることがしばしばありました。

  1. 「〇〇を実装する」や「〇〇のバグを直す」といったタスクが与えられる
  2. 問題の対処をする
  3. 問題が解決できたか確認する
  4. Pull Request を出す
  5. 「社員が忙しいのでレビューできない!ごめんね!」
  6. 退職後マージ

一方でアカツキでは、以下のような流れで仕事を進めることができました。

  1. 現状のプロダクトを触って自分で問題点を見つける(自分でタスクを決める)
  2. 問題の原因を調べる
  3. 問題の対処をする
  4. 問題が解決できたか確認する
  5. Pull Request を出す
  6. (レビューされる)
  7. レビューに応じて直す
  8. 6-7 を繰り返す
  9. マージおめでとう!

こうして社員と同じ流れをたどることができ、ただ技術力をつけるだけでなく、仕事を進める力がつけられたのではないかなと思います。

総評

アカツキでのインターンを通して、

  • Unity 力
  • Shader 力(トゥーンレンダリング 完 全 に 理 解 し た
  • コーディング規約治安部隊力
  • タスクを見つけて実行する仕事力

をつけられたと思います!

今回のインターンでは、メンターの方だけでなく、プロダクトチームのいろんな方々にお世話になりました!
この場を借りて感謝申し上げます🙏

aktsk.jp