床井研究室

テクスチャマッピングについて

手抜きOpenGL にはテクスチャマッピングについて何も書いていないので,研究室の学生さん向けにこれから何回かに分けて簡単な説明を書いてみます.最初は「画像データの読み込み方」です.これは卒業研究などで結構学生さんが引っかかるところだったりしますが,実は自分でもよく悩むんです.特に気分が落ち込んでいると,物事が決められなくなって困るんですね.

画像ローダ

以前,某君が見付けてきた方法は,AUX ライブラリに含まれる auxRGBImageLoad()auxDIBImageLoad() を使うという手でした.この方法は Windows なんかだと確かにお手軽ですけど,GLUT 使っているのにここだけ AUX 使うのも何だかなぁと言う気がしないでもありません.その線でいけば,nvsdk に png を読む関数が入っていたような記憶がありますし,ATI のサンプルソースプログラムの中には確か tga を読み込むものがあったような気がしますから,こういうものを流用するという手もあるでしょう.また glpngドキュメント)というのも便利そうです.MSDN Library にも OpenGL V: Translating Windows DIBs という記事があります.他にも陳先生Raslib(うちの学生さんなら演習で使ったはずじゃ)をはじめ,Linux で使われる imlib とか jpegsrc とか,いろんな人が画像ローダを書いているので,探せばいくらでも見付かるように思います1(おっとぉ,自分でも昔,PPM や BMP を読み込むプログラム書いた覚えがあるぞ).

ただ,Linux でも Mac OS X でも Windows でも通るプログラムにしようとか色々考え出すと,こういうものの使い方を調べたり自分のプログラムに組み込んだりすること自体がなんだか億劫になってきます.それで思考停止の挙げ句,結局「RAW 画像ですませちゃえ」となってしまうんですね.実際はかえって手間がかかるんですけど…

RAW 画像を使う

Photoshop で保存できるファイルの形式の中に,「汎用フォーマット」というのがあります.RAW 画像形式とはこのことです.これは何のことはない,画像のサイズや形式に関する情報を含まず,データの圧縮も行わない,いわゆる「ベタファイル」のことです.これの嬉しいところは,画像データの読み込みが read() 一発で行えるというあたりにあります.

但し,RAW 画像はサイズや深度(色数)に関する情報も含んでいないので,読み込みプログラム内にそのあたりのことを作り込んでおく必要があります.このため,この方法を採用すると融通の利かないプログラムになってしまいます.でも,png や jpeg を読んで画像の大きさを調べ,それに合わせてメモリを確保して…みたいなことをやることの方が,私を滅入らせてしまうのです.

テクスチャ画像を作る

Photoshop(Elements でも可)や GIMP などを使って,テクスチャに使う画像を作ります.画像のモードは,ここでは「RGB モード」にします.また画像のサイズは,縦横 256×256 とか 512×128 という風に,2n 画素にします.OpenGL のテクスチャマッピングにおけるこの制限は,テクスチャを部分的に置き換える glTexSubImage2D() を使うならあまり気にする必要はありません.また OpenGL 2.0 では,この制限自体が撤廃されています.しかし,ここではこの OpenGL の伝統的なマナーに従うことにします.

作成した画像

画像ができたら,画像を別名で保存します.オリジナルは残しておきましょう.

画像を別名で保存

フォーマット(画像の形式)に「汎用フォーマット」を指定します..

汎用フォーマット

次に現れるダイアログで「インターリーブの順序」を選ぶと,カラーデータ(RGB)が1画素毎に順番に格納されます.そのほかの部分はどうでもいいんですが,「ヘッダ」は 0 にしておいて下さい.

インターリーブの順序

あと,どういうサイズの画像を作ったかは,プログラマが覚えておかないといけません.

画像ファイルの読み込み

こうして保存した画像を読み込むプログラムを考えます.一応,雛形のプログラムを用意してあります.以後,このプログラムをもとにして説明しますので,あらかじめをダウンロードして,自分の環境でビルドして実行してみてください.

1枚の四角形

このプログラムは四角形を1枚だけ表示します.一応,マウスで回転できるようになっています.実行できることが確認できたら,このソースプログラム main.cpp の以下の部分に,太字のところを追加してください.記号定数 TEXWIDTHTEXHEIGHT には,読み込む画像の横幅と高さの画素数を定義します.

/*
** 光源
*/
static const GLfloat lightpos[] = { 0.0f, 0.0f, 1.0f, 0.0f }; /* 位置    */
static const GLfloat lightcol[] = { 1.0f, 1.0f, 1.0f, 1.0f }; /* 直接光強度 */
static const GLfloat lightamb[] = { 0.1f, 0.1f, 0.1f, 1.0f }; /* 環境光強度 */

/*
** テクスチャ
*/
#define TEXWIDTH  256                               /* テクスチャの幅    */
#define TEXHEIGHT 256                               /* テクスチャの高さ   */
static const char texture_file[] = "tire.raw";      /* テクスチャファイル名 */

/*
** 初期化
*/
static void init()
{
  /* テクスチャの読み込みに使う配列 */
  GLubyte texture[TEXHEIGHT][TEXWIDTH][3];
  FILE *fp;

  /* テクスチャ画像の読み込み */
  if ((fp = fopen(texture_file, "rb")) != NULL) {
    fread(texture, sizeof texture, 1, fp);
    fclose(fp);
  }
  else {
    perror(texture_file);
  }

  /* 初期設定 */
  glClearColor(0.3f, 0.3f, 1.0f, 0.0f);
  glEnable(GL_DEPTH_TEST);
  glDisable(GL_CULL_FACE);

  /* 光源の初期設定 */
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, lightcol);
  glLightfv(GL_LIGHT0, GL_SPECULAR, lightcol);
  glLightfv(GL_LIGHT0, GL_AMBIENT, lightamb);
} 

perror() を使ってエラーメッセージを出してますけど,Windows でコンソールを出さないんだったら,別の方法を採用すべきでしょう(あるいはエラーメッセージを出さないか).また,fread() の戻り値をチェックしてませんので,もしファイルがうまく読めなかったら何が表示されるかはお楽しみ :-)

でも,ここまでだと単にファイルを読み込んでいるだけなので,実行結果は何も変わりありません(わざわざ説明を書かないといけないプログラムじゃないかも).読み込んだ画像をテクスチャとして物体に貼り付ける方法は,次回以降で説明します.

  1. これらは、現在 (2026 年時点) では、どれもリンク切れになっています。近年は stb_image.h が便利だという話です (いま見たら、プロジェクトのページセキュリティに関する警告が書いてありました)。