原始人のプログラミング日記

println(++C++); // println(C++), ++C;

光と闇が両方そなわり最強に見える

いい感じにできた


f:id:kawazuini:20170921210332p:plain
複数光源のシャドウマッピング

虚像っぽいの

シャドウマッピングにおいて光源視点で全てのポリゴンを視野に収めていない場合、光源方向のちょうど反対側に虚像?(逆さ影)みたいのができてしまう。

曰く、デプスバッファとの比較時に負のデプス値(光源後ろ側)は無限遠の値を取るらしい。
これによって、光源視野外の後ろ側にも影ができてしまう。

推測ではシェーダでさらに判定をかませば解決できそうだけど、めんどくさいので光源のスポットカットでごまかした*1

glslでのテクスチャ系の配列

built-in array must be redeclared with a size before being indexed with a variable
組み込み配列は、変数でインデックスを付ける前にサイズを再宣言する必要があります

配列に変数の添え字を使おうとしたら出たエラー、再宣言の仕方分からなくて適当にfor分を平文に展開した。

シャドウマッピングをすべての光源に対して行おうとした場合に自分の環境だと、光源数が8個に加えてオブジェクトに適用するテクスチャがあるので、テクスチャが合計9枚必要になる。
なので、gl_TexCoord[8]でテクスチャ座標にアクセスしようとしたらアウトオブレンジではじかれた。
これも再宣言すれば解決するのかもしれないけど、めんどくさいので自分で変数を用意してごまかした。

9/22追記>

各シェーダの先頭(他の変数と同様)に

varying vec4 gl_TexCoord[gl_MaxTextureCoords];

を書けばコンパイルが通った。
けど、結局配列の数は固定っぽい?(変えるとエラーになった)

<追記終わり

作った感想

想像の10倍以上時間かかってめんどくさかった


影を作るということで描画の回数が増えたんだけど、そこらへんの柔軟性は考えてなかったのでメインループ一回壊してから再構成した。マルチテクスチャということでシェーダも同じ感じのことした。

glslの書籍とか持ってないのでネットをさまよったけど、自分の欲しい情報が見つからなくて困った。
glslは本買って体系的に理解した方がいいのかもしれない。

ちなみに、とあるOpenGL系のサイトの逆行列の計算式まちがってるので気を付けよう(3時間もっていかれた)。

ソースコードはいまからリファクタリングして上げるかもしれないし上げないかもしれない。

*1:いい方法あったら教えてください

.objとIBO

.objのフォーマット

.objファイルは主に頂点座標とテクスチャ座標、法線の3つの情報から構成されていて、それを表面情報でインデックス付けしている。
これによって同じ座標の頂点はひとまとめにされ、同様にテクスチャ座標なんかも一つにまとめられて扱いやすくなっている。

IBOとの親和性

フォーマット的にインデックス付けされてるし、一番最初にIBOを使おうと考えるんだけど、結論から言えばOpenGLでは無理っぽい。

無理とは言ったけど、たとえば頂点だけを利用して描画する分には可能。
この場合、法線は一方向のみでテクスチャなんかも使えない。

でもせっかく作ったのだから、法線で陰影付けしたいしテクスチャだって貼り付けたい。
この場合、.objでは一つの頂点に対して3つの属性(頂点/テクスチャ/法線)が付与されていて、それぞれが別のインデックスで表されているため一つのIBOでは対応できない。
それならということで、頂点のIBO、テクスチャのIBO、法線のIBOをそれぞれ設定することを考えるがOpenGLではこれができないみたい(OpenGLでは複数のIBOを使用できないらしい*1 )。
つまり、同じ頂点(ここでの同じとは3属性すべてがおなじであること)が限りなく少ない.objファイルではIBOの仕組みを使うことに意味がないということになる。

.objを展開

IBOが使えないということで、頂点情報をインデックスではなく実際の値として展開する必要がでてくる。
つまり

f 3/8/9

v 0.00 -0.44 -1.05/vt 0.09 0.72/vn 0.00 -0.97 0.21

の形式に展開しなければならない。

