[CG実習 > テクスチャマッピング]

テクスチャマッピング

物体の表面には,さまざまな模様がついていることが少なくありません. 模様が見えるのは,表面の各部分で光の反射が均一でないためです. そのような物体を描くにはどうすればよいでしょうか. 一つの方法として,たとえば,表面を小さなポリゴンに分割して, それぞれのポリゴンのマテリアルを適宜設定することが考えられます. しかし,このような方法は大変面倒です.

別の方法として,模様を画像として表現して,それを物体表面に貼りつけることが考えられます. このような手法をテクスチャマッピング(texture mapping)とよびます. ここでは,OpenGLでテクスチャマッピングを行う方法について説明します.

テクスチャマッピングの基礎

テクスチャマッピングは,テクスチャ画像を物体の表面に貼りつける手法です. 物体に画像を「貼りつける」ことは,物体の面を構成する各ピクセルに関して, 対応するテクスチャ画像の点を決めることで実現できます. 次に挙げる例では,テクスチャ画像を平行四辺形の平面物体に貼りつけています.

テクスチャ座標

テクスチャ画像には,通常のピクセルから構成される画像を用います. OpenGLで画像をテクスチャとして扱う場合,画像上の点は(s,t)で指定します. ここでs,tは,それぞれ0.0から1.0の間の値を取ります. つまり,テクスチャはピクセルの集まりではなく1辺が長さ1の正方形として扱われます. このとき,画像自体は正方形である必要はありません. 正方形でない場合,s方向とt方向では単位長が異なることになります. なお,テクスチャ画像を構成するピクセルのことをとくにテクセル(texel)といいます.

OpenGLのテクスチャには,1次元のテクスチャ,2次元のテクスチャ,3次元のテクスチャがありますが,ここでは,一般的に用いられる2次元のテクスチャだけを扱います.

サンプルプログラム(1)

まずテクスチャを貼る簡単なサンプルプログラム(texture2d.rb)を示します. このプログラムは,次のように画像を指定して実行します.

$ ruby texture2d.rb 画像ファイル名

実行すると,平行四辺形に画像が貼り付けられたウインドウが現われます.


001  # coding: utf-8
002  require 'opengl'
003  require 'glu'
004  require 'glut'
005  require 'cg/gfc'
006  
007  # ウインドウサイズ
008  WSIZE  = 600
009  WIDTH  = WSIZE
010  HEIGHT = WSIZE
011  
012  ### 描画コールバック ########
013  display = Proc.new {
014    w = 0.5; h = 0.7; a = 0.2; b = 0.1
015    GL.Clear(GL::COLOR_BUFFER_BIT)
016    # テクスチャを貼った平行四辺形の描画
017    GL.Begin(GL::POLYGON)
018      GL.TexCoord(0.0,0.0) # 頂点v0(-w+a,h-b)  ⇔ テクスチャ座標(0,0)
019      GL.Vertex(-w+a,h-b)
020      GL.TexCoord(0.0,1.0) # 頂点v1(-w-a,-h-b) ⇔ テクスチャ座標(0,1)
021      GL.Vertex(-w-a,-h-b)
022      GL.TexCoord(1.0,1.0) # 頂点v2(w-a,-h+b)  ⇔ テクスチャ座標(1,1)
023      GL.Vertex(w-a,-h+b)
024      GL.TexCoord(1.0,0.0) # 頂点v3(w+a,h+b)   ⇔ テクスチャ座標(1,0)
025      GL.Vertex(w+a,h+b)
026    GL.End()
027    GL.Flush()
028  }
029  
030  ### キーボード入力コールバック ########
031  keyboard = Proc.new { |key,x,y| 
032    exit 0 if key == 'q' or key.ord == 0x1b # [q],[ESC]で終了
033  }
034  
035  ### ウインドウサイズ変更コールバック ########
036  reshape = Proc.new { |w,h|
037    GL.Viewport(0,0,w,h)
038    GL.LoadIdentity()
039    x=w.to_f/WIDTH
040    y=h.to_f/HEIGHT
041    GLU.Ortho2D(-x,x,-y,y)
042    GLUT.PostRedisplay()
043  }
044  
045  ### テクスチャの設定 ########
046  def setup_texture(iname)
047    # 2次元テクスチャを使用可能にする.
048    GL.Enable(GL::TEXTURE_2D)
049  
050    # テクスチャに使用する画像の読み込み
051    g = Gfc.load(iname)
052    width,height = g.size
053  
054    # テクスチャ画像生成
055    #  GL::TEXTURE_2D    テクスチャの種別(target)
056    #  0                 テクスチャレベル(level)
057    #  GL::RGB           テクスチャ画像のフォーマット(iformat)
058    #  width,height      テクスチャ画像のサイズ(width,height)
059    #  0                 (テクセル)縁の幅(border)
060    #  GL::RGB           構築するテクセルのフォーマット(format)
061    #  GL::UNSIGNED_BYTE テクセルデータタイプ(type)
062    #  g0.get_bytes      画像データオブジェクト(texels)
063    #
064    #  typeとformatであるwidth×heightのtexelsの2次元テクスチャを構築する
065    #  画像データは,iformatの形式であると仮定する.
066    GL.TexImage2D(GL::TEXTURE_2D,0,GL::RGB,width,height,0,GL::RGB,
067  		GL::UNSIGNED_BYTE,g.get_bytes)
068  
069    # テクスチャ座標に対するパラメタ指定
070    #  GL::CLAMP  0.0以下が指定されたら0.0,1.0以上が指定されたら1.0とする
071    #  GL::REPEAT テクスチャがくり返しタイルされているとして座標を決める
072    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::REPEAT)
073    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::REPEAT)
074  
075    # ピクセルに対応するテクスチャの値の決定
076    # ピクセルの中心点をテクスチャ空間の点に移して,そのピクセルに対応する
077    # テクスチャの値を決める
078    #  GL::NEAREST  点にもっとも近いテクセルの値を使う
079    #  GL::LINEAR   点の周囲のテクセル値の平均を使う
080    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::NEAREST)
081    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::NEAREST)
082  end
083  
084  ##############################################
085  # main
086  ##############################################
087  
088  if ARGV.size == 0
089    STDERR.puts 'テクスチャ画像が指定されていません'
090    exit 1
091  end
092  
093  GLUT.Init()
094  GLUT.InitWindowSize(WIDTH,HEIGHT) 
095  GLUT.CreateWindow('Textured Quadrilateral')
096  GLUT.DisplayFunc(display)  
097  GLUT.KeyboardFunc(keyboard)
098  GLUT.ReshapeFunc(reshape)  
099  setup_texture(ARGV.shift)
100  GLUT.MainLoop()

