OpenGLでCg


~NV30を得るための道~






■はじめに

(少なくとも第2回、第3回の)Cg cording contest で、NV30 をもらえるのは、 NV30 部門だけです。
ぜひチャレンジしましょう。ということで、Cg を動かすためのプログラムを書きました。

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

main.cppこれだけ。

あと、実行ファイル及び、プロジェクトファイルが入っています。

■Cg を動かす

Cg Toolkit をそのままインストールしただけでは、Cg は使えません。
「オプション」-「ディレクトリ」で、ディレクトリを追加します。
私の場合は、

インクルードファイルC:\PROGRAM FILES\NVIDIA CORPORATION\NVSDK\CG\INCLUDE
ライブラリC:\PROGRAM FILES\NVIDIA CORPORATION\NVSDK\CG\LIB

に、それぞれのファイルがあるので、追加しました。

また、プログラムを書くときは、インクルードファイル

0013: #include <Cg/cg.h>
0014: #include <Cg/cgGL.h>

を、追加しましょう。

■Cg プログラム

Cg 言語によるプログラムは、DirectXのものと変わりません。
今回は、頂点シェーダで、透視変換して色は頂点色をそのまま出力します。

struct appdata
{
	float4 position : POSITION;
	float3 normal : NORMAL;
	float3 color : DIFFUSE;
};

struct vfconn
{
	float4 HPOS : POSITION;
	float4 COL0 : COLOR0;
};

vfconn main(appdata IN,
            uniform float4x4 ModelViewProj)
{
	vfconn OUT;

	OUT.HPOS = mul(ModelViewProj, IN.position);

	OUT.COL0.xyz = IN.color.xyz;
	OUT.COL0.w = 1.0;

	return OUT;
}

やっていることは、入力用と出力用の頂点データを宣言しておき、 出力データを作成します。

■Cgオブジェクト

Cgを使うためには、DirectXのように、Cgを使う時のcgContextと、プログラムを格納するcgProgramIterに、変数を結びつけるcgBindIterを用意します。

0016: // ---------------------------------------------------------------------------
0017: // オブジェクト
0018: // ---------------------------------------------------------------------------
0019: 
0020: // Cg のオブジェクト
0021: static cgContext     *Context           = NULL; // Cg の実態
0022: static cgProgramIter *ProgramIter       = NULL; // プログラム
0023: static cgBindIter    *ModelViewProjBind = NULL; // 行列
0024: static cgBindIter    *ColorBind         = NULL; // 頂点色
0025: 
0026: static char CGVertProg[] =
0027:  "\n"
0028:  "struct appdata\n"
0029:  " {\n"
0030:  "  float4 position : POSITION;\n"
以下略

また、今回は、Cg言語プログラムをCソースに埋め込みました。 Cgプログラムは文字列として、格納されます。

■Cgのセットアップ

それでは、Cgの初期化を行いましょう。
最初に、cgCreateContext() で、Cgを使えるようにします。
次に、cgAddProgram で、プログラムを登録します。
登録したプログラムは、cgProgramByName で、開始関数を指定することによって、使えるようになります。また、cgGLLoadProgram で、グラフィックチップにCgプログラムを転送します。
あとは、cgGetBindByNameで、使う変数のイテレータとプログラム内部の名前を結び付けます。

0211: // ---------------------------------------------------------------------------
0212: // Cgの初期化
0213: // ---------------------------------------------------------------------------
0214: static void InitializeCg()
0215: {
0216:     cgError Ret;
0217:     
0218:     // Cg の作成
0219:     Context = cgCreateContext();
0220:     assert(Context != NULL);
0221: 
0222:     // Cg の作成
0223:     Ret = cgAddProgram(Context, CGVertProg, cgVertexProfile, NULL);
0224:     fprintf(stderr, "LAST LISTING----%s----\n", cgGetLastListing(Context));
0225:     assert(Ret == cgNoError);
0226: 
0227:     // プログラム名でプログラムを結びつける
0228:     ProgramIter = cgProgramByName(Context, "main");
0229:     assert(ProgramIter != NULL);
0230:     
0231:     // ソースの表示
0232:     fprintf(stderr, "---- プログラム はじめ ----\n"
0233:                     "%s"
0234:                     "---- プログラム 終わり ----\n",
0235:     cgGetProgramObjectCode(ProgramIter));
0236: 
0237:     if(ProgramIter != NULL) {
0238:         GLuint ProgId = 1;
0239: 
0240:         Ret = cgGLLoadProgram(ProgramIter, ProgId);
0241:         assert(Ret == cgNoError);
0242:         // 変換行列を結びつける
0243:         ModelViewProjBind = cgGetBindByName(ProgramIter, "ModelViewProj");
0244:         assert(ModelViewProjBind != NULL);
0245:         // 頂点色
0246:         ColorBind = cgGetBindByName(ProgramIter, "IN.color");
0247:         assert(ColorBind != NULL);
0248:     }
0249: }

