Cg言語?


~nVidiaの野望は達成されるか?~






■2002年10月16日にCg Toolkit Beta 2.1への対応

spin紹介されたので、現在のバージョンで最新のCg Toolkit Beta 2.1へ対応をしました。
最初のバージョンとの違いは、頂点宣言やリンクファイル、シェーダの入力構造体になります。
一応古いのをこちらに置いておきます(必要ある人はいないと思いますが…)。

■はじめに

ミーハーなので、「プログラミング言語Cg」に手を出してみました。
結果は特に変わることがないので、いつもと見た目は変わりません(新しいモデルを作ってみましたが)。
具体的なレンダリングとしては、テクスチャーを張った3Dモデルを扱っています。
光源計算はしていません。(ここを見ておられる方なら、すぐに拡張は可能でしょう)。

今回のソースは、次のものです(DirectX8.1用です)。

まぁ、いつものように適当にファイルが入っています。

vs.cg頂点シェーダー。「Cg言語」
ps.cgピクセルシェーダー。「Cg言語」
draw.cppメインの描画部分。セットアップなどを行う。
bg.cppメッシュを作ったり描画したり。
draw.h描画の各関数の定義。特に意味無いので出番無し。
main.h基本的な定数など。今回も出番無し。
main.cpp描画に関係しないシステム的な部分。変更が無いので、出番無し。
earth.bmp (地球テクスチャー)
tile.bmp (床デカール)
sky.bmp (空デカール)

あと、モデルと、実行ファイル及び、プロジェクトファイルが入っています。
今回、トーンの模様は算術計算して求めたので、専用のトーンはありません。

■準備

まずは、『Cg Toolkit』を手に入れて、インストールしましょう。
NV Effects Browser が Cg Browser の形であとを引き継がれたりしています。
で、プログラムに入るのですが、インクルードやライブラリをいつものように、追加します(下の画像の一番下のやつね)。




次に「プロジェクト」―「設定」―「リンク」に、ライブラリ「cg.lib, cdD3D.lib」を追加します(Cg Toolkit Beta 2.1 からcg.libが必要になりました。図は古いものなので、cg.libが抜けています)。


とりあえず準備は終わりです。あとは、プログラムの変更になります。


後は、警告が出るのを防ぐために、「プロジェクトオプション」に「/nodefaultlib:"LIBC"」を追加します。
以上で、コンパイルが出来ると思います。

■頂点シェーダー

それでは、頂点シェーダープログラムの「vs.cg」を見ましょう。
「Cg」言語では、
 1)入力の型の宣言
 2)プログラムコード
が必要です。

入力される型は、実際に頂点バッファで確保される型です。
ここでは、入力も出力も4次元の頂点座標と、2次元のテクスチャー座標を指定しました。
但し、頂点シェーダに入力される時点では、4次元ベクトルとして扱います。
テクスチャ座標の残り2次元は、適当に補完されて入力されます(確か1にセットされるはず)。
「float4」など、変わった名前をしていますが、直感に従えばわかるでしょう。
出力するときの型は vf20 で、HPOS は頂点座標に使われるなど、決められています。
プログラム自体は、座標を透視変換して、テクスチャー座標はそのままコピーしています。
mul()は、掛け算命令です(正確には、ベクトルを行列に作用させているのですが)。
引数に行列を渡しているところだけ、後で出てきますから、覚えておいてください。

0001: //
0002: // テクスチャーを張る頂点シェーダープログラム
0003: //
0004: 
0005: // ----------------------------------------------------------------------------
0006: // 入力用構造体
0007: // ----------------------------------------------------------------------------
0008: struct appdata {
0009:     float4 position  : POSITION;
0010:     float4 texcoord0 : TEXCOORD0;
0011: };
0012: 
0013: // ----------------------------------------------------------------------------
0014: // 頂点シェーダープログラム
0015: // ----------------------------------------------------------------------------
0016: vf20 main(appdata I
0017:         , uniform float4x4 worldviewproj_matrix
0018: ) {
0019:     vf20 O;
0020:     
0021:     // 行列をかけて、頂点をスクリーン座標に変換する
0022:     O.HPOS = mul(worldviewproj_matrix, I.position);
0023: 
0024:     // テクスチャー座標はそのままコピーする
0025:     O.TEX0 = I.texcoord0;
0026: 
0027:     return O;
0028: } 

「uniform」など、まだまだ説明できないところもありますが、それはおいおい。

■ピクセルシェーダー

