lumino trail

ゲームエンジン作ってます。Github:https://github.com/lriki/Lumino Twitter:https://twitter.com/lriki8

霜月と作業報告の候

光かわいい。

物理ベースレンダリングを標準サポートします

f:id:lriki:20171201005215p:plain

動機:マテリアルとライトがシンプルに作れるから

こんなのこんなの はめんどくさいよ!とっつきにくくなるだけだよ!

とか言いつつ光学をかじっています。

ライトのインターフェイスを仕上げました

インターフェイスAPI とはすなわち、ユーザーとの約束である。 これを決定し、将来にわたってメンテナンスを続けることはつまり鎖であり、それは信頼である。

  • AmbientLight
  • HemisphereLight
  • DirectionalLight
  • PointLight
  • SpotLight

どこか で見たことあるようなクラスたちだね!

物理ベースレンダリング用なので、プロパティもほとんど同じようなのがいます。

ソフトシャドウ作ってます

今のところ DirectionalLight に限り、影を落とすことができます。

FXAA 作ってます

MSAA よりちょっと高速かも。ただ、なんか汚い気がする。

まとめ

Lumino っていう名前らしい機能やっと作ってあげられそう。

ていうか早くブランチ閉じて記事書く詐欺を解決して次のテスト版準備しないと・・・。

神無月と作業報告の候

よくない。とっても良くない。

主に進捗が。

またブランチ1ヶ月以上のばしてるよ。

よくない。

SSAO

ほい

f:id:lriki:20171031213352p:plain

後で記事書く。複雑だから。

Clustered Shading

ほい

f:id:lriki:20171031213338p:plain

後で記事書く。複雑だから。

CornellBox

ほい

f:id:lriki:20171031213420p:plain

これは複雑じゃないかな。

レンダリングパイプライン

先月「Deferred Shading はやらない」とか言ってたけど、SSAO 作る途中で似たような仕組みをやっていました。

その結果、クラシックなライティングでも Clustered でも Deferred でも、その子たちの差し替えとかハイブリッドとかついでにカスタムシェーダもやっちゃうよとか なんかもうなんでもできそうなパイプラインが見えた気がするので、いいかげんこいつの fix にかかるよ。 (と言っても書きなぐりゴミコードが多いから時間かかりそうだけど・・・)

これ以上求めるならコンシューマなつよいゲームエンジンとか使ってねの限界が見えた気がします。 だから進捗悪くなったのだろうか。燃え尽き症候群的な。ラスボスの前でセーブして積んでる的な。

よくない。よくないよ。

長月と作業報告の候

文字列周りの内部的な変更

先月、

  • 内部文字コードを統一にする
  • 例外を積極的に使わないようにする

とか言ってましたが、まだ全体として仕上げるには至ってないです・・・。

String クラスは内部構造を大きく変えました。できるだけ標準ライブラリに依存しないようになっています。 ちなみに SSO(Short String Optimization) も付くようになりました。

編集用のメッシュデータ構造

LOD 作るためのポリ削減とか、法線のより自然な自動計算とか、ちょっと凝ったメッシュ編集にはただのトライアングルリストじゃつらいので、編集用のデータ構造を持ったメッシュクラスを作っています。

実行速度はごめんな。

RenderView

試験的に「RenderView」という機能を作ってみています。

RenderView はこんな人↓

  • ペイントソフトでいうところのレイヤーのようなもの
  • シーンのぞき窓 (オフスクリーンレンダリングでもなんでも)
  • UIウィジェットを配置できる
  • 親子関係を持つことができる
  • ポストエフェクトを適用できる

Unity 知ってる人には、Camera からレンダーターゲット関係の情報を全部独立させたものというと何となくわかるかも。

特にアプリ全体を通して共通な、画面の構成やエフェクトに関係する情報を直感的な感じでまとめたい、というところから作り出しています。 (最近の3Dゲームエンジンは Camera がたくさんの情報を持ちすぎる気がする・・・)

Clustered Shading

作成中のゲームで、大きな1つのメッシュモデル(主に地形)の上にたくさんのライトを置きたくなりました。

というところで、Forward Rendering でもライトをたくさん使えるライティング技法であるところの Clustered Shading を実装中です。 うまく動いたらこれを Lumino 標準のライティングにしたいかな。