このとき、モデルのポリゴン数が仮に4000ポリゴンだとすると頂点数は3倍の12000でかなり多い(純粋な頂点数はカエルの場合8000弱)。
メモリの消費が激しいこの方法だが、調べた限りでは展開することが一般的なようだ(展開以外のいい手法がない?)。

問題点

上記の通り、ポリゴン毎に頂点を展開していくとメモリを消費することは妥協するしかないにしても、データの変更にかなりのコストがかかるという問題が生じる。
例えば、本来8000頂点に対してだけ行えばよかった演算を、1.5倍の12000頂点に対して行う必要が生じてしまう(回転の場合、さらに法線にも処理を行うので約3倍以上の処理)。

解決策

展開すること自体泥臭かったが、解決策はもっと泥臭く、自分の考えた方法はインデックスを処理毎に展開しなおすことだ。
つまり回転にしろ移動にしろ、処理を行うのは純粋な頂点(重複のない展開前の頂点)だけにし、処理を行った後に随時描画用の頂点に展開しなおすということだ。
一見遅くなりそうなこの方法だが、自分独自のプログロムのフレームレートでは、展開済みの全頂点に対して処理を行う場合の20fpsから設定値の50fpsまでに上昇した。

終わりに

今回書いたのは、自分の調べた範囲の情報と解決法であり、不確かだったり至らない点が多いと考えられ、多分恥をかくような内容かもしれない。
そもそも、こんな冗長な方法しかなくOpenGLに適切な方法が用意されていないのはにわかに信じがたい。
仮にOpenGLに用意されてなくても上記以上にいい方法がある、もしくはOpenGL自体にこの問題を解決するスマートな方法がある場合、是非とも教えて欲しいし知りたいと思う。

終わり

*1:できたら教えてほしい

普通にできた


f:id:kawazuini:20170902000849p:plain
普通にできた
f:id:kawazuini:20170902000853p:plain
滑らかな気がする

結論としては、objファイルのエクスポートをする段階で法線情報が書き込まれてなかっただけだった(プログラムでは法線情報をポリゴンの法線で補完していた*1 )。
なので、普通にオプション変えて出力したらいい感じになった。

シェーダを疑ってたけどOpenGLではデフォルトでスムースシェーディング有効らしい。
これ以降のクオリティの向上は、マテリアルとかがかかわってくるのかな?

*1:面から法線を割り出すのは一般のプログラムでもそうっぽい?

.objファイルを読み込んでみた


f:id:kawazuini:20170831220437p:plain
法線がない!

f:id:kawazuini:20170831220440p:plain
法線が弱い!

f:id:kawazuini:20170831220447p:plain
ポリゴン丸見え!

ボーンつけるの(どっちかっていうとblenderが)めんどくさいから、代わりにobjファイルを読み込むプログラムを書いていた。
blenderというか結構年季のあるソフト全般ってできること多すぎて、結局何していいかわからなくなるから大変。

objファイルを読み込むとこまではいけたけど、ポリゴンが丸見えになってしまった(足の付け根付近)。
モデリングツールではポリゴンなんか見えずに滑らかな陰影だったのでどんなシェーダ使ってるんだろう?
ポリゴンさえ隠せれば、とりあえずほかは大丈夫かな?

かえるをつくったー

f:id:kawazuini:20170827044937p:plain
初めての3Dモデリングだったので10時間かかりました。
初心者でもここまで作れるなんて素晴らしい時代ですね。

よくわかんないけど、このあとBlenderでボーンとかアニメーションとかつけるんすかね?

Blender、そこまで触ってないけど字が小さくて嫌です。

ゲームをアップロードしてました

PurPose

ゲーム

www.freem.ne.jp

ゲーム概要

3Dのローグライクゲームです。



・・・ゲームの説明はめんどくさいのでリンク先のページでダウンロードして見てください。

この記事は

説明書に連絡先として書いたのに、ブログにゲームのこと書いてなかったから書きました。
なので、不具合とかはこの記事にコメントしてくれるとありがたいです。


本当は

