スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

日本語では滅多に見ない cg プログラム解説

2006年03月30日 18:00

ってことで、第2部のプログラムネタっす。


あるけ が忘れることは無いとおもうけど、メモ代わりに書いておきます。
日本語で書いてる Cg や CgGL 関連のネタも少ないしね。

Google とかで「cgGLSetParameterPointer」や「cgGLEnableClientState」、「glBindBufferARB」、「glBufferDataARB」、「glGenBuffersARB」、「cgGetNamedParameter」なんかの用語で引っかかって飛んできた人に参考になればうれしいです。


Cg では頂点バッファやインデックスバッファを使ってポリゴンを描画することが出来ます。
ここで、問題となるのが頂点バッファをどうやって持つのか・・・。

Direct X(HLSL)なんかでは頂点宣言を使って1つの頂点がどういうデータで構成されているのかをシェーダーに教えています。
では、Cg ではどうするんでしょうか?


Cg では頂点宣言みたいな機能はありません。
シェーダー内のパラメーター(CGparameter 型)を取得してそのパラメーターにデータを関連付けます。
シェーダー内のパラメーターの取得方法は自動的にやることも出来るんですけど、説明が面倒なのでまた今度書きます。
ってことで、シェーダーの中身を知ってる場合限定で手続き型(要するに自動で判別は無理)ですけど、もう一つの方法を記述します。

cgGetNamedParameter 関数を使います。
この関数はシェーダープログラムの中の指定した名前の変数 or シェーダー定数を抽象化したもの(CGparameter 型)を返します。


帰ってきた CGparameter データを使ってシェーダーにデータを渡すわけです。

Cg では特定のパラメーターにデータを焼べる場合、CGparameter 型を使います。
プログラマーがわかりやすい言葉でいうと、シェーダープログラム内の変数のポインタって感じが近いかな。
もちろん、本当のポインタじゃないのでデータを与える時は関数を読んでやる必要があり、cgSetParameter1fv や cgSetParameter4fv、cgSetMatrixParameterfc なんかを使います。




さて、この CGparameter 、シェーダー内で使用される定数を設定する時に使うことはもちろん、シェーダーに頂点データを渡す場合にも使用します。
シェーダーに定数データを渡す方法は某3では他にもありますけど、PC(っていうか nVidia の開発キット)ではこれしか無いみたいです。
あるけ の探し方が悪いだけかもしれないので知ってる方は教えてくださるとうれしいです。



それで、どうやってデータを渡すか問ういうと、、、

cgGLSetParameterPointer 関数を使います。

この関数を使えば、シェーダーに入力パラメーターを渡せるわけです。
引数の説明とかは省きますが、使ってみればすぐわかるとおもいます。



さて、ここからが本題。
ここまでは別に特別なことは一切ありませんし、ノウハウなんてものも在りません。ほぼ教科書どおりの無いようです。

ここまでの説明でシェーダーにデータを入力するのは出来るんですけど、
これでは頂点バッファをすべてのパラメーターに対して用意するなんてことになっちゃいます。

こんな風に、、、




擬似コードなんで深く突っ込まないでねw
要望があればサンプル書きます。


CGparameter VShaderIN[3];
CGprogram  hVProg, hFProg;
GLuint    pVBO[3], hIBO;


// 頂点シェーダーを読む
hVProg = cgCreateProgram( hContext,
              enSourceType,
              (const S1 *)pBuffer,
              hProfile,
              "VSMain",
              NULL
      );

// フラグメントシェーダーを読む
hFProg = cgCreateProgram( hContext,
              enSourceType,
              (const S1 *)pBuffer,
              hProfile,
              "PSMain",
              NULL
      );

cgGLBindProgram( hVProg );

// 各頂点要素のパラメーターを取得
VShaderIN[0] = cgGetNamedParameter( hVProg, "In.Position" );
VShaderIN[1] = cgGetNamedParameter( hVProg, "In.Normal" );
VShaderIN[2] = cgGetNamedParameter( hVProg, "In.Color" );


// 頂点バッファオブジェクト作成
glGenBuffersARB( 3, pVBO );
glGenBuffersARB( 1, &hIBO );


// バッファを結びつける
glBindBufferARB( GL_ARRAY_BUFFER_ARB, pVBO[0] );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(float) * 3 * nVertexVol,
         pPosition, GL_STATIC_DRAW_ARB );  // 位置
          
glBindBufferARB( GL_ARRAY_BUFFER_ARB, pVBO[1] );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(float) * 3 * nVertexVol,
         pNormal, GL_STATIC_DRAW_ARB );    // 法線
          
