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を適当なフォルダにおいてください。
ソースには、いつものように適当にファイルが入っています。
今回はコンソールが表示される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.h | 3Dlabs謹製OpenGL拡張プログラム用ヘッダ |
GLApp.h | 基底アプリケーションクラス |
GLApp.cpp | 基底アプリケーションクラス |
GLSL.h | GLSLのラッパー |
GLSL.cpp | GLSLのラッパー |
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_***」のマクロを作ってもよいでしょう。
まぁ、こんなもんでしょ。
主流になるかなぁ?
オーサリングツールへの対応はどうかなぁ?