TOP

ABOUT

PROGRAM

DIALY







テクスチャを読み込む!!(1)

 1.はじめに…
 ポリゴンが表示できるようになったので,次はテクスチャファイルを読み込み表示するプログラムを組んでみます。
今回は汎用画像フォーマット(*.RAW)を読み込んでみます。このRAWファイルは明確なフォーマットがないので,結構困るんですけど,一応取り扱うRAWファイルはPhotoShopで作成されたものという前提でいきます。たしかGimpで出力しても読み込めると思います。




 2.RAWの読み込み
 汎用画像フォーマット(*.RAW)を実際に読み込んでみます。RAWファイルは無圧縮のいわゆるベタファイルです。RAWの明確なフォーマットは規定されていないため,RAWファイルを出力する画像ソフトやデジカメなんかによって全然中身が異なったりしますが,無圧縮であるために画像が綺麗であるという利点があるのが特徴です。今回はGimpやPhotoShopでRAWが出力されていてヘッダ情報なしで,RGBまたはRGBAの順番にピクセルデータが格納されているものという前提で読み込みをしていきます。
 注意して欲しいのはヘッダ情報がないので,画像データを出力した祭に画像の幅や高さ,RGBかRGBAかということをすべて記憶しておかなければならない点です。これを忘れてしまうと読み込みの際に「えらいこっちゃ!!」になって,きちんとした結果が表示されないと思うので,きっちり覚えておいてください。(この辺がRAWの面倒くさいところなんですけどね…。)
まずは以下のように,ヘッダで読み込みに必要な変数などをRAWImageというクラスで宣言してやります。

//
// include
//
#include <iostream>
#include <GL/glut.h>
using namespace std;

//////////////////////////////////////////////////////////////////////////
//  RAWImage class
//////////////////////////////////////////////////////////////////////////
class RAWImage
{
protected:
    GLuint imageSize;
    GLubyte *imageData;
    GLenum format;
    GLuint internalFormat;
    GLuint width;
    GLuint height;
    GLuint bpp;
public:
    GLuint ID;
    RAWImage();
    ~RAWImage();
    bool ReadRAW(const char *filename, const GLuint width, const GLuint height, bool alphaFlag=false);
    bool Load(const char *filename, const GLuint width, const GLuint height, bool alphaFlag=false);
};


 一応簡単に上から順に説明すると,imageSizeには画像のデータサイズが入ります。つぎのimageDataには画像のピクセルデータ(テクセル)が入ります。次のformatにはピクセルデータのフォーマットを指定するための変数が入り,internalFormatは画像データのインターナルフォーマットが入ります。具体的にはGL_RGBAとかGL_RGBとかが入ってきます。OpenGL Super Bible(Third Edition)をもっている方は428ページあたりにきちんと書いてあったりします。 widthとheightはその名のとおり画像の幅と高さが入ります。データサイズを決めるのに使ったり,for文でぐるぐる回すときに使います。
 つぎに関数のほうですが,コンストラクタとデストラクタはいつもとおり初期化とか後片付けなのでたいしたことはしません。
ReadRAWという関数は実際にC++側でファイルを開いてピクセルデータを読み取って格納していきます。
Loadという関数では,ReadRAWを呼び出してデータを格納し,テクスチャの生成・設定をします。
 ざっと説明しましたが,中身は下のようになっています。

//
// include
//
#include <fstream>
#include "RAWLoader.h"
using namespace std;


//////////////////////////////////////////////////////////////////////////
//  RAWImage class
//////////////////////////////////////////////////////////////////////////

//----------------------------------------------------------------------------------------------------
//  RAWImage
//  Desc : コンストラクタ
//----------------------------------------------------------------------------------------------------
RAWImage::RAWImage()
{
    imageSize = 0;
    imageData = NULL;
    format = GL_RGB;
    internalFormat = GL_RGB;
    width = 0;
    height = 0;
    bpp = 0;
    ID = 0;
}

//-----------------------------------------------------------------------------------------------------
//  ~RAWImage
//  Desc : デストラクタ
//-----------------------------------------------------------------------------------------------------
RAWImage::~RAWImage()
{
    if ( imageData )
    {
        delete [] imageData;
        imageData = NULL;
    }
}

//-----------------------------------------------------------------------------------------------------
//  ReadRAW
//  Desc : ファイル読み込み
//-----------------------------------------------------------------------------------------------------
bool RAWImage::ReadRAW(const char *filename, const GLuint width, const GLuint height, bool alphaFlag)
{
    ifstream file;

    file.open(filename, ios::in | ios::binary);
    if ( !file.is_open() )
    {
        cout << "Error : 指定されたファイルを開けません\n";
        cout << "File Name : " << filename << endl;
        return false;
    }

    if ( alphaFlag )
    {
        imageSize = width*height*4;
        format = GL_RGBA;
        internalFormat = GL_RGBA;
    }
    else
    {
        imageSize = width*height*3;
        format = GL_RGB;
        internalFormat = GL_RGB;
    }

    imageData = new GLubyte [imageSize];

    file.read((char*)imageData, imageSize);

    file.close();

    return true;    
}

