iizukakの作業ログ

忘れる前にメモしよう

Go 言語で OpenGL (その1)

OpenGL を知りたいなと思って Go で OpenGL をさわってみた記事シリーズ第一弾です。

OpenGL のテキストで良いのがないかと色々ググってみたのですが、和歌山大学の先生が公開されている "GLFW による OpenGL 入門 - 和歌山大学" というフリーのテキストが内容がまとまっていて良さそうでした。このテキストでは、実装しつつ OpenGL (3D グラフィックプログラミング)を知ることができるようになっているようで、自分向きっぽい。

とりあえず章ごとくらいの粒度で、備忘録をブログを書いておきたいと思います。

また、実装には Go 言語を使います。あまり言語にはこだわりがないのですが、社内でけっこう流行っている模様。

リポジトリ

OpenGLTutorial にコードはまるっと置いておきます。ライブラリのバージョンやコードの実行方法についても README.md に書いておきます。

コード

4 章のコードはここです。

バーテックスシェーダとフラグメントシェーダ

4 章で最も重要なのは、バーテックスシェーダとフラグメントシェーダという概念です。3D 空間にある図形(頂点の集合)を 2D 平面に投影する、これをラスタライズといい、バーテックスシェーダはこのラスタライズを行う。フラグメントシェーダは 2D 平面に投影された頂点の情報をもとに、ディスプレイに表示する画素情報を生成する、というかんじっぽい。

このバーテックスシェーダおよびフラグメントシェーダは GLSL という言語で記述します。なので Go で書くというのは半分ウソで、シェーダ自体は C っぽい何かである GLSL で書きます。これを CPU で実行するプログラム内(Go で書いてる部分)でコンパイルして、GPU に送り込んで処理をする、という流れみたいですな。

4 章のバーテックシェーダの中身を見てみますと、

#version 150 core
in vec4 position;
void main()
{
    gl_Position = position;
}

で全部です。 in はシェーダへの入力を受け取る変数を宣言する構文。 vec4 は 4 要素のベクトル。 gl_Position = position; は、受け取った座標系を特に加工せずにレンダリングパイプラインの次に渡す、という意味。

次にフラグメントシェーダですが、

#version 150 core
out vec4 fragment;
void main()
{
    fragment = vec4(1.0, 0.0, 0.0, 1.0);
}

で、こちらは out で出力を定義しています。この vec4 では色を定義していて、 RGBA のベクトル。赤です。なので頂点の各画素を赤く表示するシェーダーになってると思います。

Go でシェーダをコンパイルする部分に関しては、 go-glサンプルリポジトリ のコードをまるっと持ってきました。基本的にはシェーダを文字列で gl.CompileShader に渡すだけです。GLSL のコンパイルエラーとか出るので、エラー処理はちゃんとやらないといけない。

   gl.CompileShader(shader)

    var status int32
    gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
    if status == gl.FALSE {
        var logLength int32
        gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)

        log := strings.Repeat("\x00", int(logLength+1))
        gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))

        return 0, fmt.Errorf("failed to compile %v: %v", source, log)
    }

実行結果

実行結果は以下の通り。

f:id:iizukak:20180406095351p:plain

えっ、ただの白い画面だけ? と一瞬思ってしまいましたが、それもそのはずで、まだ図形を定義していないです。処理する頂点が無いので画面が出るだけ。

所感

OpenGL というのはステートフルなツールだなぁと。最近のライブラリだったら、何らかのオブジェクトのインスタンスを作ってインスタンスのメソッドを呼んで〜というのが普通な気がしますが、 OpenGL は自分自身で状態を持っていて、状態を複数持ちたい場合は、整数値で指定する、みたいなことをやるっぽい。ほほう…