テクスチャ画像の構築

OpenGLで画像をテクスチャとして利用するためには,GL.TexImage2Dを利用します.


054    # テクスチャ画像生成
055    #  GL::TEXTURE_2D    テクスチャの種別(target)
056    #  0                 テクスチャレベル(level)
057    #  GL::RGB           テクスチャ画像のフォーマット(iformat)
058    #  width,height      テクスチャ画像のサイズ(width,height)
059    #  0                 (テクセル)縁の幅(border)
060    #  GL::RGB           構築するテクセルのフォーマット(format)
061    #  GL::UNSIGNED_BYTE テクセルデータタイプ(type)
062    #  g0.get_bytes      画像データオブジェクト(texels)
063    #
064    #  typeとformatであるwidth×heightのtexelsの2次元テクスチャを構築する
065    #  画像データは,iformatの形式であると仮定する.
066    GL.TexImage2D(GL::TEXTURE_2D,0,GL::RGB,width,height,0,GL::RGB,
067  		GL::UNSIGNED_BYTE,g.get_bytes)

このメソッドは,合計9個の引数をとります.

テクスチャ座標の指定

すでに述べた通り,物体の面にテクスチャを貼りつけるには,物体の面を構成する各ピクセルについて,それに対応するテクスチャ画像の座標を決める必要があります. この対応関係を直接に決めるには,GL.TexCoordで,対象となる物体の各頂点のテクスチャ座標を指定します. 頂点以外については補間で決められます. 座標を与える際には,テクスチャ座標系と物体の座標系との関係に注意が必要です.


016    # テクスチャを貼った平行四辺形の描画
017    GL.Begin(GL::POLYGON)
018      GL.TexCoord(0.0,0.0) # 頂点v0(-w+a,h-b)  ⇔ テクスチャ座標(0,0)
019      GL.Vertex(-w+a,h-b)
020      GL.TexCoord(0.0,1.0) # 頂点v1(-w-a,-h-b) ⇔ テクスチャ座標(0,1)
021      GL.Vertex(-w-a,-h-b)
022      GL.TexCoord(1.0,1.0) # 頂点v2(w-a,-h+b)  ⇔ テクスチャ座標(1,1)
023      GL.Vertex(w-a,-h+b)
024      GL.TexCoord(1.0,0.0) # 頂点v3(w+a,h+b)   ⇔ テクスチャ座標(1,0)
025      GL.Vertex(w+a,h+b)
026    GL.End()