ちなみに、後片付けは、それぞれ適当なものを呼び出します。

0250: // ---------------------------------------------------------------------------
0251: // Cg の後片付け
0252: // ---------------------------------------------------------------------------
0253: static void CleanupCg()
0254: {
0255:     if(ModelViewProjBind)cgFreeBindIter(ModelViewProjBind);
0256:     if(ColorBind)cgFreeBindIter(ColorBind);
0257:     cgFreeProgramIter(ProgramIter);
0258:     cgFreeContext(Context);
0259:     cgCleanup();
0260: }

■メイン関数

それでは、メインループを見てみましょう。
といっても、たいしたものではありません。
OpenGLの初期化をして、先ほど見たCgの初期化をします。
あとは、メインループで処理をさせておいて、終わったら、あと片付けをします。

0261: // ---------------------------------------------------------------------------
0262: // メイン関数
0263: // ---------------------------------------------------------------------------
0264: int main(int argc, char *argv[])
0265: {
0266:     InitializeGlut(argc, argv);
0267:     InitializeCg();
0268:   
0269:     glutMainLoop();                     // メインループ
0270: 
0271:     CleanupCg();
0272:     
0273:     return 0;
0274: }

OpenGL の初期化ですが、ほとんど同じです。
違いは表示する箱オブジェクトの初期化 InitializeCube を追加したことです。

0185: // ---------------------------------------------------------------------------
0186: // OpenGL に関する初期化
0187: // ---------------------------------------------------------------------------
0188: static void InitializeGlut(int argc, char *argv[])
0189: {
0190:     // ウィンドウの作成
0191:     glutInitWindowPosition(100, 100);   // 表示位置
0192:     glutInitWindowSize(512, 512);       // サイズ
0193:     glutInit(&argc, argv);              // GLの初期化
0194:     glutInitDisplayMode(GLUT_DOUBLE     // 描画モード
0195:                      | GLUT_RGBA        // wバッファ+RGBA+深度バッファ
0196:                      | GLUT_DEPTH);
0197:     glutCreateWindow(argv[0]);          // ウィンドウの生成
0198: 
0199:     // コールバック関数の設定
0200:     glutDisplayFunc(display);   // 描画関数
0201:     glutReshapeFunc(resize);    // 画面が変形したとき
0202:     glutIdleFunc(idle);         // ひまなとき
0203: 
0204:     // 箱の初期化
0205:     InitializeCube(CubeVertices);
0206: 
0207:     // 一度だけすればいい設定
0208:     glClearColor(0.0, 0.0, 0.3, 0.0);   // 背景色の設定
0209:     glEnable(GL_DEPTH_TEST);            // 深度バッファの使用
0210: }

箱は、法線ベクトル、頂点インデックス、及び頂点座標で構成されます。
それぞれ、配列として定義しておきます。

0056: // ---------------------------------------------------------------------------
0057: // 箱のオブジェクト
0058: // ---------------------------------------------------------------------------
0059: GLfloat CubeNormals[6][3] = 
0060:  {  
0061:   {-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0},
0062:   {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, -1.0} 
0063:  };
0064: 
0065: GLint CubeFaces[6][4] = 
0066:  {  
0067:   {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4},
0068:   {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} 
0069:  };
0070: 
0071: GLfloat CubeVertices[8][3];  
0072: 
0073: static void InitializeCube(GLfloat v[8][3])
0074: {
0075:     v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
0076:     v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
0077:     v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
0078:     v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
0079:     v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
0080:     v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;
0081: }

■描画

では、描画部分です。
基本的には、前回のポリゴンを描画しているところと変わりありません。
glPushMatrix で、ビュー行列を退避したあと、回転行列を使ってぐるぐる回すワールド行列を作成したあと、箱を表示します。

