Blender ShaderでAttribute変数

blender-shader-attribute.png
Properties window…..Blenderの右端にあるメニューのことです。
3Dオブジェクトを選択したら、それに対して各々細かい設定をここですることができます。
Properties Windowで今回はこの3つに注目します。
object-data.png Object Data Tab
material.png Material Tab
texture.png Textures Tab

すべてのオブジェクトは、毎フレームOpenGLに送られます。
このことをdraw callと言います。描画するための関数呼び出しというかんじです。
各Draw callはオーバーヘッドがかかります。ちょっと呼び出すのに時間コストがかかるということです。
なので、小さいメッシュを何回もdraw callするよりも、1つにまとめた大きなメッシュで1回draw callする方が効率的です。
このメッシュデータは、大抵は長い三角形のリストでできています。
各三角形は3つの頂点でできています。三角形だから当たり前だけど。。
そして、各頂点はある属性(attribute)を持っています。
属性というのは、x,y,zという位置情報はもちろん入ってます。
これらの頂点属性はアトリビュート変数によって頂点シェーダであれやこれやいじくることが出来ます。

Blenderで使用しているOpenGLで組み込みアトリビュート変数

  • position
  • color
  • surface normal
  • texture coordinates

これらは最初から用意されているので、自分で定義しなくても良い。
OpenGLの “compability profile”によって定義されている。
これらは以下の名前で使うことができます。

   attribute vec4 gl_Vertex; // オブジェクト座標での頂点の位置
   attribute vec4 gl_Color; //頂点の色
   attribute vec3 gl_Normal; //面法線(オブジェクト座標で0〜1の範囲に正規化されています)
   attribute vec4 gl_MultiTexCoord0; //テクスチャ座標の0番目のセット(つまりデフォルトのやつ) uv座標で0〜1の範囲。
   attribute vec4 gl_MultiTexCoord1; //テクスチャ座標の1番目のセット
   ...

OpenGLの組み込みアトリビュート変数にないもので、Blenderにはあるアトリビュート変数があります。例えば、タンジェントベクトルです。
タンジェントベクトルとは面の法線に対して直角なベクトルのことです
こういうものはどうやってやればいいかというと、次のようにします。
#coding: UTF-8
import bge#blender game engin の略です
 
cont = bge.logic.getCurrentController()
 
VertexShader = """
   varying vec4 color;
   attribute vec4 tangent; //Blenderに存在するがOpenGLのデフォルトにないアトリビュート変数 
      // なので頂点シェーダで明示的に定義します
 
   void main()
   {
       color = gl_MultiTexCoord0; // set the varying to this attribute
 
       //以下のコードをコメントアウトして色々実験してみましょう〜★
 
       // color = gl_Vertex;
       // color = gl_Color;
       // color = vec4(gl_Normal, 1.0);
       // color = gl_MultiTexCoord0;
       // color = gl_MultiTexCoord1;
       // color = tangent;
 
       gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   }
"""
 
FragmentShader = """
   varying vec4 color;
 
   void main()
   {   
      gl_FragColor = color;
   }
"""
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
    shader = mat.getShader()
    if shader != None:
        if not shader.isValid():
            shader.setSource(VertexShader, FragmentShader, 1)
            shader.setAttrib(bge.logic.SHD_TANGENT)

この行に注目!
shader.setAttrib(bge.logic.SHD_TANGENT)

このPythonスクリプトでは、Blenderにtangent attributeのあるシェーダをあげますよ、と伝えているのです。
これだけではアトリビュート変数を使うことはできません。
BlenderのProperties Windowである設定をする必要があります。
object-data.pngObject DataタブでUV Mapsを指定してやります。
+ボタンを押すだけです。
uvmapplus.png
マテリアル(物質の素材のこと)は material.pngMaterialタブで指定します。
テクスチャ(3Dオブジェクトに
貼り付ける画像)はtexture.png|Texturesタブで指定します。
この例ではgl_FragColorにgl_MultiTexCoord0を代入してみて、どんなテクスチャ座標がBlenderから来てるのか見てみましょう。
このテクスチャ座標はBlenderではProperties windowでセットします。
ためしにやってみたらこんな風になりました
tangent.png
青が出てるってことは、tangent attributeはz成分にだけ値が入ってるということなんですかね?
変なの。

偽カラーの画像をどう解釈するか?