テクスチャパラメタ

テクスチャマッピングに関するパラメタに次の二つがあります.


069    # テクスチャ座標に対するパラメタ指定
070    #  GL::CLAMP  0.0以下が指定されたら0.0,1.0以上が指定されたら1.0とする
071    #  GL::REPEAT テクスチャがくり返しタイルされているとして座標を決める
072    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::REPEAT)
073    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::REPEAT)
074  
075    # ピクセルに対応するテクスチャの値の決定
076    # ピクセルの中心点をテクスチャ空間の点に移して,そのピクセルに対応する
077    # テクスチャの値を決める
078    #  GL::NEAREST  点にもっとも近いテクセルの値を使う
079    #  GL::LINEAR   点の周囲のテクセル値の平均を使う
080    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::NEAREST)
081    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::NEAREST)

テクスチャ座標パラメタ

テクスチャ座標は,0.0〜1.0で指定する必要があります. この範囲外の値が指定された場合に,GL.TexParameterを使って,次のどちらかの処理を行うようにできます. これらは,s座標,t座標に独立に指定できます.

テクスチャ値パラメタ

テクスチャを貼りつける面の形状により,テクスチャ画像のテクセルと描画するピクセルとの関係はさまざまに変わります. いくつかのピクセルが同一のテクセルに対応する場合もあれば, 一つのピクセルにいくつかのテクセルが対応する場合もあります. このとき,テクセルを拡大したり縮小したりすることが必要になります. 各ピクセルに対するテクスチャの値を決める方法は2通りあります. これらは,テクセルの拡大(MAGNIFICATION),縮小(MINIFICATION)のそれぞれの場合について独立に指定できます.

サンプルプログラム(2)

次のプログラム(texture_teapot.rb)では,指定した画像をテクスチャとして,ティーポットに貼りつけます. このプログラムでは,面に貼りつけるテクスチャ座標をどのように決めるかが主なポイントになります. プログラムは次のように実行します.

$ ruby texture_teapot.rb 画像ファイル名

なお,次のような操作ができます.


