OpenGL Shading Language


~ GLSL, slang ~






■はじめに

2003年12月9日付けで、3Dlabs 社が「OpenGL Shading Language Software Development Kit Release: 6」をリリースしました。また、ATI社のドライバも2003年11月20日のドライバから、OpenGL Shading Language に対応してきました。
遅ればせながら、OpenGL にもプログラマブルシェーダの時代が到来したと言ってよいでしょう(けっ、アセンブラなんか見たくないよ!)。
ちょっと触ってみました。

で、いつものようにプログラムです。
GLSLを使って、Phongシェーディングをしてみました。

コンパイルするには、「OpenGL Shading Language Software Development Kit Release: 6」を解答して、gl3dlabs.hを適当なフォルダにおいてください。
実行には、ドライバがGLSLに対応している必要があります。

ソースには、いつものように適当にファイルが入っています。
今回はコンソールが表示されるGLUTなんか使わないで(2003 Dec. 14追加:nakagawaさんから、http://www.opengl.org/developers/documentation/glut/glut_faq.html#Q36にコンソールを表示しなくする方法があると教えていただきました。)実装しています。 また、DirectXのアプリケーションウィザードのように(をぱくって)、アプリケーションクラスを作って、DirectX と同じ感覚で開発ができるようにしました(まだまだ未完成です)。

main.hアプリケーションのヘッダ
main.cppアプリケーションのソース
GLSL.vert頂点プログラム
GLSL.fragフラグメントプログラム
gl3dlabs.h3Dlabs謹製OpenGL拡張プログラム用ヘッダ
GLApp.h基底アプリケーションクラス
GLApp.cpp基底アプリケーションクラス
GLSL.hGLSLのラッパー
GLSL.cppGLSLのラッパー

■シェーダ言語

GLSL は、HLSL と似た言語です。C言語ライクな構文でシェーダプログラムを作成します。
シェーダプログラム内で用いる外部定数は、「uniform」変数として扱え、頂点プログラムからフラグメントプログラムへ渡すデータは、「varying」として取り扱います。
今回のプログラムでは、次のような感じです。

GLSL.vert
0007: // -------------------------------------------------------------
0008: // グローバル変数
0009: // -------------------------------------------------------------
0010: uniform vec3 LightPos;
0011: 
0012: // -------------------------------------------------------------
0013: // 頂点プログラムからフラグメントプログラムに渡すデータ
0014: // -------------------------------------------------------------
0015: varying vec3 VERTEX_COLOR;  // 頂点色
0016: varying vec3 VERTEX_EYE;    // 視線ベクトル
0017: varying vec3 VERTEX_NORMAL; // 法線ベクトル
0018: varying vec3 VERTEX_LIGHT;  // ライトベクトル

vec3 は、3次元のfloat型のベクトルです(float3の方が種類も判別できるからよかったのに、どうしてこんなものに…vec はvectorだろうけど、それは、その後に続く「3」とダブっているだろ)。

なお、頂点データは「gl_Vertex」や「gl_Normal」のように、特別な名前がついた変数として扱います(これもグローバル変数みたいで気に入らないなぁ)。

プログラムは、次のようになります。
gl_Positionへ頂点座標を出力すると共に、正規化されていない法線ベクトル、視線ベクトル、ライトベクトルと、頂点色をプラグメントプログラムに引き渡します。
HLSLと違って、明示的な型変換が必要らしく、そこらじゅうで「vec3」がでてきますが、これも「いい感じ」に処理してほしかったです。

GLSL.vert
0020: // ------------------------------------------------------------
0021: // 頂点シェーダプログラム
0022: // ------------------------------------------------------------
0023: void main(void)
0024: {
0025:     // 座標変換
0026:     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
0027:     
0028:     // 色のための計算
0029:     vec4 pos    = gl_ModelViewMatrix * gl_Vertex;
0030:     vec3 L      = LightPos - vec3(pos);
0031:     vec3 N      = gl_NormalMatrix * gl_Normal;
0032: 
0033:     VERTEX_COLOR = vec3(gl_Color);
0034:     VERTEX_NORMAL = N;
0035:     VERTEX_LIGHT  = L;
0036:     VERTEX_EYE    = vec3(pos);
0037: }
0038:

フラグメントプログラムでは、頂点プログラムから渡されたデータを正規化して、Phong鏡面反射係数を清算すると共に、ランバート拡散光の係数も計算して色出力「gl_FragColor」へします。
この辺は、HLSLと違和感は感じませんかねぇ。

GLSL.frag
0007: // -------------------------------------------------------------
0008: // 頂点プログラムからフラグメントプログラムに渡すデータ
0009: // -------------------------------------------------------------
0010: varying vec3 VERTEX_COLOR;
0011: varying vec3 VERTEX_EYE;
0012: varying vec3 VERTEX_NORMAL;
0013: varying vec3 VERTEX_LIGHT;
0014: 
0015: // ------------------------------------------------------------
0016: // フラグメントシェーダプログラム
0017: // ------------------------------------------------------------
0018: void main (void)
0019: {
0020:     vec3 N = normalize(VERTEX_NORMAL);  // 法線ベクトル
0021:     vec3 L = normalize(VERTEX_LIGHT);   // ライトベクトル
0022:     vec3 E = normalize(VERTEX_EYE);     // 視線ベクトル
0023:     vec3 R = reflect(L, N);             // 反射ベクトル
0024:     
0025:     // Phong speqular
0026:     float spec = pow(max(dot(R, E), 0.0), 50.0);
0027:     
0028:     gl_FragColor = vec4 (VERTEX_COLOR, 1.0)         // 頂点色
0029:                          * vec4(max(dot(N,L), 0.0)) // Lambert 拡散強度
0030:                  + vec4 (0.8) * vec4 (spec);        // 鏡面反射色
0031: }
0032:

■構成

GLSL を使うときの基本的な構成は、他のプログラムと同じです。初期化があり、いい感じに設定して描画して、最後に開放などの後片付けをします。

初期化
 ↓
読み込み
 ↓
シェーダの切り替え
 ↓
定数の設定
 ↓
描画
 ↓
後片付け

■初期化

今回は、初期化のためにラッパーのクラスを作り間した。
したがって、最初に使うときにこの関数を呼び出します。

main.cpp
0112:     // GLSL の初期化
0113:     hr = GLSL::Init();
0114:     if (FAILED(hr)) {
0115:         MessageBox( NULL, "Faild to initialize the shader system.", "ERROR", MB_OK);
0116:         return FALSE;
0117:     }

初期化の段階では、「wglGetProcAddress」関数を使って、使用する関数を宣言します。
そのためのマクロを作りました(GLSLのサンプルにあったものですが…)。

glsl.cpp
0061: #define PADDR(functype,funcname) \
0062:     ((funcname = (functype) wglGetProcAddress( #funcname )) == NULL)

このマクロによって、funcname という名前の関数が使えるのか調べて、使えるのならその関数へのポインタを調べて、定義した関数のポインタへ関数のポインタを保存します(つまり、このマクロは、使えるのか調べる関数ではなくて、関数のポインタを所得する関数です)。

今回用意する関数は、次のものです(使う使わないにかかわらず、使いそうなものをリストアップしています)。

glsl.cpp
0016: //-----------------------------------------------------------------------------
0017: //
0018: // OpenGL Shading Language 関数
0019: //
0020: //-----------------------------------------------------------------------------
0021: PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
0022: PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
0023: PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
0024: PFNGLDETACHOBJECTARBPROC glDetachObjectARB;
0025: PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
0026: 
0027: PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
0028: PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
0029: PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
0030: PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
0031: PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
0032: 
0033: PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
0034: PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB;
0035: PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
0036: 
0037: PFNGLUNIFORM1FARBPROC glUniform1fARB;
0038: PFNGLUNIFORM2FARBPROC glUniform2fARB;
0039: PFNGLUNIFORM3FARBPROC glUniform3fARB;
0040: PFNGLUNIFORM4FARBPROC glUniform4fARB;
0041: 
0042: PFNGLUNIFORM1IARBPROC glUniform1iARB;
0043: PFNGLUNIFORM2IARBPROC glUniform2iARB;
0044: PFNGLUNIFORM3IARBPROC glUniform3iARB;
0045: PFNGLUNIFORM4IARBPROC glUniform4iARB;
0046: 
0047: PFNGLUNIFORM1FVARBPROC glUniform1fvARB;
0048: PFNGLUNIFORM2FVARBPROC glUniform2fvARB;
0049: PFNGLUNIFORM3FVARBPROC glUniform3fvARB;
0050: PFNGLUNIFORM4FVARBPROC glUniform4fvARB;
0051: 
0052: PFNGLUNIFORM1IVARBPROC glUniform1ivARB;
0053: PFNGLUNIFORM2IVARBPROC glUniform2ivARB;
0054: PFNGLUNIFORM3IVARBPROC glUniform3ivARB;
0055: PFNGLUNIFORM4IVARBPROC glUniform4ivARB;

初期化関数では、これらのマクロと変数を次のように扱います。

glsl.cpp
0068: HRESULT GLSL::Init( )
0069: {
0070:     int error;
0071: 
0072:     error |= PADDR(PFNGLCREATEPROGRAMOBJECTARBPROC, glCreateProgramObjectARB);
0073:     error |= PADDR(PFNGLCREATESHADEROBJECTARBPROC, glCreateShaderObjectARB);
0074:     error |= PADDR(PFNGLDELETEOBJECTARBPROC, glDeleteObjectARB);
0075:     error |= PADDR(PFNGLDETACHOBJECTARBPROC, glDetachObjectARB);
0076:     error |= PADDR(PFNGLATTACHOBJECTARBPROC, glAttachObjectARB);
0077:     error |= PADDR(PFNGLSHADERSOURCEARBPROC, glShaderSourceARB);
0078:     error |= PADDR(PFNGLCOMPILESHADERARBPROC, glCompileShaderARB);
0079:     error |= PADDR(PFNGLLINKPROGRAMARBPROC, glLinkProgramARB);
0080:     error |= PADDR(PFNGLGETINFOLOGARBPROC, glGetInfoLogARB);
0081:     error |= PADDR(PFNGLUSEPROGRAMOBJECTARBPROC, glUseProgramObjectARB);
0082:     error |= PADDR(PFNGLGETOBJECTPARAMETERIVARBPROC, glGetObjectParameterivARB);
0083:     error |= PADDR(PFNGLGETOBJECTPARAMETERFVARBPROC, glGetObjectParameterfvARB);
0084:     error |= PADDR(PFNGLGETUNIFORMLOCATIONARBPROC, glGetUniformLocationARB);
0085:     error |= PADDR(PFNGLUNIFORM1FARBPROC, glUniform1fARB);
0086:     error |= PADDR(PFNGLUNIFORM2FARBPROC, glUniform2fARB);
0087:     error |= PADDR(PFNGLUNIFORM3FARBPROC, glUniform3fARB);
0088:     error |= PADDR(PFNGLUNIFORM4FARBPROC, glUniform4fARB);
0089:     error |= PADDR(PFNGLUNIFORM1IARBPROC, glUniform1iARB);
0090:     error |= PADDR(PFNGLUNIFORM2IARBPROC, glUniform2iARB);
0091:     error |= PADDR(PFNGLUNIFORM3IARBPROC, glUniform3iARB);
0092:     error |= PADDR(PFNGLUNIFORM4IARBPROC, glUniform4iARB);
0093:     error |= PADDR(PFNGLUNIFORM1FVARBPROC, glUniform1fvARB);
0094:     error |= PADDR(PFNGLUNIFORM2FVARBPROC, glUniform2fvARB);
0095:     error |= PADDR(PFNGLUNIFORM3FVARBPROC, glUniform3fvARB);
0096:     error |= PADDR(PFNGLUNIFORM4FVARBPROC, glUniform4fvARB);
0097:     error |= PADDR(PFNGLUNIFORM1IVARBPROC, glUniform1ivARB);
0098:     error |= PADDR(PFNGLUNIFORM2IVARBPROC, glUniform2ivARB);
0099:     error |= PADDR(PFNGLUNIFORM3IVARBPROC, glUniform3ivARB);
0100:     error |= PADDR(PFNGLUNIFORM4IVARBPROC, glUniform4ivARB);
0101:     if (error) {
0102:         return FALSE;
0103:     }
0104: 
0105:     return S_OK;
0106: }

■読み込み

シェーダを読み込む関数では、シェーダが描かれたファイルを読み込んで、オブジェクトを確保すします。
また、変数へのアクセスインターフェイスとして、変数への「ロケーション」を所得します(これは、HLSLの変数へのハンドルのようなものですね)。

main.cpp
0119:     // シェーダの読み込み
0120:     hr = GLSL::CreateShaderFromFile("GLSL", &m_hProgramObject);
0121:     if (FAILED(hr)) {
0122:         MessageBox( NULL, "Cannot install shader", "ERROR", MB_OK);
0123:         return FALSE;
0124:     }
0125:     
0126:     // オブジェクトのロケーションの所得
0127:     m_locLightPos = glGetUniformLocationARB(m_hProgramObject, "LightPos");

もちろん、肝はファイルを読み込む関数「CreateShaderFromFile」です。
CreateShaderFromFile では、シェーダのファイルを読み込んでから、プログラムに関するオブジェクト(HLSLではエフェクトのオブジェクト)を作成し、シェーダプログラムをコンパイルして、使えるようにします。

GLSL.cpp
0274: HRESULT GLSL::CreateShaderFromFile(char *fileName, GLhandleARB *pProgram)
0275: {
0276:     GLubyte *vertexShader;
0277:     GLubyte *fragmentShader;
0278:     GLhandleARB prog;
0279: 
0280:     // 失敗したときのために初期化
0281:     *pProgram = 0;
0282: 
0283:     // ファイルを読み込む
0284:     if (FAILED(readShaderSource(fileName, &vertexShader, &fragmentShader))) return FALSE;
0285: 
0286:     // シェーダを生成する
0287:     prog = glCreateProgramObjectARB();
0288: 
0289:     // シェーダを OpenGL へセットアップする
0290:     if (FAILED(handShadersToOpenGL( prog, vertexShader, fragmentShader))) return FALSE;
0291: 
0292:     *pProgram = prog;
0293: 
0294:     return S_OK;
0295: }

読み込むプログラムは、頂点プログラムとフラグメントプログラムをほぼ並列で行います。
今回は、それぞれのシェーダに関して、「vert」と「frag」の拡張子を変えた2つのプログラムを読み込んで、シェーダプログラムを作成します(したがって、頂点プログラムのみのケースはサポートしていないので、自己責任で書き換えてください)。
込みこむ関数、readShaderSourceでは、最初にそれぞれのファイル名を補完してから、ファイルのサイズをしらべます。さらに、そのサイズのメモリを確保してから、作成したメモリにファイルを読み込みます。

GLSL.cpp
0158: //-------------------------------------------------------------
0159: // Name: readShaderSource
0160: // Desc: シェーダプログラムを読み込む
0161: //-------------------------------------------------------------
0162: HRESULT GLSL::readShaderSource(char *fileName, GLubyte **vertShader, GLubyte **fragShader)
0163: {
0164:     // -------------------------------------------------------
0165:     // ファイル名を決める
0166:     // -------------------------------------------------------
0167:     char vert_fileName[256];// 頂点プログラムのファイル名
0168:     char frag_fileName[256];// フラグメントプログラムのファイル名
0169: 
0170:     strcpy(vert_fileName, fileName); strcat(vert_fileName, ".vert");
0171:     strcpy(frag_fileName, fileName); strcat(frag_fileName, ".frag");
0172: 
0173:     // -------------------------------------------------------
0174:     // シェーダのためのメモリの確保
0175:     // -------------------------------------------------------
0176:     int vertSize = GetShaderSize(vert_fileName);
0177:     int fragSize = GetShaderSize(frag_fileName);
0178: 
0179:     if ((vertSize == -1) || (fragSize == -1)) {
0180:         return FALSE;// シェーダのサイズが決まらなかった
0181:     }
0182: 
0183:     *vertShader = (GLubyte *) malloc(vertSize);
0184:     *fragShader = (GLubyte *) malloc(fragSize);
0185: 
0186:     // -------------------------------------------------------
0187:     // シェーダのソースプログラムを読み込む
0188:     // -------------------------------------------------------
0189:     if (FAILED(ReadShader(vert_fileName, *vertShader, vertSize)))
0190:         return FALSE;// ファイルを読めなかった
0191: 
0192:     if (FAILED(ReadShader(frag_fileName, *fragShader, fragSize)))
0193:         return FALSE;// ファイルを読めなかった
0194: 
0195:     return S_OK;
0196: }

ファイルのサイズを求める関数「GetShaderSize」や、実際に読み込む関数「ReadShader」は、べたに実装しています。たとえば、「ReadShader」は次のようになっています。

GLSL.cpp
0132: //-------------------------------------------------------------
0133: // Name: ReadShader
0134: // Desc: ファイルを読み込んで、shaderTextに格納する
0135: //-------------------------------------------------------------
0136: HRESULT GLSL::ReadShader(char *fileName, GLubyte *shaderText, int size)
0137: {
0138:     FILE *fp;
0139:     int count;
0140: 
0141:     // ファイルを開く
0142:     fp = fopen(fileName, "r");
0143:     if (!fp) return FALSE;
0144: 
0145:     // ファイルを読み込む
0146:     fseek(fp, 0, SEEK_SET);
0147:     count = fread(shaderText, 1, size, fp);
0148:     shaderText[count] = '\0';
0149: 
0150:     if (ferror(fp)) return FALSE;
0151: 
0152:     // ファイルを閉じる
0153:     fclose(fp);
0154: 
0155:     return count;
0156: }

さて、ファイルをよみこんだら、それをVPUで扱いやすいようにコンパイルして、プログラムのオブジェクトに登録します。
登録する関数「handShadersToOpenGL」を詳しく見てきましょう。
最初に、「glCreateShaderObjectARB」を使って、シェーダのオブジェクトを作成します。

GLSL.cpp
0199: //-------------------------------------------------------------
0200: // Name: handShadersToOpenGL
0201: // Desc: シェーダを OpenGL へセットアップする
0202: //-------------------------------------------------------------
0203: HRESULT GLSL::handShadersToOpenGL( GLhandleARB Program, GLubyte *vertShader,  GLubyte *fragShader )
0204: {
0205:     // -------------------------------------------------------
0206:     // シェーダに関するオブジェクトの生成
0207:     // -------------------------------------------------------
0208:     GLhandleARB hVertShaderObject = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
0209:     GLhandleARB hFragShaderObject = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

また、読み込んだしぇーだプログラムをOpenGL が認識できるように登録します。

GLSL.cpp
0211:     // -------------------------------------------------------
0212:     // OpenGL システムにプログラム文字列を引き渡す
0213:     // -------------------------------------------------------
0214:     int vert_compiled = 0;
0215:     int frag_compiled = 0;
0216:     int linked = 0;
0217:     GLint     length;
0218: 
0219:     length = strlen((char*)vertShader);
0220:     glShaderSourceARB(hVertShaderObject, 1, (const GLcharARB**)&vertShader, &length);
0221: 
0222:     length = strlen((char*)fragShader);
0223:     glShaderSourceARB(hFragShaderObject, 1, (const GLcharARB**)&fragShader, &length);
0224: 
0225:     // -------------------------------------------------------
0226:     // 不必要になったメモリを開放する
0227:     // -------------------------------------------------------
0228:     free(vertShader);
0229:     free(fragShader);

次に、glShaderSourceARB を使って、VPUにシェーダプログラムの文字列を渡してからglCompileShaderARBでコンパイルします。
GLSLでは、HLSLと違って、コンパイラがアセンブラにコンパイルしてからVPU用のオブジェクトを作成する必然は無く、VPUが好きなように変換することが許されるので、シェーダの文字列を渡したら、後はドライバにお任せして、オブジェクトを作成してもらいます。

GLSL.cpp
0231:     // -------------------------------------------------------
0232:     // シェーダプログラムをコンパイルする
0233:     // -------------------------------------------------------
0234:     glCompileShaderARB(hVertShaderObject);
0235:     glGetObjectParameterivARB(hVertShaderObject,
0236:             GL_OBJECT_COMPILE_STATUS_ARB, &vert_compiled);
0237: 
0238: 
0239:     glCompileShaderARB(hFragShaderObject);
0240:     glGetObjectParameterivARB(hFragShaderObject,
0241:             GL_OBJECT_COMPILE_STATUS_ARB, &frag_compiled);
0242: 
0243:     if (!vert_compiled || !frag_compiled) return FALSE;

それが成功したら、glAttachObjectARB で、プログラムのオブジェクトにシェーダのオブジェクトを登録します。

GLSL.cpp
0245:     // -------------------------------------------------------
0246:     // Program のオブジェクトにそれぞれのシェーダを関連付ける
0247:     // -------------------------------------------------------
0248:     glAttachObjectARB(Program, hVertShaderObject);
0249:     glAttachObjectARB(Program, hFragShaderObject);
0250: 
0251:     // -------------------------------------------------------
0252:     // 不必要になったオブジェクトを削除する
0253:     // -------------------------------------------------------
0254:     glDeleteObjectARB(hVertShaderObject);
0255:     glDeleteObjectARB(hFragShaderObject);

最後に、「glLinkProgramARB」これらのシェーダをリンクして、1つの描画パイプラインとしてまとめます。

GLSL.cpp
0257:     // -------------------------------------------------------
0258:     // 全てをリンクする
0259:     // -------------------------------------------------------
0260:     glLinkProgramARB(Program);
0261:     glGetObjectParameterivARB(Program, GL_OBJECT_LINK_STATUS_ARB, &linked);
0262: 
0263:     if (!vert_compiled || !frag_compiled || !linked) return FALSE;
0264: 
0265:     return S_OK;
0266: }

■描画

作成したシェーダを使うには、描画前に「glUseProgramObjectARB」を呼び出します。
また、「glUniform3fvARB」等を使って、定数変数を設定します。
あとは、いつもと同じように描画します。

main.cpp
0237: //-------------------------------------------------------------
0238: // Name: Render()
0239: // Desc: 画面を描画する.
0240: //-------------------------------------------------------------
0241: HRESULT CMyD3DApplication::Render(void)
0242: {
0243:     static int init=0;
0244:     if(!init){init=1;
0245:         // 箱の初期化
0246:         InitializeCube(CubeVertices);
0247:     }
0248: 
0249:     //ウィンドウを塗りつぶす
0250:     glClearColor(0,0,0,0);
0251:     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
0252: 
0253:     //視点の変換行列の初期化
0254:     glLoadIdentity();
0255:     //視点の決定
0256:     gluLookAt(  0.0, 3.0, 5.0,      // 視点
0257:                 0.0, 0.0, 0.0,      // 注目点
0258:                 0.0, 1.0, 0.0);     // 上方向
0259: 
0260:     // シェーダの切り替え
0261:     glUseProgramObjectARB(m_hProgramObject);
0262: 
0263:     // ライト位置の設定
0264:     float lightPosition[3] = {2.0f, 8.0f, -10.0f };
0265:     glUniform3fvARB(m_locLightPos, 1, &lightPosition[0]);
0266: 
0267:     // 箱の描画
0268:     glPushMatrix();
0269:         glRotated(100*rot, 0.0, 1.0, 0.0);
0270: 
0271:     for(int i = 0; i < 6; i++) {
0272:         glBegin(GL_QUADS);  // 四角形の描画
0273: 
0274:         glNormal3fv(&CubeNormals[i][0]);                            // 法線
0275:         glColor3d(CubeVertices[CubeFaces[i][0]][0]*0.5+0.5
0276:                 , CubeVertices[CubeFaces[i][0]][1]*0.5+0.5
0277:                 , CubeVertices[CubeFaces[i][0]][2]*0.5+0.5);        // 頂点色
0278:         glVertex3fv(&CubeVertices[CubeFaces[i][0]][0]);             // 座標
0279:         glColor3d(CubeVertices[CubeFaces[i][1]][0]*0.5+0.5
0280:                 , CubeVertices[CubeFaces[i][1]][1]*0.5+0.5
0281:                 , CubeVertices[CubeFaces[i][1]][2]*0.5+0.5);        // 頂点色
0282:         glVertex3fv(&CubeVertices[CubeFaces[i][1]][0]);
0283:         glColor3d(CubeVertices[CubeFaces[i][2]][0]*0.5+0.5
0284:                 , CubeVertices[CubeFaces[i][2]][1]*0.5+0.5
0285:                 , CubeVertices[CubeFaces[i][2]][2]*0.5+0.5);        // 頂点色
0286:         glVertex3fv(&CubeVertices[CubeFaces[i][2]][0]);
0287:         glColor3d(CubeVertices[CubeFaces[i][3]][0]*0.5+0.5
0288:                 , CubeVertices[CubeFaces[i][3]][1]*0.5+0.5
0289:                 , CubeVertices[CubeFaces[i][3]][2]*0.5+0.5);        // 頂点色
0290:         glVertex3fv(&CubeVertices[CubeFaces[i][3]][0]);
0291:     
0292:         glEnd();    // 四角形終わり
0293:     }
0294:     
0295:     glPopMatrix();
0296: 
0297:     return S_OK;
0298: }

■後片付け

開放は簡単です。
作成したプログラムをglDeleteObjectARBで開放します。

main.cpp
0136: //-------------------------------------------------------------
0137: // Name: DeleteDeviceObjects()
0138: // Desc: InitDeviceObjects() で作成したオブジェクトを開放する
0139: //-------------------------------------------------------------
0140: HRESULT CMyD3DApplication::DeleteDeviceObjects()
0141: {
0142:     if (m_hProgramObject){glDeleteObjectARB(m_hProgramObject);m_hProgramObject=NULL;}
0143: 
0144:     return S_OK;
0145: }

お好きな人は、「SAFE_***」のマクロを作ってもよいでしょう。

■最後に

まぁ、こんなもんでしょ。
主流になるかなぁ?
オーサリングツールへの対応はどうかなぁ?





もどる

imagire@gmail.com