(少なくとも第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部門へ応募するための準備ができました。