001  # coding: utf-8
002  require 'opengl'
003  require 'glu'
004  require 'glut'
005  require 'cg/camera'
006  require 'cg/gfc'
007  
008  INIT_THETA =  0.0  # カメラの初期位置
009  INIT_PHI   =  0.0  # カメラの初期位置
010  INIT_DIST  = 10.0  # カメラの原点からの距離の初期値
011  DT = 3             # 回転角単位
012  DZ = 0.125         # カメラの原点からの距離変更の単位
013  
014  WSIZE  = 600       # ウインドウサイズ
015  
016  ## 状態変数
017  __camera = Camera.new(INIT_THETA,INIT_PHI,INIT_DIST)
018  
019  ### 描画コールバック ########
020  display = Proc.new {
021    GL.Clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT)
022    GL.Light(GL::LIGHT0,GL::POSITION,[-1.0,1.0,1.0,0.0]) # 光源の位置
023    GL.Light(GL::LIGHT1,GL::POSITION,[1.0,-1.0,-1.0,0.0]) # 光源の位置
024  
025    GL.Material(GL::FRONT,GL::AMBIENT,  [0.2,0.2,0.2])
026    GL.Material(GL::FRONT,GL::DIFFUSE,  [0.8,0.8,0.8])
027    GL.Material(GL::FRONT,GL::SPECULAR, [0.1,0.1,0.1])
028    GL.Material(GL::FRONT,GL::SHININESS,64.0)
029  
030    # テクスチャ座標を自動生成するための式の設定
031    #  通常 s = ax + by + cz + d
032    #  GL::OBJECT_PLANE,[a,b,c,d]  物体座標系を基準とした式で指定
033    #  GL::EYE_PLANE,[a,b,c,d]     カメラ座標系を基準とした式で指定
034    GL.TexGen(GL::S,GL::OBJECT_PLANE,[1.0,0.0,0.0,0.0])
035    GL.TexGen(GL::T,GL::OBJECT_PLANE,[0.0,0.0,-1.0,0.0])
036  
037    GLUT.SolidTeapot(2.0)
038    GLUT.SwapBuffers()
039  }
040  
041  #### キーボード入力コールバック ########
042  keyboard = Proc.new { |key,x,y| 
043    case key
044    # [j],[J]: 経度の正方向/逆方向にカメラを移動する
045    when 'j','J'
046      __camera.move((key == 'j') ? DT : -DT,0,0)
047    # [k],[K]: 緯度の正方向/逆方向にカメラを移動する
048    when 'k','K'
049      __camera.move(0,(key == 'k') ? DT : -DT,0)
050    # [l],[L]: 
051    when 'l','L'
052      __camera.move(0,0,(key == 'l') ? DT : -DT)
053    # [z],[Z]: zoom in/out
054    when 'z','Z'
055      __camera.zoom((key == 'z') ? DZ : -DZ)
056    # [r]: 初期状態に戻す
057    when 'r'
058      __camera.reset
059    # [q],[ESC]: 終了する
060    when 'q'
061      exit 0
062    end
063    exit 0 if key.ord == 0x1b
064    
065    GLUT.PostRedisplay()
066  }
067  
068  #### ウインドウサイズ変更コールバック ########
069  reshape = Proc.new { |w,h|
070    GL.Viewport(0,0,w,h)
071    __camera.projection(w,h) 
072    GLUT.PostRedisplay()
073  }
074  
075  ### テクスチャの設定 ########
076  def setup_texture(iname)
077    ## テクスチャ画像の読み込み
078    g = Gfc.load(iname)
079    width,height = g.size
080  
081    ## テクスチャ生成
082    GL.TexImage2D(GL::TEXTURE_2D,0,GL::RGB,width,height,0,GL::RGB,
083  		GL::UNSIGNED_BYTE,g.get_bytes)
084  
085    ## テクスチャ座標に対するパラメタ指定
086    #GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::CLAMP)
087    #GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::CLAMP)
088    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::REPEAT)
089    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::REPEAT)
090  
091    ## ピクセルに対応するテクスチャの値の決定
092    
093    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::NEAREST)
094    GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::NEAREST)
095    #GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::LINEAR)
096    #GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::LINEAR)
097  
098    ## テクスチャの環境(表示方法)の指定
099    # GL::MODULATE テクスチャ色と物体色を掛け合わせて表示する
100    # GL::REPLACE  テクスチャ色をそのまま表示する
101    GL.TexEnv(GL::TEXTURE_ENV,GL::TEXTURE_ENV_MODE,GL::MODULATE)
102    #GL.TexEnv(GL::TEXTURE_ENV,GL::TEXTURE_ENV_MODE,GL::REPLACE)
103  
104    ## s方向についてテクスチャ座標を自動生成する
105    ## (※ t方向についても同様にGL::Tでテクスチャ座標の自動生成ができる)
106    # GL.TexGen(GL::S,GL::TEXTURE_GEN_MODE,basis)
107    # テクスチャ座標自動生成の基準(basis)の指定
108    #  GL::OBJECT_LINEAR 物体座標系を基準とする
109    #  GL::EYE_LINEAR    カメラ座標系を基準とする
110    GL.TexGen(GL::S,GL::TEXTURE_GEN_MODE,GL::OBJECT_LINEAR)
111    GL.TexGen(GL::T,GL::TEXTURE_GEN_MODE,GL::OBJECT_LINEAR)
112  
113    GL.Enable(GL::TEXTURE_2D)    # 2次元テクスチャを使用可能にする
114    GL.Enable(GL::TEXTURE_GEN_S) # s方向のテクスチャ座標の自動生成を有効にする
115    GL.Enable(GL::TEXTURE_GEN_T) # t方向のテクスチャ座標の自動生成を有効にする
116  end
117  
118  #### シェーディングの設定 ########
119  def init_shading
120    GL.Light(GL::LIGHT0,GL::AMBIENT, [0.1,0.1,0.1])
121    GL.Light(GL::LIGHT0,GL::DIFFUSE, [1.0,1.0,1.0])
122    GL.Light(GL::LIGHT0,GL::SPECULAR,[1.0,1.0,1.0])
123    GL.Light(GL::LIGHT1,GL::AMBIENT, [0.1,0.1,0.1])
124    GL.Light(GL::LIGHT1,GL::DIFFUSE, [1.0,1.0,1.0])
125    GL.Light(GL::LIGHT1,GL::SPECULAR,[1.0,1.0,1.0])
126  
127    GL.Enable(GL::LIGHTING)
128    GL.Enable(GL::LIGHT0)
129    GL.Enable(GL::LIGHT1)
130    GL.Enable(GL::NORMALIZE) # 法線の自動単位ベクトル化
131  end
132  
133  ##### main ##############################################
134  if ARGV.size == 0
135    STDERR.puts 'テクスチャ画像が指定されていません'
136    exit 1
137  end
138  
139  GLUT.Init()
140  GLUT.InitDisplayMode(GLUT::RGB|GLUT::DOUBLE|GLUT::DEPTH)
141  GLUT.InitWindowSize(WSIZE,WSIZE) 
142  GLUT.CreateWindow('Textured Teapot')
143  GLUT.DisplayFunc(display)
144  GLUT.KeyboardFunc(keyboard)
145  GLUT.ReshapeFunc(reshape)
146  GL.ClearColor(0.4,0.4,1.0,0.0)
147  GL.Enable(GL::DEPTH_TEST)
148  setup_texture(ARGV.shift) # テクスチャの設定
149  init_shading()            # シェーディングの設定
150  __camera.set              # カメラを配置する
151  GLUT.MainLoop()

