TOP

ABOUT

PROGRAM

DIALY







CgFXを使ってみる。

 1.はじめに…
 今回は,CgFXの使い方についてお勉強してみます。




 2.CgFX?
 今まで,頂点プログラムとフラグメントプログラムをCGファイルに別々に記述してきましたが,CgFXを使えば頂点プログラムとフラグメントプログラムを一緒に記述できるそうです。一緒にできるほうが管理が楽でいいと思います。
 「じゃ,このCgFXってやつは何がちがうのさ?」という話になります。 まず,今いったように同一ファイル内での記述ができるという点が1つ。レンダリングステートなんかの記述もできます。要するにglCullFace(GL_BACK)とかが,CullFace = Back;という風にCgFX側に記述すると同じことができるということです。あとはマルチパスの記述が可能です。あと最近では,Mayaや3dsMaxとかのソフトでもCgFXが使えるようですね。
 CgFXを使うにあたって便利な点はOpenGLでもDirectXでも使えるという点ですね。CgFXとDirectXのエフェクトファイル(*.fx)はほぼ同じだそうで,日本語のサンプルがすくないOpenGL利用者のPocolにとっては,DirectXのサンプルなんかもちょこっと修正すればCgFXを使ってOpenGLで動くので勉強する際にもちょっとおいしいです。
 で,このCgFXなんですけど「使い方は?」というとCg Toolkit 1.4以上をインストールしてある方であれば,Cg User's Manual(英語)の117ページあたりから使い方とかが載ったりしていると思います。残念ながら日本語版のマニュアルは更新されておらず1.0 Releaseのままになっているので,詳しい説明がないので注意。もうすぐしたら,Cg Toolkit 2.0がでると思うので(現在はベータ版)そのときに日本語のマニュアルがでることを期待しましょう。


 3.使ってみる
 では,実際に使ってみましょう。まずはコードをが〜っと載せておきます。
//
// include
//
#include <iostream>
#include <cmath>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include "Mouse.h"
using namespace std;

//
// link
//
#pragma comment (lib, "cg.lib")
#pragma comment (lib, "cgGL.lib")

//
// global
//
int WindowPositionX = 100;
int WindowPositionY = 100;
int WindowWidth = 512;
int WindowHeight = 512;
char WindowTitle[] = "★ CgFXを使ってみる ★";
GLfloat lightColor[4]  = { 1.0, 1.0, 1.0, 1.0 };
GLfloat lightPosition[4] = { 50.0, 50.0, 50.0, 1.0 };
CGcontext context;
CGeffect effect;
CGtechnique techniques;
CGparameter World;
CGparameter WView;
CGparameter WVProj;
CGparameter Diffuse;
CGparameter Ambient;
CGparameter Light;
ViewCamera camera(5.0);

//
// prototype
//
void Initialize();
void Display();
void Idle();
void Reshape(int x, int y);
void Keyboard(unsigned char key, int x, int y);
void Special(int key, int x, int y);
void Mouse(int button, int state, int x, int y);
void Motion(int x, int y);
void PassiveMotion(int x, int y);
bool Initialize_Cg();
void CheckError_Cg();
void Shutdown_Cg();


//----------------------------------------------------------------------------------------------------
//  main
//  Desc : メインエントリポイント
//----------------------------------------------------------------------------------------------------
int main( int argc, char **argv )
{
    glutInit(&argc, argv);
    glutInitWindowPosition(WindowPositionX, WindowPositionY);
    glutInitWindowSize(WindowWidth, WindowHeight);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
    glutCreateWindow(WindowTitle);
    glutDisplayFunc(Display);
    glutReshapeFunc(Reshape);
    glutIdleFunc(Idle);
    glutMouseFunc(Mouse);
    glutMotionFunc(Motion);
    glutPassiveMotionFunc(PassiveMotion);
    glutKeyboardFunc(Keyboard);
    glutSpecialFunc(Special);

    Initialize();

    glutMainLoop();

    return 0;
}