ちなみに、Lumino は今のところ Deferred Shading をやるつもりはありません。 そういうことができるレンダリングフレームワークは用意してますが、標準機能としては実装しないです。

(メモ) なぜ Deferred Shading をサポートしないのか?

半透明オブジェクトの描画には Deferred Shading は使えないです。 昨今のゲームエンジンは、不透明には Deferred、半透明には Forward のハイブリッドが一般的です。 Deferred は他にも G-Buffer の肥大化とか MSAA と相性悪いとかいろいろありますが、半透明の問題が一番大変。

きらびやかなエフェクトとか、かっこよく、あるいは切なく、Cool に決めたいんです。 日本とか、アジア圏のゲームはそういう半透明オブジェクトを多用することが多いそうです。 自分の作るゲームもそうだし。

あと、Lumino はシェーダを積極的にサポートしてます。(和製のフリーなゲームエンジン・ライブラリとしては珍しいかも?) ハイブリッドでいく場合、ユーザーはそれぞれの特徴に気を付けてコーディングしなければならないです。 学習の負担とか、実行効率とか、レンダリングのクォリティとか、Luminoがターゲットとする小・中規模ゲームデザインとして必要な範囲は何だとか、 いろいろ考えた結果、そんな方向を向いています。 (ライト1000個使えるよ!とか言われても実際にそんなに使わないよね?)

ただ、それよりもつまらない現実的な問題として、業界の外から技術を追うことに対して時間がとりにくくなりそうだってことがあったりします。

葉月と作業報告の候

今月は Blender ばっかり触ってたのであまり進捗無いです。

ただ、Lumino ベースにちょっと大きな変更を入れようといろいろ検討中です。

  • 内部文字コードを統一にする
  • 例外を積極的に使わないようにする

主にプラットフォーム間の互換性と安全性を上げるための対応です。

この2つについては別に記事を作っています。近いうちに上げると思います。

安全性?

Lumino はずっと、パフォーマンス、特に実行速度とメモリ効率を重視して開発してきました。

それはメインターゲットがゲーム開発(リアルタイムアプリ)であることと、C++ の言語コンセプトを強く受けていたからです。

心理的に大きく変わったのは特にここ1年の間です。

ひとつは仕事。仕事はエンタメ的なところからは大きくはなれて、自動車や医療などが関係するところで働いています。"機能安全" で検索。

もうひとつは最近の言語やフレームワーク。 Kotlin や Swift など(まだそんなに語れるほど勉強してないけど)が効率よりも安全を意識した設計をしているのも影響を受けました。

Lumino は「まずは動くこと」を目指して割と急ぎ足で開発してたこともあって 正しく使えば正しく動くけど、間違ったときに安全な方向に倒して動くとか間違えたことを注意してあげるとか、そのへんがすごく弱いです。

ちゃんとドキュメント化すればいいんじゃないの?それだけじゃだめ。

「無知」に対して考えることは重要だって APIデザインの極意 がゆってた。

文月と作業報告の候

Lumino の公開用ページを作りました

https://lriki.github.io/lumino/

ものすごくプロトタイプです。実用レベルには程遠いです。でも、雰囲気は伝わるかな。

FontAwesomeアイコン描けるようにしました

ネタゲーがはかどりそうなアレです。 f:id:lriki:20170730235954p:plain

アイコンの名前とサイズを指定するだけで、スプライトとして使えます。

auto icon1 = GlyphIcon2D::create("fa-globe", 64);
auto icon2 = GlyphIcon2D::create("fa-play-circle-o", 64);
auto icon3 = GlyphIcon2D::create("fa-android", 64);
icon1->setPosition(32, 32);
icon2->setPosition(96, 32);
icon3->setPosition(160, 32);

アイコンの名前はこちら。 Font Awesome Icons

後々FontAwesome以外のフォントを自由にインポートできるようにしてみたいけど、まずはここまで。

今後について

実際にゲーム作りながらバージョンアップしていきます。

ゲームのためのゲームエンジン。これだけビジュアル面の機能詰め込んだのはそれなりのもの作りたいからだ。 でも、早くても半年くらいはかかるかなぁ・・・。

水無月と作業報告の候

また1ヶ月コースの長いブランチを作ってしまいました。悪い癖です。