テクスチャの環境(表示方法)の設定

テクスチャを貼りつけたときの物体の表面の色の決め方には,いくつかの方法があります. これは,GL.TexEnv(GL::TEXTURE_ENV,GL::TEXTURE_ENV_MODE,mode)のように指定します. 単純にテクスチャ値に置き換えるには,modeにGL::REPLACEを用います. GL::MODULATEにすることで,もともと物体に指定した色とテクスチャ値を掛け合わせることもできます. さらに,GL::DECALGL::BLENDにより, 透明度(α値)を利用して,テクスチャ値と物体の色を混合することもできます.


098    ## テクスチャの環境(表示方法)の指定
099    # GL::MODULATE テクスチャ色と物体色を掛け合わせて表示する
100    # GL::REPLACE  テクスチャ色をそのまま表示する
101    GL.TexEnv(GL::TEXTURE_ENV,GL::TEXTURE_ENV_MODE,GL::MODULATE)

テクスチャ座標の自動生成

物体の面を構成するピクセルとテクスチャ画像の対応関係を決めるのにGL.TexCoordで直接テクスチャ座標を指定する代わりに,テクスチャ座標を決める式を与えることもできます. これを行うには,GL.TexGenを利用します.

具体的には,テクスチャ座標の値を物体の各点の座標に関する1次式で指定することができます. このとき,点の座標は物体座標系あるいはカメラ座標系のいずれかで与えることができます. 式はs方向(GL::S)とt方向(GL::T)について独立に設定することができます. 指定方法の詳細については,次のコードを見て下さい.


030    # テクスチャ座標を自動生成するための式の設定
031    #  通常 s = ax + by + cz + d
032    #  GL::OBJECT_PLANE,[a,b,c,d]  物体座標系を基準とした式で指定
033    #  GL::EYE_PLANE,[a,b,c,d]     カメラ座標系を基準とした式で指定
034    GL.TexGen(GL::S,GL::OBJECT_PLANE,[1.0,0.0,0.0,0.0])
035    GL.TexGen(GL::T,GL::OBJECT_PLANE,[0.0,0.0,-1.0,0.0])
:
104    ## s方向についてテクスチャ座標を自動生成する
105    ## (※ t方向についても同様にGL::Tでテクスチャ座標の自動生成ができる)
106    # GL.TexGen(GL::S,GL::TEXTURE_GEN_MODE,basis)
107    # テクスチャ座標自動生成の基準(basis)の指定
108    #  GL::OBJECT_LINEAR 物体座標系を基準とする
109    #  GL::EYE_LINEAR    カメラ座標系を基準とする
110    GL.TexGen(GL::S,GL::TEXTURE_GEN_MODE,GL::OBJECT_LINEAR)
111    GL.TexGen(GL::T,GL::TEXTURE_GEN_MODE,GL::OBJECT_LINEAR)

テクスチャ処理のON/OFF

陰影処理Zバッファなどと同様にテクスチャ処理についても,設定を行うとともに,GL.Enableにより,明示的に処理を有効にする必要があります. 逆に無効化するにはGL.Disableを実行します.


113    GL.Enable(GL::TEXTURE_2D)    # 2次元テクスチャを使用可能にする
114    GL.Enable(GL::TEXTURE_GEN_S) # s方向のテクスチャ座標の自動生成を有効にする
115    GL.Enable(GL::TEXTURE_GEN_T) # t方向のテクスチャ座標の自動生成を有効にする

サンプルプログラム(3)

次のプログラム(texture_cube.rb)では, 立方体の各面に別々のテクスチャを貼りつけて表示します. 実行時には,6枚の画像を指定する必要があります. このプログラムでは,複数のテクスチャを内部でどのように扱うかがポイントになります. プログラムは次のように実行します.