ピクセルシェーダープログラムですが、入力、出力の型情報とプログラムで成り立っています。
入力の型は、頂点シェーダの出力から座標を取り除いた構造体を指定します。
HPOS は、プログラム中には陽には出てきませんが、これは今までにピクセルシェーダーのプログラムをした人はわかると思いますけど、 そうでない人は、ぜんぜんわかんないんじゃないかな。
プログラム自体は、tex0 のテクスチャーを texcoord0 で読み込んで、その色を出力します。

0001: // ----------------------------------------------------------------------------
0002: // 頂点シェーダーからの入力構造体
0003: // ----------------------------------------------------------------------------
0004: struct v2f_simple  {
0006:     float4 texCoord0 : TEXCOORD0;
0007: };
0008: 
0009: struct myFragment {
0010:     float4 col : COLOR;
0011: };
0012: 
0013: myFragment main(v2f_simple I
0014:                 , uniform sampler2D tex0
0015: ) {
0016:   myFragment O;   
0017: 
0018:     // テクスチャーの色を読み込む
0019:     O.col = tex2D(tex0);
0020: 
0021:   return O;
0022: } 

(今回は使っていませんが)変数などが気楽に使えるのがいいところです。

■呼び出し方法

実際にcプログラムからの呼び出し方ですが、次のオブジェクトの準備が必要です。

0015: #include "Cg/cgD3D.h"
0016: 
0017: // Cg 環境
0018: cgDirect3D cg;
0019: cgContextContainer * pContextContainer = 0;
0020: // 頂点シェーダー
0021: cgProgramContainer *pVertexProgramContainer = 0;    // 頂点シェーダーを扱うコンテナ
0022: cgBindIter * vertex_mat_iter = 0;                   // ローカル/射影変換を扱う反復子
0023: // ピクセルシェーダー
0024: cgProgramContainer  *pPixelProgramContainer = 0;    // ピクセルシェーダーを扱うコンテナ
0025: cgBindIter * tex0_iter = 0;                         // テクスチャーを扱う反復子

まぁ、最初は、インクルードですが、Cg言語を使うのに、cgDirect3D と、cgContextContainer が必要です。
シェーダープログラムを使うには、コンテナ、cgProgramContainer が必要になります。
あと、引数を使うのに、cgBindIter (Cg言語バインドイテレータ)が必要になります。
なんか、Cg言語はSTL方面の方言が多いようです。
詳細は、おいおい考えていくとして、とりあえず使ってみましょう。

初期化ですが、黄色い部分が、頂点シェーダーでオレンジ色の部分がピクセルシェーダーです。
多いように見えますが、コメントが多いです。
っていうか、コメントが面白いんですよ。
コメントをはずすと、コンパイル結果をメモ帳で開いてくれます。
いやー、最初見た時はほんとに驚きましたよ。
あと、最適化が完全でないのもわかります。

0039: //-----------------------------------------------------------------------------
0040: // Name: InitRender()
0041: // Desc: 初期化
0042: //-----------------------------------------------------------------------------
0043: HRESULT InitRender(LPDIRECT3DDEVICE8 lpD3DDev)
0044: {
頂点バッファの生成など
0047: 
0048:     // プログラマぶるシェーダーを使えるようにする
0049:     cg.AddFilePath("..");
0050:     pContextContainer = cg.CreateContextContainer(lpD3DDev);
0051: 
0052:     // 
0053:     // 頂点シェーダー
0054:     // 
0055:     cgVertexDefinition  vertex_attributes[] = {
0056:         {D3DVSDT_FLOAT4, "position", 0},
0057:         {D3DVSDT_FLOAT2, "texcoord0", 0},
0058:         CGVERTEXDEFINITIONEND
0059:     };
0060: 
0061:     pVertexProgramContainer = pContextContainer->LoadCGProgramFromFile(
0062:         "vs.cg", "Vertex Shader", cgDX8VertexProfile, vertex_attributes);
0063: 
0064:     if (pVertexProgramContainer == NULL) {
0065:         // エラー表示
0066:         const char * listing = pContextContainer->GetLastListing();
0067:         if (listing == 0) listing = "Could not find cgc.exe.";
0068:         cg.NotePad("頂点シェーダープログラムの生成に失敗しました\n\n", listing);    // メモ帳に出力
0069: 
0070:         exit(1);    // 終了
0071:     }
0072:     // 頂点データの反復子と、.cg ファイルの引数を関連付ける
0073:     vertex_mat_iter = pVertexProgramContainer->GetParameterBindByName("worldviewproj_matrix");
0074: 
0075: // ここのコメントをはずすと、生成されたソースコードが表示される
0076: //  const char * object_code = pVertexProgramContainer->GetProgramObjectCode();
0077: //  cg.NotePad("", object_code);
0078:         
0079:         
0080: // ここのコメントをはずすと、入力すべき頂点情報が表示される
0081: //  const char * vert = pVertexProgramContainer->GetVertexDeclaration(); 
0082: //  cg.NotePad("cg program expects the vertex be defined like this:\n", vert);
0083: 
0084:     // 
0085:     // ピクセルシェーダー
0086:     // 
0087:     pPixelProgramContainer = pContextContainer->LoadCGProgramFromFile(
0088:                                 "ps.cg", "test", cgDX8PixelProfile);
0089:     
0090:     if (NULL == pPixelProgramContainer) {
0091:         // エラー表示
0092:         const char * error_text = pContextContainer->GetLastListing();
0093:         cg.NotePad("ピクセルシェーダープログラムの生成に失敗しました\n\n\n", error_text);
0094: 
0095:         exit(1);    // 終了
0096:     }
0097: // ここのコメントをはずすと、生成されたソースコードが表示される
0098: //  const char *pixel_object_code = pPixelProgramContainer->GetProgramObjectCode();
0099: //  cg.NotePad("", pixel_object_code);
0100: 
0101:     // テクスチャーの反復子と、.cg ファイルの引数を関連付ける
0102:     tex0_iter = pPixelProgramContainer->GetTextureBindByName("tex0");
0103:     int t0 = pPixelProgramContainer->GetTexturePosition(tex0_iter);
0104: 
0105:     
0106:     // 不変なレジスタの設定
0107:     lpD3DDev->SetRenderState( D3DRS_ZENABLE, TRUE );
0108:     lpD3DDev->SetRenderState( D3DRS_LIGHTING,  FALSE );
0109:     lpD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE);
0110: 
0111:     return S_OK;
0112: }

