TOP

ABOUT

PROGRAM

DIALY







GPUのお勉強(4)

 1.はじめに…
 今回は,頂点プログラムのみを使って,GPUに計算させて単なる平面をうねうねさせて波のようにしてみます。




 2.コーディング(C/C++側)
 CPU側では,格子状になった平面を描画します。あとは波にするためのカウンターを用意して,加算処理するだけです。
 CPU側では波生成のための計算はしません。
 まずは,いつもどおりヘッダファイルをインクルードしたり,変数の宣言をしておきます
//
// include
//
#include <iostream>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
using namespace std;

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

//
// global
//
int WindowWidth = 512;
int WindowHeight = 512;
char WindowTitle[] = "GPUでうねうね";
const float Pi = 3.1415926535;
const int mesh_size = 64;
float wave_mesh[mesh_size][mesh_size][3];
float wave_move = 0.0f;
CGcontext context;
CGprogram vertexProgram;
CGprofile vertexProfile;
CGparameter position;
CGparameter color;
CGparameter modelViewMatrix;
CGparameter wave;

次に,波用に格子状の平面を作成します。

//----------------------------------------------------------------------------------------------------
//  CreateWaveMesh
//  Desc : 波メッシュを作成
//---------------------------------------------------------------------------------------------------
void CreateWaveMesh()
{
    for ( int x=0; x<mesh_size; x++ )
    {
        for ( int z =0; z<mesh_size; z++ )
        {
            wave_mesh[x][z][0] = (float)(mesh_size/2.0f) - x;
            wave_mesh[x][z][1] = 0.0f;
            wave_mesh[x][z][2] = (float)(mesh_size/2.0f) - z;
        }
    }
}

あとは,OpenGLの初期化とかCgの初期化とかをInitialize関数で行います。

//---------------------------------------------------------------------------------------------------
//  Initialize
//  Desc : OpenGLの初期設定
//---------------------------------------------------------------------------------------------------
void Initialize()
{
    glClearColor(0.3, 0.3, 1.0, 1.0);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    // メッシュを作成
    CreateWaveMesh();

    // エラーチェック
    cgSetErrorCallback(Check_CgError);

    // コンテキストの作成
    context = cgCreateContext();
    if (context == NULL )
    {
        cout << "Error : Failed To Create Cg Context\n";
        return;
    }

    // 可能な限り高度な頂点プロファイルを使用
    vertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
    if ( vertexProfile == CG_PROFILE_UNKNOWN ||
        vertexProfile == CG_INVALID_ENUMERANT_ERROR)
    {
        cout << "Error : Invalid Profile Type\n";
        return;
    }

    // 頂点プログラムをCgファイルから作成
    vertexProgram = cgCreateProgramFromFile(
        context,            // コンテキストハンドル
        CG_SOURCE,  // Cgソースコードを使用
        "cg_wave.cg",   // Cgソースコード名
        vertexProfile,  // コンパイルする対象プロファイルの指定
        "Main",             // メインエントリポイント関数
        NULL    );

    // 頂点プログラムのロード
    cgGLLoadProgram(vertexProgram);

    // パラメータの取得
    position = cgGetNamedParameter(vertexProgram, "Input.position");
    color = cgGetNamedParameter(vertexProgram, "Input.color");
    wave = cgGetNamedParameter(vertexProgram, "Input.wave");
    modelViewMatrix = cgGetNamedParameter(vertexProgram, "ModelViewProj");

}