//----------------------------------------------------------------------------------------------------
//  Initialize_Cg
//  Desc : Cgの初期化
//----------------------------------------------------------------------------------------------------
bool Initialize_Cg()
{
    // エラーコールバック関数の設定
    cgSetErrorCallback(CheckError_Cg);
    
    // コンテキストを作成
    context = cgCreateContext();

    // 
    cgGLRegisterStates(context);
    
    // CgFXファイルを読み込む
    effect = cgCreateEffectFromFile(context, "Basic.cgfx", NULL);

    // 最初のテクニックを取得
    techniques = cgGetFirstTechnique(effect);

    // チェック
    while ( techniques && cgValidateTechnique(techniques) == CG_FALSE)
    {
        cout << "Error : Technique " << cgGetTechniqueName(techniques) << "did not validate. Skipping\n";
        techniques = cgGetNextTechnique(techniques);
    }
    if ( !techniques )
    {
        cout << "Error : No valid technique\n";
        return false;
    }

    // パラメータを取得
    World = cgGetNamedEffectParameter(effect, "World");
    WView = cgGetNamedEffectParameter(effect, "WView");
    WVProj = cgGetNamedEffectParameter(effect, "WVProj");
    Diffuse = cgGetNamedEffectParameter(effect, "Diffuse");
    Ambient = cgGetNamedEffectParameter(effect, "Ambient");
    Light = cgGetNamedEffectParameter(effect, "Light");


    return true;
}

//--------------------------------------------------------------------------------------------------
//  CheckError_Cg
//  Desc: Cgエラーのチェック
//--------------------------------------------------------------------------------------------------
void CheckError_Cg()
{
    CGerror lastError = cgGetError();

    if ( lastError )
    {
        cout << "Error: Cgエラーが発生しました\n";
        cout << cgGetErrorString( lastError ) << endl;
        cout << cgGetLastListing( context ) << endl;
        exit(0);
    }
}

//----------------------------------------------------------------------------------------------------
//  Shutdown_Cg
//  Desc : Cgの後片付け
//----------------------------------------------------------------------------------------------------
void Shutdown_Cg()
{
    cgDestroyEffect(effect);
    cgDestroyContext(context);  
}


//----------------------------------------------------------------------------------------------------
//  Initialize
//  Desc : 初期化処理
//----------------------------------------------------------------------------------------------------
void Initialize()
{
    // 塗りつぶし色
    glClearColor(0.3, 0.3, 1.0, 0.0);

    // 深度テスト無効
    glEnable(GL_DEPTH_TEST);

    // スムーズシェイディング
    glShadeModel(GL_SMOOTH);

    // ライティング(補助軸用)
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glLightfv(GL_LIGHT0, GL_AMBIENT_AND_DIFFUSE, lightColor);
    glDisable(GL_LIGHTING);

    // Cgの初期化
    if ( !Initialize_Cg() )
    {
        cout << "Error : Cgの初期化に失敗しました\n";
        exit(0);
    }
    
}



//---------------------------------------------------------------------------------------------------
//  Idle
//  Desc : アイドリング時の処理
//---------------------------------------------------------------------------------------------------
void Idle()
{
    glutPostRedisplay();
}

//---------------------------------------------------------------------------------------------------
//  Reshape
//  Desc : サイズ変更
//---------------------------------------------------------------------------------------------------
void Reshape(int x, int y)
{
    WindowWidth = x;
    WindowHeight = y;
    if ( WindowWidth < 1 ) WindowWidth = 1;
    if ( WindowHeight < 1 ) WindowHeight = 1;
}
    
    

