OpenGLとRustで3Dモデルの表示 [42 Tokyo]

## はじめに

42の課題で、OpenGLとRustで3Dモデルの表示を行ったため、その備忘録として書いています。

![teapot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533384/7f77f2e6-92c7-a66b-2b62-98109f3d503c.png)


課題全体の解説というわけではない点についてはご了承ください。

作成したものはこちらにあります。

https://github.com/kotabrog/SCOP

また42の他の活動についてはこちらにありますので、42自体に興味がある方はぜひこちらもどうぞ。

https://note.com/kotabrog/n/n292c0b5ef071

ちなみに前回解いた課題の記事はこちらになります。

https://qiita.com/Kotabrog/items/7e420b9d318ffb5c0c3d


## 作ったもの

ということで今回作ったものは、3Dモデルの表示を行うプログラムになります。

機能としてはだいたい以下のような感じになります。

- ファイルの読み込み
    - .obj形式のファイル：ブレンダーなどで出力できるモデルのデータ
        - 対応しているのはv, f(頂点インデックスのみ)
    - .bmp形式のテクスチャ画像の読み込み（コード内にパスを書く必要がある）
        - Gray, BGR, BGRAの読み込みが可能
- 3d モデルの表示
    - デフォルトの色は白黒
    - いろいろな色に変更可能（外から指定する仕組みは作ってない）
    - ボタンを押すとテクスチャの付け外しができる（外からどのテクスチャにするかの指定はできない）
    - テクスチャを増やしたりなどの拡張性はある（今回の課題では必要なかったのでそこまでやらなかった）
- 3dモデルの移動
    - 平行移動
    - 回転移動

どんな感じなのかは、冒頭にあげたgithubに飛んでもらうと、動画があるので、それを見ていただくのがいいかもしれません。

## 作った手順

ここからはどのような手順で作成したかを書いていきます。

### チュートリアル

まずは以下のチュートリアルをさらっと読みました。

http://nercury.github.io/rust/opengl/tutorial/2018/02/08/opengl-in-rust-from-scratch-01-window.html

今回やりたいこと近しいことが結構詳しめに書いてあったので、こちらを参考にしました。

ただ、途中までしか書かれていなかったり、ちょっと今回の課題に必要なことより詳細な機能がたくさんついているので、そのあとはおそらくこれのもとになっている以下のチュートリアルを見ました。

http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-1-opening-a-window/

### MVP

これらのチュートリアルにそって、環境を整えたり、画面を表示したり、立方体を表示したり……とスムーズにいき、続いてMVP（ModelViewProjection）や平行移動、回転などを行列を使って行いました。

http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-3-matrices/

今回は透視投影という遠くのものが小さく見えるような、目で立体を見たときと同じような感じで見える方法を使い、以下を参考にしました。

https://qiita.com/ryutorion/items/0824a8d6f27564e850c9

また、視線の変換は今回あまり行っていないのですが、以下を参考にしました。

https://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20090902

回転はこちらにあるロドリゲスの回転公式を使って任意の軸でくるくると回転できるようにしました。

https://risalc.info/src/rodrigues-rotation.html

この回転で問題が起こりました。

回転を何回も行うと、物体がどんどん潰れていってしまうのです。

調べてみると、物体をくるくる回転させる処理は、回転行列をどんどんかけることで行っていたのですが、回転行列をかけ続けると、どんどんその行列が回転行列が持つべき性質を保てなくなり、例えば値がどんどん大きくなるなどして変なことになっていたようでした。

そのため、回転行列が持つべき性質を調べ、最終的にシュミットの直交化法で誤差でずれていった回転行列の値をちゃんと回転行列の要件を満たすようにしました。

https://www.sky-engin.jp/blog/rotation-matrix/

https://oguemon.com/study/linear-algebra/orthonormal-basis/

ただ、実際はおそらく回転角を保持しておけばそれでよく、ちょっと遠回りなことをしてしまったかもしれません。（回転角で保持しておく方法は調べていません）

### objファイルや頂点インデックスについて

続いて以下などを読んでobjファイルを読めるようにしました。

https://ja.wikibooks.org/wiki/OpenGL%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/Modern_OpenGL_Tutorial_Load_OBJ

http://asura.iaigiri.com/OpenGL/gl15.html

またファイルの読み込みにあたって、点のつながりをindexで指定する方法についても学びました。

https://tkengo.github.io/blog/2015/01/03/opengl-es-2-2d-knowledge-2/

https://qiita.com/y_UM4/items/8b87e82c66c185905553

### テクスチャについて

続いて、面に貼り付けるテクスチャについて色々とみて実装しました。

http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-5-a-textured-cube/

http://wisdom.sakura.ne.jp/system/opengl/gl23.html

https://nn-hokuson.hatenablog.com/entry/2014/02/13/232145

https://wgld.org/d/webgl/w028.html

https://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20090828

http://www.opengl-tutorial.org/jp/intermediate-tutorials/tutorial-9-vbo-indexing/

なかなか一筋縄ではいかず、一番苦戦したポイントになります。

特に、これは一つ前の頂点インデックスについての内容にもなりますが、4つ以上の頂点を使った面の表現がうまくできず、GL_QUADS, GL_QUAD_STRIP, GL_POLYGONSなどを指定することで4つ以上の頂点にも対応できることを知ったのですが、これらはOpenGL 3.0 以降では使えず、代わりにGL_TRIANGLE_FANが使えそうということで落ち着きました。

### テクスチャの読み込み機構の作成

上記の「テクスチャについて」の段階では、配列にRGB値を入れてテクスチャとしていたのですが、画像を読み込めるようにしました。

画像を読み込むライブラリは使えなかったので、bmp形式の画像を読み込めるようにしました。

http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-5-a-textured-cube/

https://qiita.com/garkimasera/items/03ef1c2d2099e0b5ba53

https://ja.wikipedia.org/wiki/Windows_bitmap

## おわりに

こういったグラフィックなことをするのは結構好きなので、今後も色々と趣味で作りたいなぁと思いました。

特に参考にした資料の中だと、こちらがとてもわかりやすかったので、読みながら一通りRustで実装したいなぁと思いました。

https://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20040913