このように、本来の出したい色じゃない、デバッグのために出した色の画像を、ここでは
「偽カラーの画像」と呼んでいます。
このgl_FragColorに適当な値を入れて出てきた画像をどう解釈するのか?
これがシェーダのデバッグの肝心なところです。
例えば、もしattribute gl_MultiTexCoord0が、フラグメントの色に書かれたら、
色の赤の要素はgl_MultiTexCoord0のx要素を示しています。
もし、マゼンタや黄色や赤が見えたら、これらも赤要素がmaxの1です。
一方、もし青や緑やシアンが見えていたら、これらの赤要素はminの0です。

            color = vec4(gl_MultiTexCoord0.x,gl_MultiTexCoord0.y, 0.0, 1.0);

このようにすることによって、テクスチャ座標がどんな値が来るのか可視化することができます。
私の環境ではこんなかんじになりました。
texcoordxy.png
赤い色の角が座標(1,0)になってるということですね。
テクスチャ座標の値はObject Dataタブで指定されたUV Mapsで決まります。
テクスチャのマッピングの仕方はTexturesタブで決まります。
テクスチャ座標は通常0〜1の範囲なので、何も手を加えずして色に変換して可視化することができます。便利です。
同様に、法線ベクトルも-1〜+1の範囲なので、ちょっと手を加えれば簡単に可視化できます。
次のようなコードでいけます。
            color = vec4((gl_Normal + vec3(1.0, 1.0, 1.0)) / 2.0, 1.0);

こんな風になりました。
normal.png
なんか、私の思ってたのより色が薄い気もするけど。。。
gl_Normalは3次元ベクトルです。

さて、もし範囲が0〜1や-1〜+1ではないデータを可視化したい場合は、
そのみたいものを0〜1の範囲にすれば良いのです。
なにか見たい値があったら、いろいろ試してみましょう。
0〜1の範囲を越えてたら、自動的に0〜1にクランプされます。

シェーダのデバッグ手法

シェーダのデバッグの練習をするためには、
頂点シェーダで色を割り当てた時、なにかのバグが原因で描画結果が黒い色になってしまったとします。
そこで、ソースコードのどの行が悪さをしてるか突き止めましょう。
確信のない値をすべて、色として出力させて確かめてみましょう。

            color = gl_MultiTexCoord0 - vec4(1.5, 2.3, 1.1, 0.0);
            color = vec4(1.0 - gl_MultiTexCoord0.w);
            color = gl_MultiTexCoord0 / tan(0.0);

次のコードは、ちょっと内積や外積の知識が必要です。
            color = dot(gl_Normal, vec3(tangent)) * gl_MultiTexCoord0;
            color = dot(cross(gl_Normal, vec3(tangent)), gl_Normal) * gl_MultiTexCoord0;
            color = vec4(cross(gl_Normal, gl_Normal), 1.0);
            color = vec4(cross(gl_Normal, gl_Vertex), 1.0);

radians()関数が常に0を返している??
            color = radians(gl_MultiTexCoord0);

フラグメントシェーダの特別な変数

Attributesは頂点に対する特別な変数でした。
アトリビュート変数は各頂点ごとに違う値を持ってるのが普通です。
フラグメントシェーダにも似たような特別な変数があります。
各フラグメントごとに異なる値を持つ変数です。
varying変数とも違う存在です。varying変数は頂点シェーダで渡される値ですからね。
たとえばgl_FragCoordはスクリーン座標(ウィンドウ座標ともいう)を示しています。
さらにgl_FrontFacingは is provided that specifies whether the front face or the back face of a triangle is being rendered. Front faces usually face the “outside” of a model and back faces face the “inside” of a model; however, there is no clear outside or inside if the model is not a closed surface. Usually, the surface normal vectors point in the direction of the front face, but this is not required. In fact, front faces and back faces are specified by the order of the vertex triangles: if the vertices appear in counter-clockwise order, the front face is visible; if they appear in clockwise order, the back face is visible. An application is shown in the tutorial on cutaways.

Summary

Congratulations, you have reached the end of this tutorial! We have seen:

The list of built-in attributes in Blender: gl_Vertex, gl_Color, gl_Normal, gl_MultiTexCoord0, gl_MultiTexCoord1, and the special tangent.
How to visualize these attributes (or any other value) by setting components of the output fragment color.
The two additional special variables that are available in fragment programs: gl_FragCoord and gl_FrontFacing.

サポートサイト Wikidot.com