(少なくとも第2回、第3回の)Cg cording contest で、NV30 をもらえるのは、
NV30 部門だけです。
ぜひチャレンジしましょう。ということで、Cg を動かすためのプログラムを書きました。
まぁ、いつものように適当にファイルが入っています。
| main.cpp | これだけ。 |
あと、実行ファイル及び、プロジェクトファイルが入っています。
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 言語によるプログラムは、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を使うためには、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の初期化を行いましょう。
最初に、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部門へ応募するための準備ができました。