0130: // ---------------------------------------------------------------------------
0131: // 画面描画
0132: // ---------------------------------------------------------------------------
0133: void display(void)
0134: {
0135:     // 画面のクリア
0136:     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
0137: 
0138:     // ビュー行列の設定
0139:     glMatrixMode(GL_MODELVIEW);
0140:     glLoadIdentity();
0141:     gluLookAt(  0.0, 0.0, 5.0,      // 視点
0142:                 0.0, 0.0, 0.0,      // 注目点
0143:                 0.0, 1.0, 0.0);     // 上方向
0144: 
0145:     glPushMatrix();                 // ビュー行列が他のポリゴンでも使えるよう退避
0146: 
0147:     // -----------------------------------------------------------------------
0148:     // 箱の描画
0149:     // -----------------------------------------------------------------------
0150:     glRotatef(  r, 0.0, 1.0, 0.0);  // 回転
0151:     glRotatef( 60, 1.0, 0.0, 0.0);  // 適当に傾ける
0152:     glRotatef(-20, 0.0, 0.0, 1.0);  // 適当に傾ける
0153: 
0154:     DrawCube();
0155: 
0156:     glPopMatrix();                  // ビュー行列に現在の行列を戻す
0157:     glutSwapBuffers();              // 画面の更新の終了
0158: }

さて、肝心なのが箱の表示です。
プルグラマブルシェーダを使うために、cgGLBindProgram を宣言します。
頂点シェーダを使うときは、cgGLEnableProgramType/cgGLDisableProgramTypeのパラメータを cgVertexProfile にして、ON/OFFを切り替えます。
頂点シェーダプログラムの引数である行列や頂点色の設定は、cgGLBindUniformStateMatrix及び、cgGLBindVarying3f で行います。

0084: // ---------------------------------------------------------------------------
0085: // 箱の描画
0086: // ---------------------------------------------------------------------------
0087: static void DrawCube(void)
0088: {
0089:     int i;
0090:     cgError Ret;
0091:     
0092:     // イテレータへプログラムを結びつける
0093:     Ret = cgGLBindProgram(ProgramIter);
0094:     assert(Ret == cgNoError);
0095: 
0096:     // 行列を設定する
0097:     if(ModelViewProjBind != NULL) {
0098:         cgGLBindUniformStateMatrix(ProgramIter, 
0099:                                 ModelViewProjBind,
0100:                                 cgGLModelViewProjectionMatrix,
0101:                                 cgGLMatrixIdentity);
0102:     }
0103: 
0104:     // Cg による描画へ切り替え
0105:     cgGLEnableProgramType(cgVertexProfile);
0106: 
0107:     // 箱の描画
0108:     for(i = 0; i < 6; i++) {
0109:         glBegin(GL_QUADS);  // 四角形の描画
0110: 
0111:         glNormal3fv(&CubeNormals[i][0]);                            // 法線
0112:         cgGLBindVarying3f(ProgramIter, ColorBind, 1.0, 0.0, 0.0);   // 頂点色
0113:         glVertex3fv(&CubeVertices[CubeFaces[i][0]][0]);             // 座標
0114: 
0115:         cgGLBindVarying3f(ProgramIter, ColorBind, 0.0, 1.0, 0.0);
0116:         glVertex3fv(&CubeVertices[CubeFaces[i][1]][0]);
0117: 
0118:         cgGLBindVarying3f(ProgramIter, ColorBind, 0.0, 0.0, 1.0);
0119:         glVertex3fv(&CubeVertices[CubeFaces[i][2]][0]);
0120: 
0121:         cgGLBindVarying3f(ProgramIter, ColorBind, 1.0, 1.0, 1.0);
0122:         glVertex3fv(&CubeVertices[CubeFaces[i][3]][0]);
0123:     
0124:         glEnd();    // 四角形終わり
0125:     }
0126:     
0127:     // Cg による描画の終了
0128:     cgGLDisableProgramType(cgVertexProfile);
0129: }

あと、glVertex3fvで頂点座標を指定したり、glNormal3fvで法線ベクトルを指定して表示します。

リサイズとアイドルの関数は変わりません。

0159: // ---------------------------------------------------------------------------
0160: // 画面がリサイズされたとき
0161: // ---------------------------------------------------------------------------
0162: void resize(int w, int h)
0163: {
0164:     // ビューポートの設定
0165:     glViewport(0, 0, w, h);
0166: 
0167:     // 射影行列の設定
0168:     glMatrixMode(GL_PROJECTION);
0169:     glLoadIdentity();
0170:     gluPerspective( 60.0,           // 画角
0171:                     1.0,            // アスペクト比
0172:                     1.0, 10.0);     // 前方面、後方面
0173: }
0174: // ---------------------------------------------------------------------------
0175: // ひまなときに実行される
0176: // ---------------------------------------------------------------------------
0177: void idle()
0178: {
0179:     // ポリゴンの回転
0180:     r = r + 3.0f;
0181:     while (360.0f < r) r -= 360.0f;
0182: 
0183:     glutPostRedisplay();            // 再描画リクエスト
0184: }

■最後に

さて、Cg言語がOpenGLで動かせるようになりました。
コンテストは、ねた勝負なので、これだけではお話になりませんが、 NV30部門へ応募するための準備ができました。





もどる

imagire@gmail.com