//---------------------------------------------------------------------------------------------------
//  Display
//  Desc : ウィンドウへの描画
//---------------------------------------------------------------------------------------------------
void Display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glViewport(0, 0, WindowWidth, WindowHeight);
    gluPerspective(40.0, (double)WindowWidth/(double)WindowHeight, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    camera.Set();
    
    // Worldパラメータをセット
    cgGLSetStateMatrixParameter(World,
        CG_GL_MODELVIEW_MATRIX,
        CG_GL_MATRIX_INVERSE);

    // WViewパラメータをセット
    cgGLSetStateMatrixParameter(WView,
        CG_GL_MODELVIEW_MATRIX,
        CG_GL_MATRIX_IDENTITY);
    
    // WVProjパラメータをセット
    cgGLSetStateMatrixParameter(WVProj,
        CG_GL_MODELVIEW_PROJECTION_MATRIX,
        CG_GL_MATRIX_IDENTITY);

    glPushMatrix();

    // Lightパラメータをセット
    float light[4] = {0.0, 1.0, 1.0, 0.0};
    cgSetParameter4fv(Light, light);

    // Ambientパラメータをセット
    float ambient[4] = {0.2, 0.2, 0.2, 1.0};
    cgSetParameter4fv(Ambient, ambient);

    // Diffuseパラメータをセット
    float diffuse[4] = {0.0, 1.0, 0.5, 1.0};
    cgSetParameter4fv(Diffuse, diffuse);

    // 描画
    CGpass pass;
    pass = cgGetFirstPass(techniques);
    while ( pass )
    {
        cgSetPassState(pass);
    
        // 描画したいもの      
        glutSolidCube(1.0);

        cgResetPassState(pass);
        pass = cgGetNextPass(pass);
    }
    glPopMatrix();

    // 補助軸を描画
    glPushMatrix();
    glEnable(GL_LIGHTING);
    camera.RenderSubAxis(WindowWidth, WindowHeight);
    glDisable(GL_LIGHTING);
    glPopMatrix();

    glutSwapBuffers();
}

//---------------------------------------------------------------------------------------------------
//  Mouse
//  Desc : マウス処理
//---------------------------------------------------------------------------------------------------
void Mouse(int button, int state, int x, int y)
{
    camera.MouseInput(button, state, x, y);
}

//--------------------------------------------------------------------------------------------------
//  Motion
//  Desc : マウスドラッグ時
//--------------------------------------------------------------------------------------------------
void Motion(int x, int y)
{
    camera.MouseMotion(x, y);
}

//--------------------------------------------------------------------------------------------------
//  PassiveMotion
//  Desc : マウス移動時
//--------------------------------------------------------------------------------------------------
void PassiveMotion(int x, int y)
{
}

//--------------------------------------------------------------------------------------------------
//  Keyboard
//  Desc : キーボード処理
//--------------------------------------------------------------------------------------------------
void Keyboard(unsigned char key, int x, int y)
{
    switch ( key )
    {
    case '\033':
        exit(0);
        break;

    default:
        break;
    }
}

基本的にはCgと大きな流れは変わらないようです。
グローバルでパラメータとかを用意しておきます。違う点はCGprofileやCGprogramがない代わりにCGeffectとCGtechniqueというデータ型の変数が追加されていることです。CgFXを使うためにこの2つの変数を用意しましょう。
つぎにInitialize_Cg()関数内でCgFXを使うための初期設定をしていきます。 まずはいつもどおりエラーコールバックの関数の設定をして,コンテキストを作成します。コンテキストを作成後,cgGLRegisterStates()関数を呼び出します。割り当てをするのに必要だそうです。
cgGLRegisterStates関数を読んだ後にエフェクトをCgFXファイルから作成します。そのための関数がcgCreateEffectFromFile関数です。この辺の関数の説明がよくわからないので,Cg User's Manualの英語版をよく読んでみてください。おそらく第1引数がコンテキストで,第2引数が読み込むCgFXファイル名で,第3引数がconst char **argsとなっているのでCg側に渡す文字列になっているっぽいです。ほとんどの場合第3引数はNULLになると思います。 ファイルを読み込みんだら今度は最初のテクニックを取得して,テクニックのチェックを行います。これがwhile文でいろいろしているあたりです。
あとはパラメータを取得しておきます。
以上がInitialize_Cg()関数内の処理です。 次はDisplay関数内の処理です。パラメーターをセットしたりするのはCgの時とほぼ同じで,「はじめに…」で述べたようにマルチパスが可能なので,cgGetFirstPassで最初のパスを取得して,パスの数だけレンダリングを行います。
//---------------------------------------------------------------------------------------------------
//  Display
//  Desc : ウィンドウへの描画
//---------------------------------------------------------------------------------------------------
void Display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glViewport(0, 0, WindowWidth, WindowHeight);
    gluPerspective(40.0, (double)WindowWidth/(double)WindowHeight, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    camera.Set();
    
    // Worldパラメータをセット
    cgGLSetStateMatrixParameter(World,
        CG_GL_MODELVIEW_MATRIX,
        CG_GL_MATRIX_INVERSE);

    // WViewパラメータをセット
    cgGLSetStateMatrixParameter(WView,
        CG_GL_MODELVIEW_MATRIX,
        CG_GL_MATRIX_IDENTITY);
    
    // WVProjパラメータをセット
    cgGLSetStateMatrixParameter(WVProj,
        CG_GL_MODELVIEW_PROJECTION_MATRIX,
        CG_GL_MATRIX_IDENTITY);

    glPushMatrix();

    // Lightパラメータをセット
    float light[4] = {0.0, 1.0, 1.0, 0.0};
    cgSetParameter4fv(Light, light);

    // Ambientパラメータをセット
    float ambient[4] = {0.2, 0.2, 0.2, 1.0};
    cgSetParameter4fv(Ambient, ambient);

    // Diffuseパラメータをセット
    float diffuse[4] = {0.0, 1.0, 0.5, 1.0};
    cgSetParameter4fv(Diffuse, diffuse);

    // 描画
    CGpass pass;
    pass = cgGetFirstPass(techniques);
    while ( pass )
    {
        cgSetPassState(pass);
    
        // 描画したいもの      
        glutSolidCube(1.0);

        cgResetPassState(pass);
        pass = cgGetNextPass(pass);
    }
    glPopMatrix();

    // 補助軸を描画
    glPushMatrix();
    glEnable(GL_LIGHTING);
    camera.RenderSubAxis(WindowWidth, WindowHeight);
    glDisable(GL_LIGHTING);
    glPopMatrix();

    glutSwapBuffers();
}