$ ruby texture_cube.rb 画像ファイル名1 画像ファイル名2 ... 画像ファイル名6

なお,次のような操作ができます.


001  # coding: utf-8
002  require 'opengl'
003  require 'glu'
004  require 'glut'
005  require 'cg/camera'
006  require 'cg/colored_cube'
007  require 'cg/gfc'
008  
009  INIT_THETA =  0.0  # カメラの初期位置
010  INIT_PHI   =  0.0  # カメラの初期位置
011  INIT_DIST  = 10.0  # カメラの原点からの距離の初期値
012  DT = 3             # 回転角単位
013  DZ = 0.125         # カメラの原点からの距離変更の単位
014  
015  WSIZE  = 600       # ウインドウサイズ
016  
017  SZ=2.0 # 立方体のサイズ
018  # 立方体(頂点=(±SZ,0,0),(0,±SZ,0),(0,0,±SZ))
019  CUBE = ColoredCube.new(SZ,[0,0,0])
020  
021  ## 状態変数
022  __camera = Camera.new(INIT_THETA,INIT_PHI,INIT_DIST)
023  __texname = nil # テクスチャオブジェクトを管理するための変数
024  __texture_on = true
025  
026  # s方向の各面に対するテクスチャ座標自動生成式
027  TEXPLANE_S = [           # 対象とする立方体の面の式
028    [ 0.5/SZ,0.0,0.0,0.5], # z == 1 
029    [-0.5/SZ,0.0,0.0,0.5], # z == -1
030    [0.0,0.0,-0.5/SZ,0.5], # x == 1 
031    [0.0,0.0, 0.5/SZ,0.5], # x == -1
032    [ 0.5/SZ,0.0,0.0,0.5], # y == 1 
033    [ 0.5/SZ,0.0,0.0,0.5]  # y == -1
034  ]
035  
036  # t方向の各面に対するテクスチャ座標自動生成式
037  TEXPLANE_T = [           # 対象とする立方体の面の式
038    [0.0,-0.5/SZ,0.0,0.5], # z == 1 
039    [0.0,-0.5/SZ,0.0,0.5], # z == -1
040    [0.0,-0.5/SZ,0.0,0.5], # x == 1 
041    [0.0,-0.5/SZ,0.0,0.5], # x == -1
042    [0.0,0.0, 0.5/SZ,0.5], # y == 1 
043    [0.0,0.0,-0.5/SZ,0.5]  # y == -1
044  ]
045  
046  #### 描画コールバック ########
047  display = Proc.new {
048    GL.Clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT)
049    GL.Light(GL::LIGHT0,GL::POSITION,[-1.0,1.0,1.0,0.0]) # 光源の位置
050    GL.Light(GL::LIGHT1,GL::POSITION,[1.0,-1.0,-1.0,0.0])
051    draw_cube(__texname)
052    GLUT.SwapBuffers()
053  }
054  
055  #### 立方体の描画 ########
056  def draw_cube(texname)
057    # 各面を描く
058    6.times do |i|
059      # i番目のテクスチャオブジェクトを利用する
060      # (i番目のテクスチャ画像とテクスチャパラメタを使用する)
061      GL.BindTexture(GL::TEXTURE_2D,texname[i])
062      # テクスチャ座標の自動生成
063      GL.TexGen(GL::S,GL::OBJECT_PLANE,TEXPLANE_S[i])
064      GL.TexGen(GL::T,GL::OBJECT_PLANE,TEXPLANE_T[i])
065      # i番めの面の描画
066      CUBE.draw_face(i) 
067    end
068  end
069  
070  #### キーボード入力コールバック ########
071  keyboard = Proc.new { |key,x,y| 
072    case key
073    # [j],[J]: 経度の正方向/逆方向にカメラを移動する
074    when 'j','J'
075      __camera.move((key == 'j') ? DT : -DT,0,0)
076    # [k],[K]: 緯度の正方向/逆方向にカメラを移動する
077    when 'k','K'
078      __camera.move(0,(key == 'k') ? DT : -DT,0)
079    # [l],[L]: 
080    when 'l','L'
081      __camera.move(0,0,(key == 'l') ? DT : -DT)
082    # [z],[Z]: zoom in/out
083    when 'z','Z'
084      __camera.zoom((key == 'z') ? DZ : -DZ)
085    when 't','T'
086      if __texture_on
087        GL.Disable(GL::TEXTURE_2D)  # テクスチャマッピングOFF
088        GL.Disable(GL::LIGHTING)    # シェーディングOFF
089      else
090        GL.Enable(GL::TEXTURE_2D)   # テクスチャマッピングON
091        GL.Enable(GL::LIGHTING)     # シェーディングON
092      end
093      __texture_on = (not __texture_on)
094    # [r]: 初期状態に戻す
095    when 'r'
096      __camera.reset
097    # [q],[ESC]: 終了する
098    when 'q'
099      exit 0
100    end
101    exit 0 if key.ord == 0x1b
102  
103    GLUT.PostRedisplay()
104  }
105  
106  #### ウインドウサイズ変更コールバック ########
107  reshape = Proc.new { |w,h|
108    GL.Viewport(0,0,w,h)
109    __camera.projection(w,h) 
110    GLUT.PostRedisplay()
111  }
112  
113  ### テクスチャの設定 ########
114  def setup_texture(inames)
115    teximages=create_texture_images(inames) # テクスチャ画像の配列を構築する
116    texname = GL.GenTextures(6)             # テクスチャオブジェクトのIDの確保(6面分)
117  
118    # 立方体の各面に貼るテクスチャの設定
119    6.times do |i|
120      GL.BindTexture(GL::TEXTURE_2D,texname[i])
121  
122      ## テクスチャ画像生成
123      img,width,height = teximages[i]
124      GL.TexImage2D(GL::TEXTURE_2D,0,GL::RGB,width,height,0,GL::RGB,
125  		  GL::UNSIGNED_BYTE,img)
126  
127      ## テクスチャ座標に対するパラメタ指定
128      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::REPEAT)
129      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::REPEAT)
130  
131      ## ピクセルに対応するテクスチャの値の決定
132      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::NEAREST)
133      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::NEAREST)
134    end
135  
136    ## テクスチャの環境(表示方法)の指定(GL::REPLACE;テクスチャ値をそのまま使う)
137    GL.TexEnv(GL::TEXTURE_ENV,GL::TEXTURE_ENV_MODE,GL::REPLACE) 
138  
139    ## テクスチャ座標自動生成方法の指定→オブジェクトを基準とする
140    GL.TexGen(GL::S,GL::TEXTURE_GEN_MODE,GL::OBJECT_LINEAR)
141    GL.TexGen(GL::T,GL::TEXTURE_GEN_MODE,GL::OBJECT_LINEAR)
142  
143    GL.Enable(GL::TEXTURE_2D)    # 2次元テクスチャを使用可能にする
144    GL.Enable(GL::TEXTURE_GEN_S) # s方向のテクスチャ座標の自動生成を有効にする
145    GL.Enable(GL::TEXTURE_GEN_T) # t方向のテクスチャ座標の自動生成を有効にする
146  
147    texname
148  end
149  
150  ## テクスチャ画像の読み込み
151  # inames: テクスチャ画像ファイル名の配列
152  # 読みこんだ画像のデータ配列を返す[[pixels_0,width_0,height_0],...]
153  def create_texture_images(inames)
154    images=[]
155    inames.each do |iname|
156      g = Gfc.load(iname)
157      width,height = g.size
158      images.push([g.get_bytes,width,height])
159    end
160    images
161  end
162  
163  #### シェーディングの設定 ########
164  def init_shading
165    GL.Light(GL::LIGHT0,GL::AMBIENT, [0.1,0.1,0.1])
166    GL.Light(GL::LIGHT0,GL::DIFFUSE, [1.0,1.0,1.0])
167    GL.Light(GL::LIGHT0,GL::SPECULAR,[1.0,1.0,1.0])
168  
169    GL.Light(GL::LIGHT1,GL::AMBIENT, [0.1,0.1,0.1])
170    GL.Light(GL::LIGHT1,GL::DIFFUSE, [0.7,0.7,0.7])
171    GL.Light(GL::LIGHT1,GL::SPECULAR,[1.0,1.0,1.0])
172  
173    GL.Enable(GL::LIGHTING)
174    GL.Enable(GL::LIGHT0)
175    GL.Enable(GL::LIGHT1)
176    GL.Enable(GL::NORMALIZE) # 法線の自動単位ベクトル化
177  end
178  
179  ##### main ##############################################
180  if ARGV.size < 6
181    STDERR.puts 'テクスチャ画像を6枚指定して下さい'
182    exit 1
183  end
184  # 画像ファイルの配列を作る
185  ifiles=ARGV[0,6]
186  
187  GLUT.Init()
188  GLUT.InitDisplayMode(GLUT::RGB|GLUT::DOUBLE|GLUT::DEPTH)
189  GLUT.InitWindowSize(WSIZE,WSIZE) 
190  GLUT.CreateWindow('Fully Textured Cube')
191  GLUT.DisplayFunc(display)        
192  GLUT.KeyboardFunc(keyboard)      
193  GLUT.ReshapeFunc(reshape)
194  GL.ClearColor(0.4,0.4,1.0,0.0)   
195  GL.Enable(GL::DEPTH_TEST)
196  __texname=setup_texture(ifiles) # テクスチャの構築
197  init_shading()                  # シェーディングの設定
198  __camera.set                    # カメラを配置する
199  GLUT.MainLoop()