オフスクリーンレンダリングの実装中

作成中のゲーム、最初にその作品の中で一番の山場にあたりをつけてざっくりと作ってみています。 そこでどうしてもやりたい演出があるのですが、それに必要なので機能を追加しています。

鏡とかね。

f:id:lriki:20170630211918j:plain

空の作成中

この表現も重要なので作ってます。

f:id:lriki:20170630212106j:plain

プロファイラの実装中

テスト戦略成功の秘訣は日常的な実施である。

ということでパフォーマンステストしやすいように簡易的なプロファイラを作っています。

f:id:lriki:20170630212335j:plain

コーディングスタイルの変更(というか回帰)

関数名をすべて lower camel case に直しました。何年か前に upper に直してから、やっぱり lower に戻した感じです。

勤めている会社にまつわるいくつかの事情から upper にしていましたが、もうそういうのは気にしなくていいかなというのと、

  • WinAPI 関数名(マクロ)とのバッティングがとても、とてもつらい

  • C++11 の range based for とか STL とか、標準が lower 要求してるし

など諸々の理由で lower にしました。

upper にした時はある意味後ろめたい理由だったけど、今回はそうじゃ無いのでまるっきりバカな変更にはならない・・・はず。

今後

思うところあって7/7に一度リリースしようとインストーラやサンプルを準備していたのですが、多分無理そうです。 ちょっと仕事忙しくなっちゃった。

7月中には出したいですね。というか、もうブログ始めて半年だ。ダラダラしないようにしよう。

皐月と作業報告の候

新しくゲームを作り始めました

ゲームエンジンはゲームを作るためのものです。使ってみてはじめてわかる粗がたくさんあるわけです。その修正でまだAPIは安定しません・・・。

今後はゲームを作りながら、その都度 Lumino のほうも更新していきます。

f:id:lriki:20170531205554p:plain

ちなみにキャラ2D、マップ3Dなゲームが好きです。

シーングラフへの Entity Component System の導入

ゲームというのは仕様変更が頻発しがちな、ある種の特殊なシステムです。

変更に対して柔軟に対応するため一般的にはオブジェクト指向で作りますが、 is-a の関係を簡単に破壊するような変更よく起こります。

その解決策のひとつが Entity Component System です。Unity をはじめとして昨今のゲームエンジンはみんな備えているアレです。 機能を部品として分割し、オブジェクトは部品の集合体として定義する、そんな仕組みを入れています。

シリアライズ

Jsonファイルを簡単にシリアライズできる機能を作りました。 boost::serialization と同じようなものですが、Object を継承したオブジェクトをシリアライズする場合、型情報を利用した高度なシリアライズが可能になっています。

class EventData : public Object
{
public:
    String          name;
    float          x;
    float          y;
    float          z;
    String          textureName;
    List<Variant> commands;

    void Serialize(Archive& ar, int version)
    {
        ar & LN_NVP(name);
        ar & LN_NVP(x);
        ar & LN_NVP(y);
        ar & LN_NVP(z);
        ar & LN_NVP(textureName);
        ar & LN_NVP(commands);
    }
};

struct MapData
{
    String                  displayName;
    String                  meshName;
    List<RefPtr<EventData>> events;

    void Serialize(Archive& ar, int version)
    {
        ar & LN_NVP(displayName);
        ar & LN_NVP(meshName);
        ar & LN_NVP(events);
    }
};
{
    "displayName": "map1",
    "meshName": "MapMesh1.mqo",
    "events": [
        {
            "name": "EV000",
            "x": 1.0, "y": 1.0, "z": 1.0,
            "textureName": "chara_1.png",
            "commands" : [100, 101, 102]
        },
        {
            "name": "EV001",
            "x": -1.0, "y": 1.0, "z": 1.0,
            "textureName": "chara_1.png",
            "commands" : [100, 101, 102]
        }
    ]
}
MapData data = JsonSerializer::Load<MapData>(Assets::LoadText("MapData.json"));
data.events[1]->name;   // => "EV001"

とりあえずマップデータとかをツクールMVライクな JSON で用意していく方向で。

ビルド方法

Lumino のビルド方法を簡単にまとめました。 https://github.com/lriki/Lumino/wiki/BuildingWindows