libpngでpng画像ファイルからOpenGLテクスチャを作成


OpenGLでlibpngを利用してテクスチャを貼り付ける手順を調べたのでメモ。

OpenGLでテクスチャを利用する際、画像ファイルフォーマットのパースはOpenGLでサポートされないので自前でパースするか、あるいは既存のライブラリを利用するなどして読み込む必要がある。

C/C++で画像ファイルを扱うライブラリは例えば以下がある。

boost::gil C++のBoostライブラリに含まれるGeneric Image Library.一番汎用に見える。jpgやpngを扱うにはこのライブラリのほかにlibjpgやlibpngがインストールされている必要がある。ばりばりにテンプレートを使う。
libpng,libjpg C言語用の画像処理ライブラリ。
OpenCV Intelで作られた画像処理ライブラリ。
MIST 名古屋大学情報系のポスドクとか学生が作っているらしいライブラリ。詳しくはよく分からない。公式サイト

boost::gilはイマイチよく分からんかったwので、使い方が比較的分かりやすくて透過画像を扱えるlibpngを使う事にした。

使い方は以下を参考にした。画像ファイルをただ読み込むだけならここの内容の一部を読むだけで足りる。
http://gmoon.jp/png/

というわけでコードが見やすいようにpng画像を適当に読み込んでテクスチャを作成するクラスを作る事にした。んー、テキト〜。もっと汎用にするならテクスチャクラスと画像クラスを分けて複数の画像フォーマットをサポートできるようにするとか、テクスチャを作成するときのパラメータやビット深度を変えられるように考える必要があると思う。

PngTexture.hpp

#ifndef PNG_TETXURE_HPP
#define PNG_TEXTURE_HPP

#include<string>

typedef unsigned char ubyte_t;
class PngTexture
{
    unsigned int id; // テクスチャID
    ubyte_t *data; // 生データを保持する
    unsigned int width, height;
    int depth, colortype, interlacetype;
    std::string filename;

    void init();
    void final();
public:
    PngTexture();
    PngTexture(const std::string&, unsigned int);
    ~PngTexture();
    ubyte_t* rawData();
    unsigned int getID();

    unsigned int getWidth();
    unsigned int getHeight();
};

#endif

PngTexture.cpp

#include<stdexcept>
#include"PngTexture.hpp"
#include<GL/gl.h>
#include<GL/glu.h>
#include<GL/glut.h>
#include<png.h>

PngTexture::PngTexture(const std::string& fname, unsigned int tid)
{
    filename = fname;
    id = tid;
    init();
}

PngTexture::~PngTexture()
{
    final();
}

void PngTexture::init()
{
    // png画像ファイルのロード
    png_structp sp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop   ip = png_create_info_struct(sp);
    FILE       *fp = fopen(filename.c_str(), "rb");
    if (!fp){
        perror(filename.c_str());
    }
    png_init_io(sp, fp);
    png_read_info(sp, ip);
    png_get_IHDR(sp, ip, (png_uint_32*)&width, (png_uint_32*)&height,
        &depth, &colortype, &interlacetype, NULL, NULL);
    // メモリ領域確保
    int rb = png_get_rowbytes(sp, ip);
    data = new ubyte_t[height * rb];
    ubyte_t **recv = new ubyte_t*[height];
    for (int i = 0; i < height; i++)
        recv[i] = &data[i * rb];
    png_read_image(sp, recv);
    png_read_end(sp, ip);
    png_destroy_read_struct(&sp, &ip, NULL);
    fclose(fp);
    delete[] recv;

    // テクスチャへの登録
    glBindTexture(GL_TEXTURE_2D, id);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
}

void PngTexture::final()
{ free(data); }

unsigned char* PngTexture::rawData()
{ return data; }

unsigned int PngTexture::getID()
{ return id; }

unsigned int PngTexture::getWidth()
{ return width; }

unsigned int PngTexture::getHeight()
{ return height; }