//-----------------------------------------------------------------------------------------------------
//  Load
//  Desc : ファイルを読み込みテクスチャを作成
//-----------------------------------------------------------------------------------------------------
bool RAWImage::Load(const char *filename, const GLuint width, const GLuint height, bool alphaFlag)
{
    if ( !ReadRAW(filename, width, height, alphaFlag) )
        return false;

    // テクスチャを生成
    glGenTextures(1, &ID);

    // テクスチャをバインドする
    glBindTexture(GL_TEXTURE_2D, ID);

    if ( alphaFlag ) glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    else glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // テクスチャの割り当て
    gluBuild2DMipmaps(
        GL_TEXTURE_2D,
        internalFormat,
        width,
        height,
        format,
        GL_UNSIGNED_BYTE,
        imageData );

    // テクスチャを拡大・縮小する方法の指定
    glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,  GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

    // テクスチャ環境
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    //
    glBindTexture(GL_TEXTURE_2D, 0);

    // いらなくなったメモリを破棄
    delete [] imageData;
    imageData = NULL;

    return true;
}


 上に書いてある細かい,glGenTexturesとかglTexParameteriやらの関数はPocolが書くよりも赤坂さんのWisdomSoft(古いほう)のページを見たほうがためになると思うので省略します。
 ReadRAW関数ですが,まずはファイルを開きます。もちろん画像ファイルなので,開くときはバイナリで開くのを忘れないでください。で,ちゃんと開いているかどうかをis_open()関数を使って確認します。
 つぎに画像のデータサイズを決めます。RGBAの場合は横にRGBARGBARGBA……という風にデーターが並んでいきます。よって横は(画像の幅×4)のサイズになり,これが縦にもあるので画像サイズは(画像の幅×画像の高さ×4)となります。このデータサイズでメモリを確保します。メモリが確保できたら,read関数をつかって一気にピクセルデータを格納します。読み込み終わったらclose関数でファイルを閉じて終了です。


 4.テクスチャマッピング
 Load関数でテクスチャの作成までが終わっているので,あとはglEnable(GL_TEXTURE_2D)とglBindTexture関数を呼び出して,ポリゴンに画像を貼り付けます。ポリゴンに貼り付ける際は画像のどの位置と対応させるかというテクスチャ座標の指定を必要があります。これはglTexCoord2d関数を用いて行います。一応+1.0〜-1.0の範囲でテクスチャ座標値を指定します。テクスチャ座標の指定をしてポリゴンを描画し終えたら,あとはglDisable(GL_TEXTURE_2D)とglBindTexture(GL_TEXTURE_2D, 0)で後片付けをしてあげます。
//
// include
//
#include <iostream>
#include <GL/glut.h>
#include "RawLoader.h"
using namespace std;

//
// global
//
int WindowPositionX = 100;
int WindowPositionY = 100;
int WindowWidth = 512;
int WindowHeight = 512;
char WindowTitle[] = "Texture Mapping (1) - Raw File -";
RAWImage texture;

//
// prototype
//
void Initialize();
void Display();
void Idle();


//----------------------------------------------------------------------------------------------------
//  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
//  Desc : 初期化処理
//----------------------------------------------------------------------------------------------------
void Initialize()
{
    glClearColor(0.3, 0.3, 1.0, 1.0);
    glEnable(GL_DEPTH_TEST);

    texture.Load("sample.raw", 512, 512);

}

//---------------------------------------------------------------------------------------------------
//  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()
{
    double size = 0.5;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // テクスチャマッピング有効化
    glEnable(GL_TEXTURE_2D);
    // テクスチャをバインド
    glBindTexture(GL_TEXTURE_2D, texture.ID);
    // 色の指定
    glColor4f(1.0, 1.0, 1.0, 1.0);
    // 四角形をテクスチャ座標つきで描画
    glBegin(GL_QUADS);
    glTexCoord2d(-1.0, 1.0);    glVertex3d(-size, -size, 0.0);
    glTexCoord2d(-1.0, 0.0);    glVertex3d(-size, size, 0.0);
    glTexCoord2d(0.0, 0.0); glVertex3d(size, size, 0.0);
    glTexCoord2d(0.0, 1.0); glVertex3d(size, -size, 0.0);
    glEnd();
    // 
    glBindTexture(GL_TEXTURE_2D, 0);
    // テクスチャマッピング無効化
    glDisable(GL_TEXTURE_2D);

    Idle();
    glutSwapBuffers();
}


こんな感じで一応画像の読み込みができます。


 5.おわりに…
今回はRAWの説明をざっとしました。おそらくテクスチャはOpenGL側の設定よりもどうやってファイルを解析して読み込むか?ということに頭を悩ませるとこだと思うので,その辺をざっと説明していこうと思います。


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