『RustではじめるOpenGL』をMacで写経する

# はじめに
『[RustではじめるOpenGL] (https://www.amazon.co.jp/Rust%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8BOpenGL-%E6%8A%80%E8%A1%93%E3%81%AE%E6%B3%89%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%EF%BC%88NextPublishing%EF%BC%89-%E5%B1%B1%E5%8F%A3-%E8%81%96%E5%BC%98-ebook/dp/B084ZC9WF8)』は、Windows, Linux を想定プラットフォームとして書かれています。そのため、Macで写経してもその通りに動かない部分があります。この記事は上記の本の内容をMacで実施するために必要な変更のメモです。
とはいえ、実際にはMac起因ではないところもあるかもしれません。とりあえず「私の場合はこれで動いた」という内容です。

# 第２章
## SDLのインストール
本書では説明されていませんが、シンプルに`brew install sdl2` で入ります。

# 第3章
## フラグメントシェーダー`shader.fs`のコンパイルでエラーになる
本の通りにコーディングして`cargo run`するとコンパイル時に以下のエラーが出ます。
> Use of undeclared identifier 'gl_FragColor'

GLSL ES 3.00には`gl_FragColor`は存在しないらしいです。（[参考](https://stackoverflow.com/questions/26695253/when-switching-to-glsl-300-met-the-following-error))
以下のように出力変数を自前で定義すればコンパイル通るようになります。

```diff_glsl:shader.fs
#version 140

in vec3 FragPosition;
+ out vec4 FragColor;

void main()
{
- gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
```

# 第6章
## バーテックスシェーダー`shader.vs`のコンパイルでエラーになる
本の通りにコーディングして`cargo run`するとコンパイル時に以下のエラーが出ます。
> Invalid call of undeclared identifier 'inverse'

詳細はわからないですが、コードの冒頭で指定するGLSLのバージョンを上げるとコンパイルが通るようになります。
ちなみに`#version 150`(= version 1.5)でも一応コンパイル通りますが、次のワークアラウンドがversion 3.3以上が必要なのでこうしてます。

```diff_glsl:shader.vs
- #version 140
+ #version 330

in vec3 iPosition;
in vec3 iNormal;
in vec2 iTexCoords;

// (以下略）
```

## 出力結果が想定と違う
立方体が表示されるはずなのに、以下のように上側の一面しか表示されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91332/ebb64764-ce41-22c5-926c-f00c9210f1e2.png)

確認したところ、頂点配列オブジェクトの順番が想定と違う順番で解釈されていたようです。
頂点配列オブジェクトは`main.rs`内で以下のように定義されており、1行が一つの頂点を表しており、最初の3要素が頂点のxyz座標（3次元）、法線ベクトル（3次元）、テクスチャー座標（2次元）ということになっています。

```rust:main.rs
fn main(){
    // (略)
    // set buffer
    #[rustfmt::skip]
    let buffer_array: [f32; BUF_LEN] = [
        // 1
        0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0,
        1.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0,

        0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0,
        1.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0,
        1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 1.0,
    // (略)   
```

ですが、なぜか私のコードでは頂点属性の順番が、法線ベクトル、頂点座標、テクスチャー座標となってしまっていたようです。

バーテックスシェーダーで頂点属性の順番を明示的に指定すると想定通りに表示されます。

```diff_glsl:shader.vs
#version 330
- in vec3 iPosition;
- in vec3 iNormal;
- in vec2 iTexCoords;
+ layout(location = 0) in vec3 iPosition;
+ layout(location = 1) in vec3 iNormal;
+ layout(location = 2) in vec2 iTexCoords;
// (略)
```

想定通り表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91332/9902e9b8-ca55-81f3-0579-44e857ede409.png)

# 第7章
## 出力結果が想定と違う その2
先程と同様に立方体が1つ画面の中心に表示されるはずなのに、なぜか立方体が4つ画面の右上に表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91332/66901a1d-2f51-1504-a827-49fcfe59fd72.png)
今度はフレームバッファーに描画する頂点属性の順番が逆になってしまっているようです。
先程と同様に、頂点属性の順番を明示的に指定すると想定通りに表示されます。
フレームバッファーのバーテックスシェーダーのファイルは全部で4つあるので、全部同じ変更を入れる必要があります。

```diff_glsl:screen_shader.vs
- #version 140
+ #version 330

- in vec3 iPosition;
- in vec2 iTexCoords;
+ layout(location = 0) in vec3 iPosition;
+ layout(location = 1) in vec2 iTexCoords;
// （略）
```

想定通り表示されました。（画面は球体エフェクトがかかった状態）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91332/e98bcdf3-3916-fbbe-ba52-da91c0fe25eb.png)
# さいごに
本の「使用するライブラリーとプラットフォーム」の部分に、
> 本書が解説するプラットフォームは、LinuxとWindowsです。MacOSXでの動作は未検証です。もしそのままのコードで動作しなかったとしても、使用しているSDLやOpenGLがクロスプラットフォームなライブラリーなので、小さな修正で動作できるようになると思います。

と書いてあった通り、Mac でも少し修正すれば動作できました。ですが、この**少しの修正**がわからずに途中で投げてしまう人も、もしかしたらいるかもしれません。本記事がそうした方の助けになれば幸いです。