音ゲーの出来ない雑魚のブログ

LWJGLとかのことをいうブログ

「moreover lejgl」8 プログラマブルシェーダ

現代的な方法でテクスチャを表示するには、「プログラマブルシェーダ」という物が必要になるので、それをやってからにします。

元々OpenGLはプログラムの内部に「シェーダ(陰影処理、グラデーションとか)」という物があって、それらはOpenGLが決めた「固定機能(マテリアル、ライトとか)」を組み合わせたりして、物を描画するようにしていました。

しかし、3DCGでもっと複雑なことをしたいというユーザーのニーズに応えるために、KHRONOS Group固定機能を廃止する方針にして、「プログラマブルシェーダ(プログラミングできるシェーダ)」を推奨するようになりました。

そうして、OpenGL3.2からは完全に固定機能を廃止しておりプログラマブルシェーダ以外に描画する方法がなくなりました。


それで、その「プログラマブルシェーダ」は、プログラミングできると言っているので、様々なシェーディング言語によって書くことが出来ます。その中で、OpenGLが対応しているのが、

  • GLSLOpenGL Shading Language): OpenGL2.0で標準機能になった公認のシェーディング言語
  • Cg : NVIDIA社の作ったシェーディング言語

今回は、GLSLを使います。
また、プログラマブルシェーダには、次の2種類があって…

  • 頂点シェーダ(Vertex Shader): 頂点ごとの処理が出来るシェーダ
  • フラグメントシェーダ(Fragment Shader): 画素(ピクセル)ごとの処理が出来るシェーダ。ピクセルシェーダとも言う

この2つはどちらも不可欠で、片方がないとちゃんと描画してくれません。それは、頂点シェーダの情報をフラグメントシェーダでも使うからです。

f:id:cakkby:20160406030042p:plain
こんな感じ…

実は、シェーダにはもう一つあって、「ジオメトリシェーダ」と言います。これは、1オブジェクト(ポリゴン)に対する処理をすることが出来ます。ただ、今回は使う必要が無いので、なしです。

とりあえず、シェーダを使ったプログラムを…

Test5_Shaders.java

shaderV.vert / shaderF.frag


シェーダのほうは****…***(50個)の上が頂点シェーダ、下がフラグメントシェーダです。

解説

Test5_Shaders.java

36,37行目 : シェーダのパスです。基本的に、シェーダの拡張子はなんでもいいのですが、習慣的に頂点シェーダは".vert"。フラグメントシェーダは".frag"となっているようなので、一応そのかたちにしてあります。中身はただのテキストファイルと同じです。

39~41行目 : シェーダとプログラムを管理するIDです。

137行目~ : シェーダを読み込んで、コンパイルし、プログラムにアタッチさせ、最後にそのプログラムを使うというメソッドです。

139行目 : 頂点シェーダのIDに、シェーダを頂点シェーダとして作成します。

int glCreateShader(int type)

GL_VERTEX_SHADER
シェーダが頂点シェーダであることを示します。

140行目 : loadShaderメソッドで読み込んだ頂点シェーダ(shaderV.vert)のデータを頂点シェーダのIDのソースとしています。

void glShaderSource(int shader,CharSequence code)

141行目 : シェーダをコンパイルしています。

void glCompileShader(int shader)

コンパイルを実行時にやるのは、GPUごとの性能の差を隠蔽するためだそうです。

142~146行目 : シェーダが正しくコンパイルされたかどうかを確認しています。

143行目 : 頂点シェーダの状態を取得しています。

void glGetShaderiv( . . . )

第2引数で状態を指定し、第3引数のIntBufferに値が返されます。第2引数には次のようなものが有ります。

  • GL_SHADER_TYPE : シェーダのタイプを取得します。頂点シェーダ(GL_VERTEX_SHADER)かフラグメントシェーダ(GL_FRAGMENT_SHADER)が得られます。
  • GL_DELETE_STATUS : シェーダをちゃんと削除(プログラム上から)出来たかどうかを取得します。返るのは、GL_TRUE/FALSEです。
  • GL_COMPILE_STATUS : シェーダがちゃんとコンパイルされたかどうかを取得します。返るのはGL_TRUE/FALSEです。
  • GL_INFO_LOG_LENGTH : シェーダのインフォメーションの長さを返します。(インフォメーション自体は返しません)
  • GL_SHADER_SOURCE_LENGTH : シェーダのソースコードの長さを返します。