具体的には、「LoadCGProgramFromFile」でファイルを読み込みます。
次に、「GetParameterBindByName」で、Cgプログラム中の変数名の名前とイテレータを関係付けます。
また、テクスチャーを指定するために、「GetTexturePosition」でテクスチャーのポインタが使えるような準備をします。
要は、テクスチャーを使うのに、2つの関数がいるということです。
これで、プログラムの外から引数として、パラメータを渡すことができるようになります。

あとは、レンダリングするときに、

まずは、draw.cpp
0139:     // プログラマぶるシェーダーを有効にする
0140:     pVertexProgramContainer->SetShaderActive();
0141:     pPixelProgramContainer->SetShaderActive();
以下、bg.cpp
0244:     //
0245:     // 地球
0246:     //
0247:     D3DXMATRIX m1, m2;
0248:     D3DXMatrixRotationY( &m1, ~0-timeGetTime()/1000.0f );
0249:     D3DXMatrixTranslation(&m2, 0,EARTH_R,0);
0250:     m = m1*m2*mVP;
0251:     D3DXMatrixTranspose( &m, &m );
0252:     pVertexProgramContainer->SetShaderConstant( vertex_mat_iter, &m  );
0253:     pPixelProgramContainer->SetTexture(tex0_iter, pEarthTex);
0254:     lpD3DDev->SetTextureStageState(0,D3DTSS_ADDRESSU,   D3DTADDRESS_WRAP);
0255: 
0256:     lpD3DDev->SetStreamSource(0, pEarthVB, sizeof(MyVertex));
0257:     lpD3DDev->SetIndices(pEarthIB,0);
0258:     lpD3DDev->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, NUM_EARTH_VERTICES, 0 , NUM_EARTH_POLYGONE );

で、シェーダープログラムを使用するように設定します。
SetVertexShader などの関数が不必要になって、その代わりに一般的な「SetShaderActive()」関数で使う宣言をします。
将来的にシェーダープログラムを統合したり、テセレーション用シェーダーを考えてこうしてるんでしょうね。
あと、違いは、「SetShaderConstant」で、イテレータを使って、変数を入れたり、「SetTexture」がイテレータ指定になっています。
あとは、今までのプログラムがそのまま使えるので、楽勝でしょう。

■最後に

PC Watchの「後藤弘茂のWEEKLY海外ニュース」の後藤さんとお話したときに、 「高水準グラフィックプログラミング言語がでれば、今のレンダリングプログラミングの世界がかわりますよ(かなり意訳。言葉ずらは完全に忘れました)」と おっしゃってられたので、早めに挑戦してみました。
手続きは面倒くさいですが(慣れか?)、プログラム内で、変数が使えるのがいいですね。
やっぱり、最適化の余地があるので、追及する人は現在の言語で高速化をねらうのでしょうが、 アセンブラからC言語に移ったように、こちらに流れは進むのでしょうね。




もどる

imagire@gmail.com

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy