床井研究室

環境のテクスチャを最下層に置いたとき

前回の放物面マッピングでは、映り込みのテクスチャを下地のテクスチャの上から重ねてマッピングしたので、下地のテクスチャが隠されてしまいました。今回は、映り込みのテクスチャに下地に使っていたテクスチャを合成してみます。

テクスチャユニットを指定したテクスチャ座標の設定

前回作成したプログラムでは、もともとあったテクスチャを、テクスチャユニット2 (GL_TEXTURE0) に割り当てていました。しかし、このテクスチャは、実際には表示されませんでした。

今回は、これをテクスチャユニット2 (GL_TEXTURE2) に移します。このために、裏面の放物面マッピングをテクスチャユニット0 (GL_TEXTURE0) に、表面の放物面マッピングをテクスチャユニット1 (GL_TEXTURE1) に入れ替えます。

ただし、box.cpp で定義している立方体を描く関数 box() では、glTexCoord2dv() を使って各面に貼るテクスチャのテクスチャ座標を設定しています。この関数はデフォルトのテクスチャユニット、すなわちテクスチャユニット0 (GL_TEXTURE0) に対してテクスチャ座標を設定することができますが、テクスチャユニット2に割り当てたテクスチャに対してテクスチャ座標を設定することはできません。

glMultiTexCoord*()

特定のテクスチャユニットに対してテクスチャ座標を設定するには、glMultiTexCoord*() という関数群を使います。この関数も Windows の gl.h では宣言されていないので、Windows でこの関数を使用する場合は、glMultiTexCoord2dv() の関数ポインタ変数の宣言を main.cpp に追加する必要があります。

#if defined(__APPLE__)
#  define GL_SILENCE_DEPRECATION
#  include <GLUT/glut.h>
#  include <OpenGL/glext.h>
#else
#  if defined(_MSC_VER)
//#    pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#    define _USE_MATH_DEFINES
#    define _CRT_SECURE_NO_WARNINGS
#  endif
#  include <GL/glut.h>
#  include <GL/glext.h>
#  if defined(_WIN32)
PFNGLACTIVETEXTUREPROC glActiveTexture;
PFNGLMULTITEXCOORD2DVPROC glMultiTexCoord2dv;
#  endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

そしてプログラムの初期化時に、この関数ポインタ変数に関数の実体のエントリポイントを格納しておきます。

/*
** 初期化
*/
static void init()
{
  /* テクスチャ画像はワード単位に詰め込まれている */
  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

  /* テクスチャの読み込みに使う配列 */
  GLubyte texture[TEXHEIGHT * TEXWIDTH * 4];

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

  /* テクスチャの境界色 */
  static const GLfloat border[] = { 0.0f, 0.0f, 0.0f, 0.0f };

#if defined(_WIN32)
  glActiveTexture =
    (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture");
  glMultiTexCoord2dv =
    (PFNGLMULTITEXCOORD2DVPROC)wglGetProcAddress("glMultiTexCoord2dv");
#endif

下地のテクスチャを割り当てるテクスチャユニットを GL_TEXTUTRE2 に変更します。

  /* テクスチャ名を3つ作る */
  GLuint texname[3];
  glGenTextures(3, texname);

  /* 3つ目のテクスチャユニットには下地のテクスチャを割り当てる */
  glActiveTexture(GL_TEXTURE2);
  glBindTexture(GL_TEXTURE_2D, texname[0]);

裏面のテクスチャを割り当てるテクスチャユニットを GL_TEXTUTRE0 に変更します。

  /* 1つ目のテクスチャユニットには裏面の放物面テクスチャを割り当てる */
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, texname[1]);

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

表面のテクスチャを割り当てるテクスチャユニットを GL_TEXTUTRE1 に変更します。

  /* 2つ目のテクスチャユニットには表面の放物面テクスチャを割り当てる */
  glActiveTexture(GL_TEXTURE1);
  glBindTexture(GL_TEXTURE_2D, texname[2]);

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

描画する際のテクスチャユニットの指定も、忘れずに変更しておきます。

/*
** シーンの描画
*/
static void scene()
{
  static const GLfloat color[] = { 1.0f, 1.0f, 1.0f, 1.0f };   /* 材質 (色) */

  /* 材質の設定 */
  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);

  /* 下地のテクスチャマッピング開始 */
  glActiveTexture(GL_TEXTURE2);
  glEnable(GL_TEXTURE_2D);

  /* 裏面のテクスチャマッピング開始 */
  glActiveTexture(GL_TEXTURE0);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_TEXTURE_GEN_S);
  glEnable(GL_TEXTURE_GEN_T);
  glEnable(GL_TEXTURE_GEN_R);

  /* 表面のテクスチャマッピング開始 */
  glActiveTexture(GL_TEXTURE1);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_TEXTURE_GEN_S);
  glEnable(GL_TEXTURE_GEN_T);
  glEnable(GL_TEXTURE_GEN_R);

対応する描画終了後の処理でも、テクスチャユニットの指定を忘れずに変更しておきます。なんだか宗田ことをしていますが、趣味の問題なので気にしないでください。

  /* 表面のテクスチャマッピング終了 */
  glActiveTexture(GL_TEXTURE1);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glDisable(GL_TEXTURE_GEN_R);
  glDisable(GL_TEXTURE_2D);

  /* 裏面のテクスチャマッピング終了 */
  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glDisable(GL_TEXTURE_GEN_R);
  glDisable(GL_TEXTURE_2D);

  /* 下地のテクスチャマッピング終了 */
  glActiveTexture(GL_TEXTURE2);
  glDisable(GL_TEXTURE_2D);
}

box.cpp も、テクスチャ座標を glTexCoord2dv() を使って設定している部分を glMultiTexCoord2dv() に置き換えます。Windows (_WIN32) の場合は、関数ポインタ変数 glTexCoord2dv を外部変数として宣言しておいてください。

#if defined(__APPLE__) || defined(MACOSX)
#  define GL_SILENCE_DEPRECATION
#  include <GLUT/glut.h>
#  include <OpenGL/glext.h>
#else
#  include <GL/glut.h>
#  include <GL/glext.h>
#  if defined(_WIN32)
extern PFNGLMULTITEXCOORD2DVPROC glMultiTexCoord2dv;
#  endif
#endif

glTexCoord2dv()glMultiTexCoord2dv() に置き換えます。テクスチャユニットには GL_TEXTURE2 を指定します。

/*
** 箱の描画
*/
void box(double x, double y, double z)
{
  ...

  /* 四角形6枚で箱を描く */
  glBegin(GL_QUADS);
  for (j = 0; j < 6; ++j) {
    glNormal3dv(normal[j]);
    for (i = 0; i < 4; ++i) {
      /* テクスチャ座標の指定 */
      glMultiTexCoord2dv(GL_TEXTURE2, texcoord[j][i]);
      /* 対応する頂点座標の指定 */
      glVertex3dv(vertex[j][i]);
    }
  }
  glEnd();
}

こういう結果が得られます。

放物面マッピングによる映り込みに別のテクスチャを合成

もちろん、テクスチャユニット2 (GL_TEXTURE2) のテクスチャ環境の GL_MODULATEGL_COMBINE に置き換えて、線形補間によるテクスチャの合成を行うこともできます。

  /* 下地のテクスチャののテクスチャ環境 */
  //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
  static const GLfloat blend[] = { 1.0f, 1.0f, 1.0f, 0.5f };
  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blend);

放物面マッピングによる映り込みに別のテクスチャを線形補間による合成