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

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