Diffuseパラメータをセットしているしたあたりがそうです。
描画がおわったら,Shutdown_Cg関数で後片付けを行います。cgDestroyProfileやらの代わりにcgDestroyEffect関数を読んであげましょう。
//----------------------------------------------------------------------------------------------------
//  Shutdown_Cg
//  Desc : Cgの後片付け
//----------------------------------------------------------------------------------------------------
void Shutdown_Cg()
{
    cgDestroyEffect(effect);
    cgDestroyContext(context);  
}

GPU側ですが,CgFXファイルではたいそうなことはせず,Vertes Shaderでスクリーン座標系に値を変換して送るだけの処理をしています。テスト用につくったやつそのままなのであまり考えてつくっていません。
//
// global
//
float4x4 World;
float4x4 WView;
float4x4 WVProj;
float4 Diffuse;
float3 Light;
float3 Ambient;


//----------------------------------------------------------------------------------------------------
//  VertexShader
//  Desc : 頂点シェーダ
//---------------------------------------------------------------------------------------------------
void VertexShader(
                  float4 in_pos : POSITION,
                  float3 in_norm : NORMAL,
                  out float4 out_pos : POSITION,
                  out float4 out_col : COLOR0
                  )
{
    out_pos = mul(WVProj, in_pos);
    float3 w_normal = normalize(mul((float3x3)World, in_norm));
    out_col = float4(max(dot(w_normal, Light), 0.0) + Ambient, 1.0);
}

//---------------------------------------------------------------------------------------------------
//  PixelShader
//  Desc : ピクセルシェーダ
//---------------------------------------------------------------------------------------------------
float4 PixelShader(float4 in_col : COLOR ) : COLOR
{
    return in_col * Diffuse;
}

//----------------------------------------------------------------------------------------------------
//  Basic
//  Desc : テクニック
//----------------------------------------------------------------------------------------------------
technique Basic
{
    pass P0
    {
        VertexProgram = compile vp40 VertexShader();
        FragmentProgram = compile fp40 PixelShader();
    }
}
結構いい加減な説明ですが,こんな感じでコードを組むとCgFXが一応使えます。
関数うんぬんの説明は省いてしまっているのでCg User's Manualの117ページあたりに「Introduction To CgFX」という章に詳しい説明が載っているのでそちらの方を一通り読んでみてください。


 Download
本ソースコードおよびプログラムを使用したことによる如何なる損害も製作者は責任を負いません。
本ソースコードおよびプログラムは自己責任でご使用ください。
プログラムの作成にはVisual Studio 2005 ProfessionalとCg Toolkit 1.5 Septemberを用いています。