最後の2つは使い方がわかりません。とりあえず、あるって感じでしょうか?

145行目 : 頂点シェーダのインフォメーションを取得して、String型で返しています。

String glGetShaderInfoLog(int shader)



フラグメントシェーダは、上の頂点シェーダの解説の「頂点」を「フラグメント」に変えただけです。つまり、同じ処理です。飛ばします。


158行目 : プログラムのIDに新しくプログラムを作成します。

int glCreateProgram()

さっきから「プログラム」を連発していましたが、この「プログラム」はJavaプログラムとかではなく、OpenGL上のプログラム(Program Object)です。
この「プログラム」はシェーダなどをアタッチ(Attach,関連付け)して、その中の値なども「プログラム」中で使えるようにした、処理のまとまりです。Javaプログラムと似てますね。というか、同じ意味だと思います。(?)

Javaプログラムのように、プログラムが違っていれば、シェーダで同じ変数名などを使っても大丈夫です。

160,161行目 : プログラムに頂点シェーダとフラグメントシェーダをアタッチしています。

void glAttachShader(int program,int shader)

163,164行目 : アタッチし終わったシェーダはもう使わないので、メモリから消します。

void glDeleteShader(int shader)

166行目 : 先ほどのプログラムをプログラムオブジェクトにリンク(設定)しています。これがないとプログラムとして認められません。

void glLinkProgram(int program)

167~171行目 : プログラムのリンクが成功したかどうかを確認しています。

168行目 : プログラムの状態を取得しています。

void glGetProgramiv( . . . )

頂点シェーダの時と同じで、第2引数の状態を第3引数に渡しています。第2引数は下のようなものが有ります。(多いので、いくらか書いてません。それらが必要になったらその時書きます)

  • GL_DELETE_STATUS : 削除できたかどうかです。返り値はGL_TRUE/FALSE。
  • GL_LINK_STATUS : リンクが成功したかどうかです。返り値は上と同じ。
  • GL_VALIDATE_STATUS : 今のプログラムが実行可能かどうかです。返り値は上と同じ。
  • GL_ATTACHED_SHADERS : アタッチされているシェーダの数です。
  • GL_ACTIVE_ATTRIBUTES : アクティブな属性の数です。
  • GL_ACTIVE_UNIFORMS : アクティブな定数の数です。

色々有ります。属性とか定数とかは、後で使っていくので今はいいません。

173行目 : 今描画に使うプログラムを定義したプログラムにします。

void glUseProgram(int program)

違うシェーダをアタッチしたプログラムに変えれば、違う結果になるということです。

描画は、VAOの時と同じです。また、loadShaderメソッドは、IOで1行ずつ読み込んで、改行を挟んでを繰り返しています。

shaderV.vert / shaderF.frag


シェーダはC言語に似ているので、main(void)が実行されるんだとかわかります。GLSLは桃色で表します。また、頂点シェーダは"v"、フラグメントシェーダは"f"を行数の前に入れておきます。

v4行目 : gl_Positionは頂点データを渡す組み込み変数です。今回は使ってませんが、モデルビュー変換というものをした時でも頂点がちゃんと反映されるようになっています。基本的には、頂点の座標をそのまま代入しているだけです。

f11行目 : gl_FragColorは画素の色です。vec4(Vector4)は4個のFloat値を保持する型のことです。ここでは、R:0.9 G:0.5 B:0.9 A:1.0の色合いになるようにしています。この時のRGBAの最大値は1.0です。





いちおうこれでプログラムが動くはずです。実行結果がこちら
f:id:cakkby:20160406034941p:plain
(つまらな…)