テクスチャオブジェクト

OpenGLでのテクスチャマッピング処理は,「現在のテクスチャ」を対象に行われます. これは,色,マテリアル,モデル・ビュー変換行列などが「現在のもの」を使って処理されるのと,まったく同様です. したがって,複数のテクスチャを使い分けるためには,その都度テクスチャを切り替える必要があることになります. ところが,テクスチャ画像を扱うには,大量なデータの処理が必要となりえますので,毎回ゼロからテクスチャを構築するのは,あまり効率的ではありません.

そこで,複数のテクスチャを使い分ける場合には,テクスチャオブジェクトを利用するのが効果的です. テクスチャオブジェクトは,テクスチャ画像テクスチャパラメタをまとめて保持しておくための仕組みを提供します. 予めテクスチャオブジェクトを構築しておけば,テクスチャの切り替えは,「現在のテクスチャ」に既存のテクスチャオブジェクトを対応させるだけの操作で実現できるようになります. ただし,その効果は,OpenGLでテクスチャ用に使用できるメモリの量に依存します. メモリが十分に利用できない場合,効果は限定的なものとなります.

テクスチャオブジェクトの生成と管理

テクスチャオブジェクトはGL.GenTextures(n)で確保します. これによりn個(n>0)のテクスチャオブジェクトのIDが確保されます. これによりオブジェクトを指し示すIDの配列が返されます. テクスチャオブジェクトは,このIDを利用して管理することになります.