glBindBufferARB( GL_ARRAY_BUFFER_ARB, pVBO[2] );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(float) * 4 * nVertexVol,
         pColor, GL_STATIC_DRAW_ARB );      // 色


// インデックスバッファを設定する
glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER, hIBO );
glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * uIndexVol,
         pIndexStream, GL_STATIC_DRAW );



// 描画する
for(;;)
{
  cgGLBindProgram( hVProg );
  cgGLBindProgram( hPProg );
  
  glBindBufferARB( GL_ARRAY_BUFFER_ARB, pVBO[0] );
  cgGLEnableClientState( VShaderIN[0] );
  cgGLSetParameterPointer( VShaderIN[0], 3, GL_FLOAT, sizeof(float) * 3, 0 );
 
  glBindBufferARB( GL_ARRAY_BUFFER_ARB, pVBO[1] );
  cgGLEnableClientState( VShaderIN[1] );
  cgGLSetParameterPointer( VShaderIN[1], 3, GL_FLOAT, sizeof(float) * 3, 0 );
 
  glBindBufferARB( GL_ARRAY_BUFFER_ARB, pVBO[2] );
  cgGLEnableClientState( VShaderIN[2] );
  cgGLSetParameterPointer( VShaderIN[2], 4, GL_FLOAT, sizeof(float) * 4, 0 );
  
  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, hIBO );
  glDrawElements( GL_TRIANGLE_STRIP, uIndexVol, GL_UNSIGNED_INT, 0 );
}






これでは1つの頂点を処理するたびに何回もキャッシュミスを発生してしまい、速度が全然出ません。
出来れば Direct X みたいに頂点バッファは法線や座標データ、接線などのデータが構造体としてまとまっていた状態で配列になっていたほうがキャッシュヒットもしやすく良いに決まってます。


そこで、Cg でも頂点バッファを一つしか使わない方法を教えちゃいます。
って、勿体ぶっても知ってる人は多いかもしれませんw
ちなみに、あるけ が探した限り、こういうサンプルは見たことありませんでした( ;Д⊂ヽ
(これもまた探し方がわr(ry )

まず、Direct X の様に頂点ごとに各要素をパックしたものを用意します。
次に、頂点バッファオブジェクトを一つ作成します。

そんでもって、cgGLSetParameterPointer に渡す最後のパラメーターを頂点構造体の要素ごとのオフセットにします。

ようするに、、、






typedef struct
{
  float  position[3];
  float  normal[3];
  float  color[4];
}
VTXDATA *pVertexStream;


// オフセットを取得
int pOffset[3] =
{
  (int)((VTXDATA *)0)->position,
  (int)((VTXDATA *)0)->normal,
  (int)((VTXDATA *)0)->color
};


・・・・途中まで省略・・・・



// 頂点バッファオブジェクト作成
glGenBuffersARB( 1, &hVBO );
glGenBuffersARB( 1, &hIBO );


// バッファを結びつける
glBindBufferARB( GL_ARRAY_BUFFER_ARB, hVBO );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(VTXDATA) * nVertexVol,
         pVertexStream, GL_STATIC_DRAW_ARB );


// インデックスバッファを設定する
glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER, hIBO );
glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * uIndexVol,
         pIndexStream, GL_STATIC_DRAW );


// 描画する
for(;;)
{
  cgGLBindProgram( hVProg );
  cgGLBindProgram( hPProg );
  
  glBindBufferARB( GL_ARRAY_BUFFER_ARB, hVBO );
  
  cgGLEnableClientState( VShaderIN[0] );
  cgGLEnableClientState( VShaderIN[1] );
  cgGLEnableClientState( VShaderIN[2] );
  
  cgGLSetParameterPointer( VShaderIN[0], 3, GL_FLOAT, sizeof(float) * 3, pOffset[0] );
  cgGLSetParameterPointer( VShaderIN[1], 3, GL_FLOAT, sizeof(float) * 3, pOffset[1] );
  cgGLSetParameterPointer( VShaderIN[2], 4, GL_FLOAT, sizeof(float) * 4, pOffset[2] );
  
  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, hIBO );
  glDrawElements( GL_TRIANGLE_STRIP, uIndexVol, GL_UNSIGNED_INT, 0 );
}





こんな感じに書きます。

動くかどうかはわかりませんがw
ニュアンスで読んでください(´ω`)

blog_rank
スポンサーサイト


コメント

    コメントの投稿

    (コメント編集・削除に必要)
    (管理者にだけ表示を許可する)

    トラックバック

    この記事のトラックバックURL
    http://angra.blog31.fc2.com/tb.php/26-8d709e41
    この記事へのトラックバック


    最近の記事


    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。