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

テクスチャ画像には,通常のピクセルから構成される画像を用います. OpenGLで画像をテクスチャとして扱う場合,画像上の点は(s,t)で指定します. ここでs,tは,それぞれ0.0から1.0の間の値を取ります. つまり,テクスチャはピクセルの集まりではなく1辺が長さ1の正方形として扱われます. このとき,画像自体は正方形である必要はありません. 正方形でない場合,s方向とt方向では単位長が異なることになります. なお,テクスチャ画像を構成するピクセルのことをとくにテクセル(texel)といいます.
サンプルプログラム(1)
まずテクスチャを貼る簡単なサンプルプログラム(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個の引数をとります.
- 1番目の引数(target)には,GL::TEXTURE_2Dを指定します.
- 2番目の引数(level)は,ミップマップ(mipmap)といって,
テクスチャ処理を効率的に行うために一つのテクスチャに対して,複数の解像度のものを用意するときに利用するものです.
そのようなことを行わない場合には,0を指定します.
- 3番目の引数(iformat)には,読みこむ画像データのピクセルの種類(GL::RGB,GL::RGBAなど)を指定します.
- 4,5番目の引数(width,height)は,画像のサイズを表します.
- 6番目の引数(border)には,テクスチャ画像のピクセル(つまりテクセル)のうち,
周囲1ピクセルを境界部分として定義する場合に1を指定します.
このようにして定義する境界のテクセルは,物体に貼るテクスチャの値を
いくつかのテクセルの平均値で決めるようにテクスチャパラメタを指定した場合(GL::LINEAR)に利用されます.
[注意]:: このパラメタの設定方法はOpenGLのバージョンによって異なるようです.
- 7番目の引数(format)には,構築するテクスチャのテクセルの種類(GL::RGB,GL::RGBAなど)を指定します.
- 8番目の引数(type)には,構築するテクスチャのテクセルの各要素のデータタイプを指定します.通常は,GL::UNSIGNED_BYTEを指定します.
- 9番目の引数(texel) には,画像のデータを与えます. これは,Ruby/OpenGLでは,文字列のデータとして渡すことになります(文字といっても実体は整数の列であることに注意して下さい). Gfcライブラリを利用する場合,Gfcオブジェクトのget_bytesメソッドで,必要なデータを得ることができます.
テクスチャ座標の指定
すでに述べた通り,物体の面にテクスチャを貼りつけるには,物体の面を構成する各ピクセルについて,それに対応するテクスチャ画像の座標を決める必要があります. この対応関係を直接に決めるには,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座標に独立に指定できます.
- 0.0以下は0.0,1.0以上は1.0とする(clamp)
GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::CLAMP) GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::CLAMP)
- テクスチャ画像が(s,t)平面にくり返し敷きつめられているものとして扱う(repeat)
GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_S,GL::REPEAT) GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_WRAP_T,GL::REPEAT)
テクスチャ値パラメタ
テクスチャを貼りつける面の形状により,テクスチャ画像のテクセルと描画するピクセルとの関係はさまざまに変わります. いくつかのピクセルが同一のテクセルに対応する場合もあれば, 一つのピクセルにいくつかのテクセルが対応する場合もあります. このとき,テクセルを拡大したり縮小したりすることが必要になります. 各ピクセルに対するテクスチャの値を決める方法は2通りあります. これらは,テクセルの拡大(MAGNIFICATION),縮小(MINIFICATION)のそれぞれの場合について独立に指定できます.
- 指定した点にもっとも近いテクセルの値を使う(nearest)
GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::NEAREST) GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::NEAREST)
- 指定した点の周囲のテクセルの平均を使う(linear)
GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MIN_FILTER,GL::LINEAR) GL.TexParameter(GL::TEXTURE_2D,GL::TEXTURE_MAG_FILTER,GL::LINEAR)
サンプルプログラム(2)
次のプログラム(texture_teapot.rb)では,指定した画像をテクスチャとして,ティーポットに貼りつけます. このプログラムでは,面に貼りつけるテクスチャ座標をどのように決めるかが主なポイントになります. プログラムは次のように実行します.
なお,次のような操作ができます.
- [j]/[J],[k]/[K],[l]/[L]でカメラの位置と姿勢を変えることができます.
- [z]/[Z]でカメラの原点からの距離を変えます.
- [r]で初期状態に戻ります.
- [q],[ESC]でプログラムを終了します.
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::DECALやGL::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枚の画像を指定する必要があります. このプログラムでは,複数のテクスチャを内部でどのように扱うかがポイントになります. プログラムは次のように実行します.
なお,次のような操作ができます.
- [j]/[J],[k]/[K],[l]/[L]でカメラの位置と姿勢を変えることができます.
- [z]/[Z]でカメラの原点からの距離を変えます.
- [r]で立方体の向きを初期状態に戻します.
- [q],[ESC]でプログラムを終了します.
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