116    texname = GL.GenTextures(6)             # テクスチャオブジェクトのIDの確保(6面分)

テクスチャオブジェクトの設定

テクスチャオブジェクトのデータ,すなわちテクスチャ画像テクスチャパラメタ は,(データ未設定の)テクスチャオブジェクトを「現在のテクスチャ」にしておいてから設定します. 「現在のテクスチャ」をテクスチャオブジェクトと対応づけるには,GL.BindTextureを実行します. 引数としてテクスチャの種別(GL::TEXTURE_2D)とテクスチャオブジェクトのIDを指定します.

なお,テクスチャオブジェクトのデータは,テクスチャ画像テクスチャパラメタであり,テクスチャの環境,あるいはテクスチャ座標自動生成のデータは,オブジェクトでは保持されないことに注意して下さい


118    # 立方体の各面に貼るテクスチャの設定
119    6.times do |i|
120      GL.BindTexture(GL::TEXTURE_2D,texname[i])
121  
122      ## テクスチャ画像生成
123      img,width,height = teximages[i]
124      GL.TexImage2D(GL::TEXTURE_2D,0,GL::RGB,width,height,0,GL::RGB,
125  		  GL::UNSIGNED_BYTE,img)
126  
127      ## テクスチャ座標に対するパラメタ指定
128      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::REPEAT)
129      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::REPEAT)
130  
131      ## ピクセルに対応するテクスチャの値の決定
132      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::NEAREST)
133      GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::NEAREST)
134    end

テクスチャオブジェクトの切り替え

テクスチャオブジェクトを実際に「現在のテクスチャ」として使うには,オブジェクトのデータを設定した後に,GL.BindTextureを実行します.


055  #### 立方体の描画 ########
056  def draw_cube(texname)
057    # 各面を描く
058    6.times do |i|
059      # i番目のテクスチャオブジェクトを利用する
060      # (i番目のテクスチャ画像とテクスチャパラメタを使用する)
061      GL.BindTexture(GL::TEXTURE_2D,texname[i])
062      # テクスチャ座標の自動生成
063      GL.TexGen(GL::S,GL::OBJECT_PLANE,TEXPLANE_S[i])
064      GL.TexGen(GL::T,GL::OBJECT_PLANE,TEXPLANE_T[i])
065      # i番めの面の描画
066      CUBE.draw_face(i) 
067    end
068  end

[CG実習 > テクスチャマッピング]