普通に面を書いてもいいのですが,普通に表示しちゃうと動いているかどうかがすごくわかりずらいので,
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)でワイヤーフレーム表示しています。
positionとcolorは頂点座標と色で,waveは波生成用のカウンターで,modelViewMatrixはモデルビュー射影行列です。
あとは描画関数です。
//---------------------------------------------------------------------------------------------------
//  RenderWaveMesh
//  Desc : 波メッシュを描画
//---------------------------------------------------------------------------------------------------
void RenderWaveMesh()
{
    for ( int x=0; x<mesh_size-1; x++ )
    {
        glBegin(GL_QUAD_STRIP);
        for ( int z=0; z<mesh_size-1; z++ )
        {
            cgGLSetParameter3f(wave, wave_move, 1.0f, 1.0f);
            glVertex3f(
                wave_mesh[x+0][z][0],
                wave_mesh[x+0][z][1],
                wave_mesh[x+0][z][2]
            );
            glVertex3f(
                wave_mesh[x+1][z][0],
                wave_mesh[x+1][z][1],
                wave_mesh[x+1][z][2]
            );
            wave_move += 0.00001f;
            if ( wave_move > 2.0*Pi ) wave_move = 0.0f;
        }
        glEnd();
    }
}
描画側では,QUAD_STRIPで四角形を描画して,waveパラメーターをセットします。あとはwave_moveが波生成のカウンタなのでカウントしてやります。wave_moveは角度(rad)に対応するもんです。なので一回転(=2π)したらカウンターを0に戻してやります。
Display関数は下のような感じです。
//---------------------------------------------------------------------------------------------------
//  Display
//  Desc : ウィンドウへの描画処理
//---------------------------------------------------------------------------------------------------
void Display()
{
    // バッファをクリア
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (double)WindowWidth/(double)WindowHeight, 0.1, 1000.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glViewport(0, 0, WindowWidth, WindowHeight);
    gluLookAt(
        0.0, 25.0, -45.0,
        0.0, 0.0, 0.0, 
        0.0, 1.0, 0.0 );

    // モデルビュー射影行列を設定
    cgGLSetStateMatrixParameter(
        modelViewMatrix,
        CG_GL_MODELVIEW_PROJECTION_MATRIX,
        CG_GL_MATRIX_IDENTITY);

    // 頂点プロファイルを有効化
    cgGLEnableProfile(vertexProfile);

    // 頂点プログラムをバインド
    cgGLBindProgram(vertexProgram);

    // パラメータを設定
    cgGLSetParameter4f(color, 1.0, 1.0, 1.0, 1.0);

    // 波を描画
    RenderWaveMesh();

    // 頂点プロファイルを無効化
    cgGLDisableProfile(vertexProfile);

    // ダブルバッファ
    glutSwapBuffers();
}



3.コーディング(Cg側)
 Cg側ではいつもどおり,入力用の構造体と出力用の構造体を用意します。
波生成のようカウンタはCOLOR1として入ってきます。実際には色としてではなく頂点を動かすために使います。
 頂点プログラムでやっているのは,wave.xにカウンタの数値が入っているので,これを用いて,入力されてきた頂点座標のy方向の成分をsin関数を使って適当に上下に変位させているだけです。
//--------------------------------------------------------------------------------------------------------
//  InputData
//--------------------------------------------------------------------------------------------------------
struct InputData
{
    float4 position : POSITION;
    float4 color : COLOR0;
    float3 wave : COLOR1;
};

//--------------------------------------------------------------------------------------------------------
//  OutputData
//--------------------------------------------------------------------------------------------------------
struct OutputData
{
    float4 position : POSITION;
    float4 color0 : COLOR0;
};

//--------------------------------------------------------------------------------------------------------
//  Main
//  Desc : エントリー関数
//--------------------------------------------------------------------------------------------------------
OutputData Main(InputData Input, uniform float4x4 ModelViewProj)
{
    OutputData Output;

    Input.position.y = (sin(Input.wave.x + (Input.position.x/5.0) ) + sin(Input.wave.x + (Input.position.z/4.0) ) )*2.5f;
    Output.position = mul(ModelViewProj, Input.position);
    Output.color0.xyz = Input.color.xyz;
    return Output;
}

一応,こんな感じで頂点プログラムで計算できるそうです。
ここでポイントになるのは,CPU側からそのまま情報が送れないことがあるので,テクスチャ座標や色などとしてごまかして変数を送るということですかね。


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