ゲームを宣伝しなかった場合のダウンロード数について書こうと思ってたけど、書けるほどの材料が集まらなかった*1ので諦めました。

ちなみに、アップロード5日目までのダウンロード数の推移

1日目 : 12(+12)
2日目 : 25(+13)
3日目 : 31(+6)
4日目 : 39(+8)
5日目 : 44(+5)

これを見て少ないと思うかもしれないけど、自分の想像より多い。
なんでかっていうと、このブログの閲覧数が20もいってないから。

そう考えると、フリーゲームの需要は結構あるのかな?
それとも夏休みの開始に投稿タイミングが重なったからかな?
多分、後者。

*1:ふりーむだと自分のダウンロード数しか分からない

円筒と球の衝突判定 はじめました

円筒と球の衝突判定って簡単なのでは?

円筒って便利ですよね?

キャラクター同士の衝突判定を考えたとき、「キャラクターの当たり判定を球にして移動の軌跡を円筒にする」っていうのが一番最初にぱっと思いつきますよね?そうでもないですか?

だけどネットにそこまで落ちてない

ネットで衝突判定のアルゴリズム探すんだけど、検索方法が悪いのかいまいち見かけない気がする。

簡単だから

仕方ないので図を書いて考えると、あっさり答えが出てしまった。
なるほど、線分と球の衝突判定をほんの少し拡張しただけなので、あえてネットにまで書く必要がないんですね。(というか、円筒って太さ持った線分だから、そんなの当然の如く分かるでしょ的スタンスなのかもしれない。)

実装は?


f:id:kawazuini:20170728011215j:plain
適当な図(左が線分と球、右が線分から円筒にする考え方)

線分と球の衝突判定
bool KSphere::operator*(const KSegment& aSegment) const {
    KVector vec1(mPosition - aSegment.mVec1);
    KVector vec2(mPosition - aSegment.mVec2);
    KVector seg(aSegment.mVec2 - aSegment.mVec1);
    KVector para(vec1.extractParallel(seg));

    if (seg.length() == 0.0f) { // 線分の長さがないときは点として判定
        return vec1.length() < mRadius;
    }

    //  中心座標から線分に垂らした垂線との交点による内分比
    float t(para.length() / seg.length());

    float dist; // 中心座標と線分の最短距離
    if (0 < t && t < 1) dist = (para - vec1).length();
    else { // 交点が線分上にない(線分の端との距離を計測)
        if (t < 0) dist = vec1.length();
        if (1 < t) dist = vec2.length();
    }
    return dist < mRadius;
}
円筒と球の衝突判定*1
bool KSphere::operator*(const KCylinder& aCylinder) const {
    KSegment seg(aCylinder.mVec1, aCylinder.mVec2);
    if (!aCylinder.mRounding) {
        KVector dir(seg.direction());
        float len(seg.length() / 2);
        KVector center(seg.mVec1 + dir * len);
        float lenDiff(Math::max(0.0f, len - aCylinder.mRadius));
        seg = KSegment(center + dir * lenDiff, center - dir * lenDiff);
    }
    return KSphere(mPosition, mRadius + aCylinder.mRadius) * seg;
}

円筒と球の衝突判定ってどちゃくそ重い

見たらわかるけど、分岐処理があほみたいに多い。
たとえば「キャラクターの当たり判定を球にして移動の軌跡を円筒にする」みたいなことは、処理が重すぎて毎フレームできたものじゃない。
本末転倒だけど、ネットにないのは実用性がないからなのかもしれない。

妥協案として、キャラクターは自分の半径以上1回の処理で動けないってルールを追加するのが妥当な*2気がする。*3
キャラクターを速く動かしたいなら、球と球の衝突判定にしてフレームレート上げましょう!!

いつ使うんですか?

めったに使えたものではないので、銃弾とかの稀に発生するオブジェクトなら使えるかもしれない。
ただ、それでも線分として処理をした方が速い。

極太レーザーならありなのかもしれない。

*1:そこまで検証してない

*2:アニメーション的にも無理がなくなる

*3:もっといい方法、あったら教えてください。