■テクスチャーマッピング

テクスチャーマッピングは、ポリゴンの表面に2次元や3次元の画像を適用します。(1次元の場合もありますが、Java 3Dには1次元テクスチャーのクラスはありません)

テクスチャーマッピングのための画像は、GIF, JPEG などのグラフィックファイルを読み込んで生成することができます。また、java.awt.image.BufferedImage を使ってプログラム中で生成することもできます。

2次元テクスチャーは「1枚の絵」と考えてください。 テクスチャーをポリゴンに張り付けるのにはテクスチャー座標というものを使用します。 テクスチャーは 0.0〜1.0 の範囲の2次元テクスチャー座標 s, t の間に引き延ばされ (または縮められ) る、 と考えてください。 s は右方向、 t は上方向が正方向(プラス方向)になります。

TXCOORD_2.jpg

ポリゴンの各頂点にテクスチャー座標 (s, t) を設定し、ポリゴンにテクスチャーをマッピングします。

TextureApplet1.gif

この例では四角形ポリゴン (QuadArray) の 4つの頂点にテクスチャー座標を設定し、テクスチャーマッピングしています。

テクスチャー座標はテクスチャーが持つ独自の座標系で、Java 3Dの仮想空間における3次元座標とは別個の座標系です。

ポリゴンの各頂点にテクスチャー座標を設定すると、ポリゴンの各ピクセルがテクスチャーの各テクセルの色で描画されます。テクスチャーのピクセルのことを"テクセル"と言うことがあります。

ポリゴンの各ピクセルに対してテクスチャーのどのテクセルが対応するかは、ポリゴンの頂点に設定したテクスチャー座標によって決定されます。頂点間の各ピクセルは、「頂点に設定したテクスチャー座標」間のテクセルによってそれぞれ描画されます。

0.0〜1.0 の範囲を超えるテクスチャー座標を適用した場合はどうなるでしょうか。

TextureApplet4.gif

0.0〜1.0 の範囲を超えたときに、テクスチャーを繰り返し適用することができます。テクスチャーを繰り返し適用するモードを WRAP と呼びます。

TextureApplet5.gif

0.0〜1.0 の範囲だけにテクスチャーを適用することもできます。テクスチャーを0.0〜1.0 の範囲だけに適用するモードを CLAMP と呼びます。

3次元テクスチャーは「複数枚の重なった絵」と考えてください。3次元のテクスチャー座標は s, t に加えて r 座標を持ちます。r は奥行き(深さ)方向が正方向になります。

CircleImage.gif

TEXCOORD_3.gif

たとえば、木の年輪を考えてください。年輪は木の中で同心円の円柱のように重なっています。この木を任意の形に掘り出すと、木彫りの彫刻の表面には年輪の模様が描かれます。

年輪のような同心円状の絵が複数重なった3次元テクスチャーを作成したとします。任意の形状の物体にこのテクスチャーを適用すると、物体の表面には木彫りの彫刻の表面のような年輪状の模様が描かれることになります。

3次元テクスチャーは、ポリゴンの頂点に (s, t, r) で表現されるテクスチャー座標を適用し、テクスチャーをポリゴンにマッピングします。

サンプルの実行例を見てください。

Texture3DTest.gif

年輪のような3次元テクスチャーを作成し、年輪の色を深さに応じて変化させています。物体の表面に年輪状のテクスチャーが適用されます。

javax.media.j3d.Texture

javax.media.j3d.Texture はテクスチャーマッピングの形式、画像フォーマット、画像の幅、高さなどを定義し、テクスチャーマップのための javax.media.j3d.ImageComponent オブジェクトを保持します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.Texture

クラス宣言

public abstract class Texture
extends NodeComponent

Texture は抽象クラスです。実際にはそのサブクラスである javax.media.j3d.Texture2D, javax.media.j3d.Texture3D を使用します。

コンストラクター

public Texture()

public Texture(int mipMapMode, // MipMap形式
               int format,     // テクスチャーフォーマット
               int width,      // 幅
               int height)     // 高さ

定数フィールド (一部)

public static final int BASE_LEVEL         // 基本レベルの画像のみ
public static final int MULTI_LEVEL_MIPMAP // 複数の MipMap レベルを指定する

Texture のコンストラクターでは、MipMap形式、テクスチャーフォーマット、テクスチャーの幅、高さなどを指定します。

MipMapとは何でしょうか。

face128.gif face64.gif face32.gif face16.gif face8.gif face4.gif face2.gif face1.gif
128×128 64×64 32×32 16×16 8×8 4×4 2×2 1×1

通常遠くにある物体は小さく描画されますので、遠くの物体に解像度が高いテクスチャー画像を適用しても無意味です。
逆に、視点の近くにある物体に解像度が低いテクスチャーが適用してあると、テクスチャー画像のピクセル(余談ですがこれを「テクセル」と呼ぶことがあります)が拡大されてリアル感を損なうことになります。

視点からの距離に応じて適切なテクスチャー画像を適用するには、あらかじめ複数のテクスチャー画像を指定して、距離に応じて切替えるという技法があります。このために、徐々に解像度が小さくなっていく複数の画像が必要です。この複数の画像を MipMap と呼びます。

mipMapModeBASE_LEVEL のときは距離に関係なく一つのテクスチャー画像(基本レベルテクスチャー)が適用されます。デフォルトは BASE_LEVEL です。

mipMapModeMULTI_LEVEL_MIPMAP のときは距離に応じた複数のテクスチャー画像を適用します。MULTI_LEVER_MIPMAP を指定した場合、すべての MipMap レベルのテクスチャー画像を用意して適用しておく必要が生じます。

format にはテクスチャー画像のフォーマットを指定します。これには次のものがあります。

定数フィールド (一部)

public static final int INTENSITY          // 輝度のみ
public static final int LUMINANCE          // 光度のみ
public static final int ALPHA              // 不透明度(アルファ値)のみ
public static final int LUMINANCE_ALPHA    // 光度と不透明度(アルファ値)
public static final int RGB                // 赤、緑、青の色
public static final int RGBA               // 赤、緑、青、不透明度(アルファ値)

テクスチャー画像のフォーマットは RGB, RGBA が多いと思いますが、INTENSITY(輝度のみ)、LUMINANCE(光度のみ)、ALPHA(不透明度のみ)、LUMINANCE_ALPHA光度と不透明度(アルファ値)を選ぶこともできます。

javax.media.j3d.TextureAttributes を設定いて、物体の色とテクスチャー画像をいろいろなモードで混合することができます。輝度のみ、光度のみ、不透明度のみ、光度と不透明度などのフォーマットのテクスチャーと物体の色を混合すると様々な効果が得られると思います。
もちろん RGB, RGBA 形式のテクスチャー画像と混合することも可能です。

MODULATE.gif

テクスチャー画像の設定には Texture#setImage(), Texture#setImages() メソッドを使用します。

メソッド (一部)

public final void setImage(int level,            // MipMapレベル
                           ImageComponent image) // テクスチャー画像の ImageComponent

public final void setImages(ImageComponent[] images) // 複数の MipMapレベルに応じた ImageComponent の配列

テクスチャー画像は、javax.media.j3d.ImageComponent オブジェクトで指定します。ImageComponent は抽象クラスで、実際にはそのサブクラスの ImageComponent2D, ImageComponent3D を使用します。

mipMapModeBASE_LEVEL を指定したとき、基本レベルの ImageComponent を一つだけ設定します。このとき、setImage() の引数の level(MipMapレベル)は 0 です。

mipMapModeMULTI_LEVEL_MIPMAP を指定したとき、すべてのMipMapレベルの ImageComponent が必要です。
MipMapレベルの数はテクスチャー画像の大きさによって一意に決まります。

テクスチャー画像のサイズは 2n×2n ピクセルの正方形である必要があります。

face128.gif face64.gif face32.gif face16.gif face8.gif face4.gif face2.gif face1.gif
128×128 64×64 32×32 16×16 8×8 4×4 2×2 1×1

このとき (n+1) の数の MipMapレベルが必要になります。(基本レベルを含む)

例えばテクスチャー画像のサイズが 128×128 ピクセル (27×27 ピクセル) のとき、(7+1) の数の MipMapレベルが必要です。
128×128ピクセルの基本レベルから 64×64, 32×32, 16×16, 8×8, 4×4, 2×2 ... と徐々に解像度を低下させて行き、最終的には 1×1 ピクセルまで解像度を低下させます。

mipMapModeMULTI_LEVEL_MIPMAP を指定したとき、すべての MipMapレベルに応じた、異なる解像度の複数のテクスチャー画像を作成する必要があります

ポリゴンの頂点に指定したテクスチャー座標が 0.0〜1.0 の範囲を超えるとき、テクスチャーを繰り返し適用するか、それとも 0.0〜1.0 の範囲だけにテクスチャーを適用するかを選ぶことができます。
このためのメソッドが setBoundaryModeS(), setBoundaryModeT() です。

メソッド (一部)

public final void setBoundaryModeS(int boundaryModeS) // S座標方向の境界形式
public final void setBoundaryModeT(int boundaryModeT) // T座標方向の境界形式

定数フィールド (一部)

public static final int CLAMP // テクスチャーを[0, 1]の範囲にクランプする
public static final int WRAP  // テクスチャーを繰り返す

S座標方向、T座標方向それぞれに対して、テクスチャーの適用を CLAMP または WRAP に指定することができます。デフォルトは WRAP です。

テクスチャー画像のピクセル(テクセル)が描画時のピクセルと一致しないとき、最も近いテクセルをそのまま適用するか、周囲の複数のテクセルから補間するかを選ぶことができます。このためのメソッドが setMagFilter(), setMinFilter() です。

メソッド (一部)

public final void setMagFilter(int magFilter) // 拡大フィルターを設定
public final void setMinFilter(int minFilter) // 縮小フィルターを設定

定数フィールド (一部)

public static final int FASTEST            // 可能な限り高速なフィルターを使用
public static final int NICEST             // 可能な限り高品質なフィルターを使用
public static final int BASE_LEVEL_POINT   // 基本レベルのテクスチャーの最も近いテクセルを適用
public static final int BASE_LEVEL_LINEAR  // 基本レベルのテクスチャーの最も近い4テクセルを補間して適用
public static final int MULTI_LEVER_POINT  // 最も近い MipMap での最も近いテクセルを適用
public static final int MULTI_LEVEL_LINEAR // 最も近い2つの MipMap でのそれぞれの4テクセルを補間して適用

setMagFilter() は拡大フィルターを、setMinFilter() は縮小フィルターを設定します。

デフォルト値は BASE_LEVEL_POINT です。ピクセルに最も近い、基本レベルテクスチャーのテクセルが適用されます。

複数の MipMapレベルを指定してある場合、MULTI_LEVEL_POINT を指定すると最も近いレベルのひとつの MipMap が選択され、その最も近いテクセルが適用されます。

BASE_LEVEL_LINEAR を指定すると、ピクセルに最も近い4つのテクセルを加重平均したものが適用されます。

複数の MipMapレベルを指定してある場合、MULTI_LEVEL_LINEAR を指定すると最も近いレベルの2つの MipMap が選択され、最も近いそれぞれ4つのテクセルを加重平均したものが適用されます。

javax.media.j3d.ImageComponent

javax.media.j3d.ImageComponent はテクスチャーマッピングのための画像を保持するためのクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.ImageComponent

クラス宣言

public abstract class ImageComponent
extends NodeComponent

コンストラクター (一部)

public ImageComponent(int format, // 画像フォーマット
                      int width,  // 幅
                      int height) // 高さ

ImageComponent は抽象クラスであり、実際にはサブクラスの ImageComponent2D, ImageComponent3D を使用します。

format には画像フォーマットを指定します。画像フォーマットには次のものがあります。

定数フィールド (一部)

public static final int FORMAT_RGB         // 各8ビットの赤、緑、青
public static final int FORMAT_RGBA        // 各8ビットの赤、緑、青、不透明度
public static final int FORMAT_RGB8        // 各8ビットの赤、緑、青          
public static final int FORMAT_RGBA8       // 各8ビットの赤、緑、青、不透明度
public static final int FORMAT_RGB5        // 各5ビットの赤、緑、青
public static final int FORMAT_RGB5_A1     // 各5ビットの赤、緑、青と、1ビットの不透明度
public static final int FORMAT_RGB4        // 各4ビットの赤、緑、青
public static final int FORMAT_RGBA4       // 各4ビットの赤、緑、青、不透明度
public static final int FORMAT_LUM4_ALPHA4 // 4ビットの光度と4ビットの不透明度
public static final int FORMAT_LUM8_ALPHA8 // 8ビットの光度と8ビットの不透明度
public static final int FORMAT_R3_G3_B2    // 各3ビットの赤、緑と2ビットの青
public static final int FORMAT_CHANNEL8    // 8ビットの 光度または不透明度または輝度(どれか一種類)

これらの画像フォーマットは、Texture での画像フォーマット指定に対応いています。

javax.media.j3d.Texture の定数フィールド (一部)

public static final int INTENSITY          // 輝度のみ
public static final int LUMINANCE          // 光度のみ
public static final int ALPHA              // 不透明度(アルファ値)のみ
public static final int LUMINANCE_ALPHA    // 光度と不透明度(アルファ値)
public static final int RGB                // 赤、緑、青の色
public static final int RGBA               // 赤、緑、青、不透明度(アルファ値)

javax.media.j3d.Texture2D

javax.media.j3d.Texture2D は2次元テクスチャーのためのクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.Texture
                    |
                    +--javax.media.j3d.Texture2D

クラス宣言

public class Texture2D
extends Texture

コンストラクター

public Texture2D()

public Texture2D(int mipmapMode, // MipMap形式
                 int format,     // 画像フォーマット
                 int width,      // 幅
                 int height)     // 高さ

Texture から特に拡張された点はありません。2次元テクスチャーを使用する場合は Texture2D を使うことになります。

javax.media.j3d.ImageComponent2D

javax.media.j3d.ImageComponent2D は2次元テクスチャーのための画像を保持します。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.ImageComponent
                    |
                    +--javax.media.j3d.ImageComponent2D

クラス宣言

public class ImageComponent2D
extends ImageComponent

コンストラクター (一部)

public ImageComponent2D(int format, // 画像フォーマット
                        int width,  // 幅
                        int height) // 高さ

public ImageComponent2D(int format,                         // 画像フォーマット
                        java.awt.image.BufferedImage image) // テクスチャー画像の BufferedImage

ImageComponent2D では ImageComponent で宣言されているコンストラクターに加えて、java.awt.image.BufferedImage を引数として指定するコンストラクターが増えています。

ImageComponent2D には BufferedImage を設定するためのメソッドもあります。

メソッド (一部)

public final void set(java.awt.image.BufferedImage image) // テクスチャー画像の BufferedImage

ファイルから読み込んで BufferedImage に画像を保存したり、プログラム中で BufferedImage に描画するなどの方法でテクスチャーを作成します。

サンプルのソースはつぎの通りです。

TextureApplet.java
  1  // Java 3D Test Applet
  2  // TextureApplet.java
  3  //   Copyright (c) 1999 ENDO Yasuyuki
  4  //                      mailto:yasuyuki@javaopen.org
  5  //                      http://www.javaopen.org/j3dbook/index.html
  6  
  7  import java.applet.*;
  8  import java.awt.*;
  9  import java.awt.event.*;
 10  import java.awt.image.*;
 11  import java.text.*;
 12  import javax.media.j3d.*;
 13  import javax.vecmath.*;
 14  import com.sun.j3d.utils.applet.MainFrame;
 15  import com.sun.j3d.utils.universe.SimpleUniverse;
 16  import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
 17  import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
 18  import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
 19  
 20  public class TextureApplet extends Applet {
 21    private boolean isStandalone = false;....................(1)
 22  
 23    public TextureApplet() {.................................(2)
 24      this(false);
 25    }
 26    
 27    public TextureApplet(boolean isStandalone) {.............(3)
 28      this.isStandalone = isStandalone;
 29  
 30      this.setLayout(new BorderLayout());
 31      
 32    }
 33  
 34    public void init() {.....................................(4)
 35      GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
 36      Canvas3D canvas = new Canvas3D(config);
 37      this.add(canvas, BorderLayout.CENTER);
 38  
 39      SimpleUniverse universe = new SimpleUniverse(canvas);
 40      universe.getViewingPlatform().setNominalViewingTransform();
 41    
 42      BranchGroup scene = createSceneGraph();
 43  
 44      universe.addBranchGraph(scene);
 45    }
 46  
 47    private BranchGroup createSceneGraph() {
 48      BranchGroup root = new BranchGroup();
 49  
 50      Background bg = new Background(new Color3f(0.5f, 0.5f, 0.5f));
 51      bg.setApplicationBounds(new BoundingSphere(new Point3d(), 100.0));
 52      root.addChild(bg);
 53      
 54      TransformGroup trans = new TransformGroup();
 55      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
 56      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
 57  
 58      BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
 59      
 60      MouseRotate rotator = new MouseRotate(trans);
 61      rotator.setSchedulingBounds(bounds);
 62      root.addChild(rotator);
 63      
 64      MouseTranslate translator = new MouseTranslate(trans);
 65      translator.setSchedulingBounds(bounds);
 66      root.addChild(translator);
 67      
 68      MouseZoom zoomer = new MouseZoom(trans);
 69      zoomer.setSchedulingBounds(bounds);
 70      root.addChild(zoomer);
 71  
 72      Point3d[] vertices = new Point3d[4];
 73      vertices[0] = new Point3d(-0.4, -0.4, 0.0); // 左下  3+-----+2
 74      vertices[1] = new Point3d( 0.4, -0.4, 0.0); // 右下   |     |
 75      vertices[2] = new Point3d( 0.4,  0.4, 0.0); // 右上   |     |
 76      vertices[3] = new Point3d(-0.4,  0.4, 0.0); // 左上  0+-----+1
 77  
 78      Point2f[] txcoords = new Point2f[4];                      ┐
 79      txcoords[0] = new Point2f(0.0f, 0.0f); // 左下  3+-----+2  │
 80      txcoords[1] = new Point2f(1.0f, 0.0f); // 右下   |     |   ├(5)
 81      txcoords[2] = new Point2f(1.0f, 1.0f); // 右上   |     |   │
 82      txcoords[3] = new Point2f(0.0f, 1.0f); // 左上  0+-----+1  ┘
 83  
 84      QuadArray geom =
 85        new QuadArray( vertices.length,
 86                       GeometryArray.COORDINATES |
 87                       GeometryArray.TEXTURE_COORDINATE_2 );...(6)
 88      geom.setCapability(Geometry.ALLOW_INTERSECT);
 89      geom.setCoordinates(0, vertices);
 90      geom.setTextureCoordinates(0, txcoords);.................(7)
 91  
 92      Appearance ap = createAppearance();
 93  
 94      Shape3D grid = new Shape3D(geom, ap);
 95      grid.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
 96      trans.addChild(grid);
 97  
 98      root.addChild(trans);
 99      
100      return root;
101    }
102  
103    private Appearance createAppearance() {
104      Appearance app = new Appearance();
105  
106      Image image = null;......................................(9)
107      if (this.isStandalone) {
108        // アプリケーションとして実行されている
109        final Toolkit toolkit = Toolkit.getDefaultToolkit(); ┬(10)
110        image = toolkit.getImage("face.gif");                ┘
111      } else {
112        // アプレットとして実行されている
113        image = getImage(getCodeBase(), "face.gif");.........(11)
114      }
115      MediaTracker mt = new MediaTracker(this);                 ┐ 
116      mt.addImage(image, 0);                                    │
117      mt.checkAll(true);                                        ├(12)
118      try { mt.waitForID(0); }                                  │
           catch (InterruptedException e) { e.printStackTrace(); } ┘
119  
120      BufferedImage bimage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB);....(13)
121      int[] imagefield = ((DataBufferInt)bimage.getRaster().getDataBuffer()).getData();...(14)
122      PixelGrabber pg = new PixelGrabber(image, 0, 0, 128, 128, imagefield, 0, 128);......(15)
123      try { pg.grabPixels(); } catch (InterruptedException e) { e.printStackTrace(); }....(16)
124  
125      ImageComponent2D icompo2d =
126        new ImageComponent2D(ImageComponent.FORMAT_RGBA, bimage);.........................(17)
127  
128      Texture2D texture2d =
129        new Texture2D(Texture.BASE_LEVEL, Texture.RGBA, 128, 128);........................(18)
130      texture2d.setImage(0, icompo2d);....................................................(19)
131  
132      app.setTexture(texture2d);..........................................................(20)
133  
134      return app;
135    }
136  
137    public static void main(String[] args) {
138      TextureApplet applet = new TextureApplet(true); // isStandalone = true;.............(21)
139      Frame frame = new MainFrame(applet, 500, 500);
140    }
141  }

アプリケーションとしてもアプレットとしても動作するようにしました。(1)でuアプリケーションとして実行されているとき true、アプレットとして実行されているとき false の状態をとるフラグ isStandalone を宣言しています。

アプレットとして実行されるときに使われる引数無しのコンストラクター (2) の中では、boolean の引数をとるコンストラクター (3) を引数 false で実行しています。

boolean の引数をとるコンストラクター (3u) では、引数の値を isStandalone に代入し、レイアウトマネジャーに java.awt.BorderLayout を指定しているだけです。

画像を URL を使って読み込むために、 シーングラフの構築をコンストラクターから init() メソッドに移動しました(4)。 アプレットのコンストラクターでは URL からのファイル読み込みが出来ないためです。

この例ではひとつの四角形ポリゴン (QuadArray)に ひとつのテクスチャーを適用しています。 (5)でテクスチャー座標配列を生成/初期化しています。 テクスチャー座標はテクスチャーの左下を原点とし、 テクスチャーの右上を (1.0, 1.0) とする、 横軸が s、縦軸が t の座標系です。

TXCOORD_2.jpg

QuadArray にテクスチャー座標を設定したいので、 QuadArray のコンストラクターで GeometryArray.TEXTURE_COORDINATE_2 を指定しています(6)。 (7)で setTextureCoordinates() メソッドを使ってテクスチャー座標を設定しています。テクスチャー座標の数は、頂点座標の数と同じである必要があります。

この例は GeometryArray のサブクラスを使っています。GeometryArray のサブクラスでのテクスチャー座標の指定方法は次のようになります。

  1. 頂点座標の配列と、頂点数と同じ数のテクスチャー座標の配列を用意する。
    (2次元テクスチャー座標配列が double, float の配列の場合、配列要素数/が頂点数になる。3次元テクスチャーでは 配列要素数/が頂点数)
  2. GeometryArray(のサブクラス)の生成。
    テクスチャー座標指定オプション TEXTURE_COORDINATE_2 (2次元テクスチャー)または TEXTURE_COORDINAME_3 (3次元テクスチャー) を指定する。
  3. setCoordinates() で頂点配列を設定する。
  4. setTextureCoordinates() でテクスチャー座標配列を設定する。
  5. GometryArray をコンストラクター引数にして Shape3D を生成する。
  6. Shape3D をシーングラフに addChild() する。

IndexedGeometryArray の場合はつぎのような手順になります。

  1. 頂点座標の配列と、頂点数と同じ数のテクスチャー座標の配列を用意する。
  2. 頂点 index の配列を用意する。この配列はテクスチャー座標配列の index としても使用する
  3. IndexedGeometryArray(のサブクラス)の生成。
    テクスチャー座標指定オプション TEXTURE_COORDINATE_2 (2次元テクスチャー)または TEXTURE_COORDINAME_3 (3次元テクスチャー) を指定する。
  4. setCoordinates() で頂点配列を設定する。
  5. setCoordinateIndices() で頂点配列の index を設定する。
  6. setTextureCoordinates() でテクスチャー座標配列を設定する。
  7. setTextureCoordinateIndices() でテクスチャー座標配列の index を設定する。これは頂点 index と同じものを使用する。
  8. IndexedGometryArray をコンストラクター引数にして Shape3D を生成する。
  9. Shape3D をシーングラフに addChild() する。

画像の読み込みですが、アプリケーションとしてもアプレットとしても動作するように若干複雑なコーディングをしています。

(8)で画像のための java.awt.Image を宣言しています。

(9)の if 文で isStandalone フラグをみてアプリケーションとして実行されているのか、アプレットとして実行されているのかを判定しています。

アプリケーションとして実行されているときは、java.awt.Toolkit クラスの static メソッドである getDefaultToolkit() で現在の Toolkit オブジェクトを取得して、Toolkit#getImage() でファイル名を指定して画像をファイルから読み込んでいます(10)。

アプレットとして実行されているときは Applet#getImage() メソッドを使って、getCodeBase() で得た codeBase の URL にあるファイル名 face.gif から画像を読み込んでいます(11)。

(12)は java.awt.MediaTracker を使った画像読み込みの同期処理です。waitForID() で、完全に画像が読み込まれるまで待ち続けています。

(13)でテクスチャー画像を読み込ませるための java.awt.image.BufferedImage を生成しています。テクスチャー画像のサイズは 2n×2n ピクセルである必要があるので、用意した GIF ファイル face.gif のサイズは 128×128 ピクセルにしました。BufferedImage のコンストラクターでも 128×128 ピクセルを指定しています。また、用意した GIF ファイルは透明度を持っています。このため、imageType (画像形式)には TYPE_INT_ARGB を指定しています。

(14)で画像をコピーするために、int配列を宣言して、BufferedImage から得た画像データの配列で初期化しています。
まずgetRaster() メソッドで java.awt.image.Raster オブジェクトを取得し、Raster#getDataBuffer() メソッドで java.awt.image.DataBuffer を取得しています。
つぎに DataBufferjava.awt.image.DataBufferInt でキャストして、DataBufferInt#getData() メソッドで int配列を取得しています。

(15)で画像データをコピーするために java.awt.image.PixcelGrabber を生成しています。コンストラクター引数では最初にコピー元になる java.awt.image.Image を指定し、以下 画像の左上の x座標=0, y座標=0, 画像の幅=123, 高さ=128, コピー先の int 配列、最初のピクセルを格納する配列要素の index=0、画像の1列のピクセル数=128 を指定しています。

(16)でgrabPixcels() でピクセルのデータを int 型として取り出し、コピー先の配列の各要素に格納しています。

(17)でImageComponent2D を生成しています。コンストラクター引数には、画像フォーマット ImageComponent.FORMAT_RGBA と、画像を読み込んだ BufferedImage を与えています。なお、透明度は TransparencyAttributes を適用しないと反映されません。

(18)でTexture2D を生成しています。コンストラクター引数は、MipMap形式に基本レベルテクスチャー Texture.BASE_LEVELを、テクスチャーフォーマットとして Texture.RGBAを、テクスチャーの横幅として 128、高さとして 128 を指定しています。(19)でsetImage()メソッドを使ってテクスチャー画像を読み込んだBufferedImageTexture2Dにセットしています。

(20)で setTexture() メソッドを使って Texture2dAppearance に設定しています。

アプリケーションとして実行されたときに使われる main() メソッドでは、(21)でコンストラクター引数に true を渡して TextureApplet を生成しています。

サンプルの実行結果はつぎのようになります。

TextureApplet1.gif

ポリゴンの頂点に書かれているテクスチャー座標は説明のために記入したもので、画面には表示されません。マウスを使って回転/移動/ズームができるようになっています。

com.sun.j3d.utils.image.TextureLoader

ファイルや URL から画像を読み込んで Texture を生成するための便利なクラスが com.sun.j3d.utils.image パッケージに用意されています。TextureLoader クラスです。

クラス継承

java.lang.Object
  |
  +--com.sun.j3d.utils.image.TextureLoader

クラス宣言

public class TextureLoader
extends java.lang.Object

コンストラクター

public TextureLoader(java.awt.image.BufferedImage bImage) // テクスチャー画像の BufferedImage

public TextureLoader(java.awt.image.BufferedImage bImage, // テクスチャー画像の BufferedIMage
                     java.lang.String format)             // 画像フォーマット

BufferedImage を引数に取るコンストラクターでは ImageComponent2D と同様に、ファイルから読み込んだ画像を BufferedImage に保存したり、プログラムから BufferedImage に描画したりしてテクスチャーのための画像を BufferedImage に描画しておきます。

format には画像フォーマットを文字列で指定します。画像フォーマットにはつぎのものがあります。

"RGBA"        // 各8ビットの赤、緑、青、不透明度(アルファ値)
"RGBA4"       // 各4ビットの赤、緑、青、不透明度(アルファ値)
"RGB5_A1"     // 各5ビットの赤、緑、青と、1ビットの不透明度(アルファ値)
"RGB"         // 各8ビットの赤、緑、青
"RGB4"        // 各4ビットの赤、緑、青
"RGB5"        // 各5ビットの赤、緑、青
"R3_G3_B2"    // 各3ビットの赤、緑と、2ビットの青
"LUM8_ALPHA8" // 8ビットの光度と、8ビットの不透明度(アルファ値)
"LUM4_ALPHA4" // 4ビットの光度と、4ビットの不透明度(アルファ値)
"LUMINANCE"   // 8ビットの光度
"ALPHA"       // 8ビットの不透明度(アルファ値)

TextureImageComponent で指定する画像フォーマットに似ていますが、TextureLoader では文字列で指定します。

TextureLoader では、複数のレベルの MipMap を生成することも出来ます。

public TextureLoader(java.awt.image.BufferedImage bImage, // テクスチャー画像の BuffueredImage
                     int flags)                           // MipMap 生成フラグ

引数に flags を取るコンストラクターで GENERATE_MIPMAP を指定すると、TextureLoader は複数のレベルの MipMap を生成してくれます。

public static final int GENERATE_MIPMAP // 複数の MipMap を生成する

TextureLoader には java.awt.image.BufferedImage, java.awt.image.Image, java.net.URL などの様々なオブジェクトから Texture を生成するためのコンストラクターが数多く用意されています。java.lang.String でファイル名を指定するコンストラクターもあります。

public TextureLoader(java.awt.image.BufferedImage bImage, // テクスチャー画像の BufferedImage
                     java.lang.String format,             // 画像フォーマット
                     int flags)                           // MipMap 生成フラグ

public TextureLoader(java.awt.Image image,                // テクスチャー画像の Image
                     java.awt.Component observer)         // imageUpdate() の通知先

public TextureLoader(java.awt.Image image,                // テクスチャー画像の Image
                     java.lang.String format,             // 画像フォーマット
                     java.awt.Component observer)         // imageUpdate() の通知先   

public TextureLoader(java.awt.Image image,                // テクスチャー画像の Image 
                     int flags,                           // MipMap 生成フラグ         
                     java.awt.Component observer)         // imageUpdate() の通知先    

public TextureLoader(java.awt.Image image,                // テクスチャー画像の Image
                     java.lang.String format,             // 画像フォーマット
                     int flags,                           // MipMap 生成フラグ       
                     java.awt.Component observer)         // imageUpdate() の通知先  

public TextureLoader(java.lang.String fname,              // テクスチャー画像のファイル名
                     java.awt.Component observer)         // imageUpdate() の通知先      

public TextureLoader(java.lang.String fname,              // テクスチャー画像のファイル名
                     java.lang.String format,             // 画像フォーマット
                     java.awt.Component observer)         // imageUpdate() の通知先      

public TextureLoader(java.lang.String fname,              // テクスチャー画像のファイル名
                     int flags,                           // MipMap 生成フラグ
                     java.awt.Component observer)         // imageUpdate() の通知先

public TextureLoader(java.lang.String fname,              // テクスチャー画像のファイル名
                     java.lang.String format,             // 画像フォーマット
                     int flags,                           // MipMap 生成フラグ
                     java.awt.Component observer)         // imageUpdate() の通知先

public TextureLoader(java.net.URL url,                    // テクスチャー画像の URL
                     java.awt.Component observer)         // imageUpdate() の通知先

public TextureLoader(java.net.URL url,                    // テクスチャー画像の URL
                     java.lang.String format,             // 画像フォーマット
                     java.awt.Component observer)         // imageUpdate() の通知先

public TextureLoader(java.net.URL url,                    // テクスチャー画像の URL
                     int flags,                           // MipMap 生成フラグ
                     java.awt.Component observer)         // imageUpdate() の通知先

public TextureLoader(java.net.URL url,                    // テクスチャー画像の URL
                     java.lang.String format,             // 画像フォーマット
                     int flags,                           // MipMap 生成フラグ
                     java.awt.Component observer)         // imageUpdate() の通知先

ファイルなどから読み込んで TextureLoader を生成したら、getTexture() メソッドを使って Texture を取得できます。

メソッド (一部)

public Texture getTexture() // Texture を取得する

ImageComponent のところで、テクスチャー画像のサイズは 2n×2n ピクセルでなければならないと書きましたが、TextureLoader はこれ以外のサイズの画像も読み込むことができます。
TextureLoader に読み込まれた画像は 2n×2n ピクセルに拡大縮小されます

TextureLoader を使ったサンプルは次のようになります。

TextureAttributes.java (一部)
104    private Appearance createAppearance() {
105      Appearance app = new Appearance();
106  
107      Image image = null;

108      if (this.isStandalone) {                                   ┐
109        // アプリケーションとして実行されている                          │
110        final Toolkit toolkit = Toolkit.getDefaultToolkit();     │
111        image = toolkit.getImage("face.gif");                    │
112      } else {                                                   │
113        // アプレットとして実行されている                               │
114        image = getImage(getCodeBase(), "face.gif");             ├(1)
115      }                                                          │
116      MediaTracker mt = new MediaTracker(this);                  │
117      mt.addImage(image, 0);                                     │
118      mt.checkAll(true);                                         │
119      try { mt.waitForID(0); }                                   │
           catch (InterruptedException e) { e.printStackTrace(); }  ┘
120  
121      Texture2D texture2d =
           (Texture2D)new TextureLoader(image, this).getTexture();...(2)
122  
123      app.setTexture(texture2d);
124                                                                
125      return app;
126    }

(1)の Image の読み込みは前のサンプルと同じです。

Texture の生成は非常に簡単です。(2)で画像を読み込んだ ImageimageUpdate() の通知先となるこのアプレット自身 this をコンストラクター引数に指定して TxuterLoader を生成し、getTexture() メソッドで Texture オブジェクトを取得しています。

実行結果は前のサンプルと同じなので省略します。

■■透明テクスチャーと TransparencyAttributes

透明度があるテクスチャー画像でも、TransparencyAttributes を設定していないと透明度が反映されません。

TextureApplet.java (一部)
104    private Appearance createAppearance() {
105      Appearance app = new Appearance();
106  
107      Image image = null;
108      if (this.isStandalone) {
109        // アプリケーションとして実行されている
110        final Toolkit toolkit = Toolkit.getDefaultToolkit();
111        image = toolkit.getImage("face.gif");
112      } else {
113        // アプレットとして実行されている
114        image = getImage(getCodeBase(), "face.gif");
115      }
116      MediaTracker mt = new MediaTracker(this);
117      mt.addImage(image, 0);
118      mt.checkAll(true);
119      try { mt.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); }
120  
121      Texture2D texture2d = (Texture2D)new TextureLoader(image, this).getTexture();
122  
123      app.setTexture(texture2d);
124                                                                
125      app.setTransparencyAttributes(
126        new TransparencyAttributes(TransparencyAttributes.BLENDED, 0.0f));...(1)
127  
128      app.setPolygonAttributes(
129        new PolygonAttributes( PolygonAttributes.POLYGON_FILL,
130                               PolygonAttributes.CULL_NONE,
131                               0.0f, false) );
132  
133      return app;
134    }

(1)でsetTransparencyAttributes()を生成しています。透明度の描画モードは TransparencyAttributes.BLENDED (アルファブレンドで透明度を描画)、透明度は 0.0 (不透明) です。

サンプルの実行結果を見てください。

TextureApplet3.gif

テクスチャー画像の透明な部分では背景が透過しています。
TransparencyAttributesShape3D に設定した透明度は 0.0 で不透明ですが、テクスチャーの透明度に完全に置き換えられています。デフォルトでは物体の色、透明度は、テクスチャーの色、透明度によって完全に置き換えられてしまうようになっています。後で説明しますが、テクスチャーと元の色/透明度との混合モードは TextureAttributes を適用して変更することができます。

■■ポリゴンの裏面とテクスチャー (PolygonAttributesを設定してみる)

テクスチャーマップした Shape3DPolygonAttributes を設定するとどうなるでしょうか?

PolygonAttributesで裏面へのカリングを無し(CULL_NONE)に設定すると裏面にもテクスチャーが描画されます。

TextureApplet.java (一部)
128      app.setPolygonAttributes(
129        new PolygonAttributes( PolygonAttributes.POLYGON_FILL,
130                               PolygonAttributes.CULL_NONE,
131                               0.0f, false) );

ポリゴンをマウスで回転させてみてください。裏面にもテクスチャー描画されています。

■■テクスチャーの WRAP

ポリゴンの頂点に設定されたテクスチャー座標が 0.0〜1.0 の範囲を超えていた場合、デフォルトではテクスチャーは繰り返し適用されます。(WRAP される)

つぎのサンプルでは、ポリゴンの頂点に-2.0〜+2.0 の範囲のテクスチャー座標を設定しています。

TextureApplet4.gif

このサンプルのソースは次の通りです。

TextureApplet.java (一部)
 48    private BranchGroup createSceneGraph() {
 49      BranchGroup root = new BranchGroup();
 50  
 51      Background bg = new Background(new Color3f(0.5f, 0.5f, 0.5f));
 52      bg.setApplicationBounds(new BoundingSphere(new Point3d(), 100.0));
 53      root.addChild(bg);
 54      
 55      TransformGroup trans = new TransformGroup();
 56      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
 57      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
 58  
 59      BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
 60      
 61      MouseRotate rotator = new MouseRotate(trans);
 62      rotator.setSchedulingBounds(bounds);
 63      root.addChild(rotator);
 64      
 65      MouseTranslate translator = new MouseTranslate(trans);
 66      translator.setSchedulingBounds(bounds);
 67      root.addChild(translator);
 68      
 69      MouseZoom zoomer = new MouseZoom(trans);
 70      zoomer.setSchedulingBounds(bounds);
 71      root.addChild(zoomer);
 72  
 73      Point3d[] vertices = new Point3d[4];
 74      vertices[0] = new Point3d(-0.4, -0.4, 0.0); // 左下  3+-----+2
 75      vertices[1] = new Point3d( 0.4, -0.4, 0.0); // 右下   |     |
 76      vertices[2] = new Point3d( 0.4,  0.4, 0.0); // 右上   |     |
 77      vertices[3] = new Point3d(-0.4,  0.4, 0.0); // 左上  0+-----+1
 78  
 79      Point2f[] txcoords = new Point2f[4];                       ┐
 80      txcoords[0] = new Point2f(-2.0f, -2.0f); // 左下  3+-----+2 │
 81      txcoords[1] = new Point2f( 2.0f, -2.0f); // 右下   |     |  ├(1)
 82      txcoords[2] = new Point2f( 2.0f,  2.0f); // 右上   |     |  │
 83      txcoords[3] = new Point2f(-2.0f,  2.0f); // 左上  0+-----+1 ┘
 84  
 85      QuadArray geom =
 86        new QuadArray( vertices.length,
 87                       GeometryArray.COORDINATES |
 88                       GeometryArray.TEXTURE_COORDINATE_2);
 89      geom.setCapability(Geometry.ALLOW_INTERSECT);
 90      geom.setCoordinates(0, vertices);
 91      geom.setTextureCoordinates(0, txcoords);
 92  
 93      Appearance ap = createAppearance();
 94  
 95      Shape3D grid = new Shape3D(geom, ap);
 96      grid.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
 97      trans.addChild(grid);
 98  
 99      root.addChild(trans);
100      
101      return root;
102    }

(1)でテクスチャー座標配列を宣言/初期化しています。左下が (-2.0, -2.0)、右上が (2.0, 2.0) にしてあります。0.0〜1.0 の範囲を超える部分にはテクスチャーが繰り返し適用されることになります。

■■テクスチャーの CLAMP

0.0〜1.0 のテクスチャー座標の範囲だけにテクスチャーを適用するには、setMagFilter(), setMinFilter() メソッドを使ってS座標方向、T座標方向へのテクスチャー境界の形式を Texture.CLAMP に設定します。

TextureApplet5.gif

TextureApplet.java (一部)
104    private Appearance createAppearance() {
105      Appearance app = new Appearance();
106  
107      Image image = null;
108      if (this.isStandalone) {
109        // アプリケーションとして実行されている
110        final Toolkit toolkit = Toolkit.getDefaultToolkit();
111        image = toolkit.getImage("face.gif");
112      } else {
113        // アプレットとして実行されている
114        image = getImage(getCodeBase(), "face.gif");
115      }
116      MediaTracker mt = new MediaTracker(this);
117      mt.addImage(image, 0);
118      mt.checkAll(true);
119      try { mt.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); }
120  
121      Texture2D texture2d = (Texture2D)new TextureLoader(image, this).getTexture();
122      texture2d.setMagFilter(Texture.BASE_LEVEL_LINEAR); ┐
123      texture2d.setMinFilter(Texture.BASE_LEVEL_LINEAR); ├(1)
124      texture2d.setBoundaryModeS(Texture.CLAMP);         │
125      texture2d.setBoundaryModeT(Texture.CLAMP);         ┘
126      app.setTexture(texture2d);
127                                                                
128      return app;
129    }

(1)でsetMagFilter(), setMinFilter() を使って、拡大縮小フィルターを Texture.BASE_LEVEL_LIEAR に設定しています。この場合、0.0〜1.0 の範囲外での色はテクスチャーの外周のピクセルの色が加重平均されたものが適用されます。

拡大縮小フィルターを Texture.BASE_LEVEL_POINT に設定すると、テクスチャーの外周のピクセルの色が繰り返し使われます。

BASE_LEVEL_POINT.gif

javax.media.j3d.TexCoordGeneration

ポリゴンの頂点に明示的にテクスチャー座標を指定する方法のほかに、テクスチャー座標生成平面と呼ばれるものや物体の法線ベクトルを利用してテクスチャー座標を生成することができます。このためのクラスが javax.media.j3d.TexCoordGeneration です。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.TexCoordGeneration

クラス宣言

public class TexCoordGeneration
extends NodeComponent

コンストラクター

public TexCoordGeneration()

public TexCoordGeneration(int genMode,     // テクスチャー座標生成モード
                          int format)      // テクスチャー形式(2次元または3次元)

TexCoordGeneration のコンストラクターでは、引数 genMode にテクスチャー座標の生成モードを指定します。テクスチャー座標生成モードには次のものがあります。

public static final int OBJECT_LINEAR // オブジェクト座標に従ってテクスチャー座標を生成
public static final int EYE_LINEAR    // 視点座標に従ってテクスチャー座標を生成
public static final int SPHERE_MAP    // 球体反射マッピングを使用してテクスチャー座標を生成

genModeOBJECT_LINEAR を指定すると、テクスチャー座標はオブジェクト座標に従います。物体を回転/移動すると、テクスチャーは物体の表面に固定され回転/移動に従います。

OBJECT_LINEAR.gif

EYE_LINEAR を指定するとテクスチャーは視点座標に従います。テクスチャーは視点座標に固定され、物体に投影されたようになります。物体を回転/移動してもテクスチャーは視点に固定されたままです。物体はテクスチャーの中を回転/移動するように見えます。

EYE_LINEAR.gif

SPHERE_MAP は鏡や金属の表面のように、周囲のものを反射する物体の描画に使用します。これを球体反射マッピングと呼びます。

このとき魚眼レンズで撮影した写真のようなテクスチャーが必要になります。テクスチャーは物体の周囲の球体の内側に適用されているような状態になり、物体の表面には周囲のテクスチャーを反射しているような画像が適用されます。

SPHERE_MAP.gif

Java 3D ではレイトレーシングのような光線の反射計算は行われません。このため鏡や金属の表面のようなものを描画する機能はありませんが、球体反射マッピングで鏡や金属の表面のような効果が得られます。

コンストラクター引数の format には、2次元テクスチャーか3次元テクスチャーかの別を指定します。

public static final int TEXTURE_COORDINATE_2 // 2次元テクスチャー
public static final int TEXTURE_COORDINATE_3 // 3次元テクスチャー

テクスチャー座標は、テクスチャー座標生成平面というものを指定して生成します。2次元テクスチャーでは S平面、T平面を、3次元テクスチャーでは S, T, R の3平面を指定します。

public TexCoordGeneration(int genMode,     // テクスチャー座標生成モード              
                          int format,      // テクスチャー形式(2次元または3次元)
                          Vector4f planeS) // S座標の平面方程式

public TexCoordGeneration(int genMode,     // テクスチャー座標生成モード              
                          int format,      // テクスチャー形式(2次元または3次元)
                          Vector4f planeS, // S座標の平面方程式
                          Vector4f planeT) // T座標の平面方程式

public TexCoordGeneration(int genMode,     // テクスチャー座標生成モード              
                          int format,      // テクスチャー形式(2次元または3次元)
                          Vector4f planeS, // S座標の平面方程式
                          Vector4f planeT, // T座標の平面方程式
                          Vector4f planeR) // R座標の平面方程式

テクスチャー座標生成平面は平面方程式を使って指定します。平面方程式は面法線ベクトルを使って表現します。

平面方程式のデフォルト値は次の通りです。

plane S : (1,0,0,0)
plane T : (0,1,0,0)
plane R : (0,0,0,0)

デフォルト値の S平面は X=0 の YZ平面、デフォルト値の T平面は Y=0 の XZ平面になるようです。

テクスチャー座標は、テクスチャー座標生成平面からポリゴンの頂点までの距離によって決定されます。

genModeOBJECT_LINEAR を指定したとき、テクスチャー座標生成平面はオブジェクト座標に従います。テクスチャー座標生成平面はオブジェクト座標に固定されたようになり、物体を回転/移動するとテクスチャー座標生成平面も回転/移動します。

genModeEYE_LINEAR を指定すると、テクスチャー座標生成平面は視点座標に従います。物体を回転/移動してもテクスチャー座標生成平面は回転/移動しません。

genModeSPHERE_MAP を指定したとき、テクスチャー座標の計算にはテクスチャー座標生成平面を使用しません。テクスチャー座標は物体の法線ベクトルを使って計算します。このとき物体の Geometry には頂点法線ベクトルの設定が必要です

テクスチャーは半球として扱われます。法線が各頂点からテクスチャー球体に投影され、その点の色を使って物体が描画されます。

テクスチャーが半球として扱われるので、魚眼レンズで撮影したようなテクスチャー画像が必要になります。

fisheye.jpg

TexCoordGeneration の使用方法は次の通りです。

  1. TexCoordGeneration を生成する
  2. Appearance#setTexCoordGeneration() メソッドで Appearance に設定する
  3. Appearance を物体 (Shape3D など) に設定する

■■OBJECT_LINEAR のサンプル

OBJECT_LINEAR のサンプルのソースはつぎのようになっています。

TexGenTest.java (一部)
217    private Appearance createAppearance() {
218      Appearance app = new Appearance();
219  
220      Image image = null;
221      if (this.isStandalone) {
222        // アプリケーションとして実行されている
223        final Toolkit toolkit = Toolkit.getDefaultToolkit();
224        image = toolkit.getImage("face.gif");
225      } else {
226        // アプレットとして実行されている
227        image = getImage(getCodeBase(), "face.gif");
228      }
229      MediaTracker mt = new MediaTracker(this);
230      mt.addImage(image, 0);
231      mt.checkAll(true);
232      try { mt.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); }
233  
234      Texture2D texture2d = (Texture2D)new TextureLoader(image, this).getTexture();
235  
236      app.setTexture(texture2d);
237  
238      texgen =                                                                           ┐
239        new TexCoordGeneration( TexCoordGeneration.OBJECT_LINEAR,....................(2) │
240                                TexCoordGeneration.TEXTURE_COORDINATE_2,.............(3) ├(1)
241                                new Vector4f(1.0f, 0.0f, 0.0f, 0.0f),   // PlaneS ...(4) │
242                                new Vector4f(0.0f, 1.0f, 0.0f, 0.0f) ); // PlaneT ...(5) │
243      app.setTexCoordGeneration(texgen);                                                 ┘
244                                                                
245      return app;
246    }

テクスチャーを適用するのは、(0.0, 0.0, 0.0)〜(1.0, 1.0, -1.0) の大きさの立方体です。立方体の辺、テクスチャー生成平面に置く平面ポリゴン、座標軸となる LineArray を描画しています。

(1)で TexCoordGeneration を生成しています。

(2)でテクスチャー座標生成モードとして TexCoordGeneration.OBJECT_LINEAR を指定しています。テクスチャー座標生成平面はオブジェクト座標に従います。

(3)で2次元テクスチャー座標を生成させるために TexCoordGeneration.TEXTURE_COORDINATE_2 を指定しています。

(4)でS座標のテクスチャー生成平面方程式 (1.0, 0.0, 0.0, 0.0) を指定しています。ここでは X=0 の YZ平面を定義しています。

(5)でT座標のテクスチャー生成平面方程式 (0.0, 1.0, 0.0, 0.0)を指定しています。ここでは Y=0 の XZ平面を定義しています。

サンプルの実行結果を見てください。

OBJECT_LINEAR.gif

S座標方向のテクスチャー座標は、S座標のテクスチャー座標生成平面から、ポリゴンの頂点までの距離になります。サンプルではS座標方向のテクスチャー座標生成平面の位置 (X=0のYZ平面) に透明な赤の平面ポリゴンを置いてあります。このサンプルでは最も近い頂点までの距離が 0.0、最も遠い頂点までの距離が 1.0 になっています。

T座標方向のテクスチャー座標は、T座標のテクスチャー座標生成平面から、ポリゴンの頂点までの距離になります。サンプルではT座標方向のテクスチャー座標生成平面の位置 (Y=0のXZ平面) に透明な緑の平面ポリゴンを置いてあります。このサンプルでは S座標の場合と同様に、最も近い平面までの距離が 0.0、最も遠い頂点までの距離が 1.0 になっています。

マウスで物体を回転させてみてください。テクスチャーは物体の座標系 (オブジェクト座標) に従います。

■■EYE_LINEAR のサンプル

テクスチャー座標生成モードを EYE_LINEAR に設定すると、テクスチャー座標生成平面は常に視点座標に従います。

サンプルのソースは次のようになっています。

TexGenTest.java (一部)
176    private Appearance createAppearance() {
177      Appearance app = new Appearance();
178  
179      Image image = null;
180      if (this.isStandalone) {
181        // アプリケーションとして実行されている
182        final Toolkit toolkit = Toolkit.getDefaultToolkit();
183        image = toolkit.getImage("face.gif");
184      } else {
185        // アプレットとして実行されている
186        image = getImage(getCodeBase(), "face.gif");
187      }
188      MediaTracker mt = new MediaTracker(this);
189      mt.addImage(image, 0);
190      mt.checkAll(true);
191      try { mt.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); }
192  
193      Texture2D texture2d = (Texture2D)new TextureLoader(image, this).getTexture();
194  
195      app.setTexture(texture2d);
196  
197      TexCoordGeneration texgen =                                                 ┐
198        new TexCoordGeneration( TexCoordGeneration.EYE_LINEAR, ...............(2) │
199                                TexCoordGeneration.TEXTURE_COORDINATE_2,          ├(1)
200                                new Vector4f(1.0f, 0.0f, 0.0f, 0.0f),   // PlaneS │
201                                new Vector4f(0.0f, 1.0f, 0.0f, 0.0f) ); // PlaneT │
202      app.setTexCoordGeneration(texgen);                                          ┘
203  
204      return app;
205    }

テクスチャーを適用する立方体は前のサンプルと同じです。

テクスチャー座標生成平面は視点座標に固定されているので、テクスチャー座標生成平面の位置を示す平面ポリゴンは省略しました。

Z座標の座標軸は描画しても見えないので省略しました。マウスで立方体を回転させても、XY座標の座標軸は回転しないようにしてあります。

(1)で TexCoordGeneration を生成しています。前のサンプルとの違いは、(2)のテクスチャー座標生成モードが EYE_LINEAR になっている点だけです。

サンプルの実行結果を見てください。

EYE_LINEAR.gif

マウスの右ボタンドラッグで物体を移動させると、物体はテクスチャーの中を移動するように見えます。

■■SHPERE_MAP のサンプル

テクスチャー座標生成モードに SPHERE_MAP を指定すると球体反射マッピングが適用されます。

サンプルソースは次の通りです。

TexGenTest.java
 53    private BranchGroup createSceneGraph() {
 54      BranchGroup root = new BranchGroup();
 55      root.setCapability(BranchGroup.ALLOW_DETACH);
 56      
 57      Bounds bounds = new BoundingSphere(new Point3d(), 100.0);
 58      
 59      Light light = new DirectionalLight();
 60      light.setInfluencingBounds(bounds);
 61      root.addChild(light);
 62      
 63      Background bg = new Background(new Color3f(0.5f, 0.5f, 0.5f));
 64      bg.setApplicationBounds(bounds);
 65      root.addChild(bg);
 66      
 67      TransformGroup trans = new TransformGroup();
 68      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
 69      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
 70      
 71      MouseRotate rotator = new MouseRotate(trans);
 72      rotator.setSchedulingBounds(bounds);
 73      root.addChild(rotator);
 74      
 75      MouseTranslate translator = new MouseTranslate(trans);
 76      translator.setSchedulingBounds(bounds);
 77      root.addChild(translator);
 78      
 79      MouseZoom zoomer = new MouseZoom(trans); 
 80      zoomer.setSchedulingBounds(bounds);
 81      root.addChild(zoomer);
 82      
 83      Appearance ap = createAppearance();
 84      
 85      Cone cone = new Cone( 0.5f, 1.0f, Primitive.GENERATE_NORMALS, ap );...(1)
 86      trans.addChild(cone);
 87      
 88      root.addChild(trans);
 89      
 90      return root;
 91    }
 92  
 93    private Appearance createAppearance() {
 94      Appearance app = new Appearance();
 95  
 96      Image image = null;
 97      if (this.isStandalone) {
 98        // アプリケーションとして実行されている
 99        final Toolkit toolkit = Toolkit.getDefaultToolkit();
100        image = toolkit.getImage("fisheye.jpg");
101      } else {
102        // アプレットとして実行されている
103        image = getImage(getCodeBase(), "fisheye.jpg");
104      }
105      MediaTracker mt = new MediaTracker(this);
106      mt.addImage(image, 0);
107      mt.checkAll(true);
108      try { mt.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); }
109  
110      Texture2D texture2d = (Texture2D)new TextureLoader(image, this).getTexture();
111  
112      app.setTexture(texture2d);
113  
114      TexCoordGeneration texgen =                                            ┐
115        new TexCoordGeneration( TexCoordGeneration.SPHERE_MAP,...........(3) ├(2)
116                                TexCoordGeneration.TEXTURE_COORDINATE_2 );   │
117      app.setTexCoordGeneration(texgen);                                     ┘
118  
119      return app;
120    }

テクスチャー座標は法線ベクトルを使って計算されるため、(1)で Primitive.GENERATE_NORMALS を指定して法線ベクトルを生成しています。

(2)で TexCoordGeneration を生成しています。テクスチャー座標は頂点法線を使って計算されるのでテクスチャー座標生成平面の平面方程式は設定していません。

(3)でテクスチャー座標生成モードに SPHERE_MAP を指定しています。

サンプルの実行結果は次のようになります。

SPHERE_MAP.gif

物体の表面には投影されたテクスチャーで、鏡や金属の表面のように周囲を反射しているように見えます。マウスで物体を回転させてみてください。物体の位置に応じて周囲を反射しているようにテクスチャーが適用されます。

javax.media.j3d.TetxureAttributes

javax.media.j3d.TetxureAttributes クラスを使うと、テクスチャーが持つ色や透明度と物体に適用されている Material や透明度とを様々なモードで混合させることができます。また、テクスチャーを回転させたり、遠近に応じた補正のモードを設定することができます。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.TextureAttributes

クラス継承

public class TextureAttributes
extends NodeComponent

コンストラクター

public TextureAttributes()

public TextureAttributes(int textureMode,           // テクスチャーモード
                         Transform3D transform,     // テクスチャー座標の座標軸
                         Color4f textureBlendColor, // テクスチャー混合色
                         int perspCorrectionMode)   // 遠近法補正モード

textureMode には次の定数値を指定できます。

定数フィールド

public static final int MODULATE // オブジェクトの色をテクスチャーの色で調整する
public static final int DECAL    // テクスチャーの色をデカール表面色としてオブジェクトに適用する
public static final int BLEND    // テクスチャーの色をオブジェクトの色と混合する
public static final int REPLACE  // オブジェクトの色をテクスチャーの色に置き換える

MODULATE.gif

textureModeMODELATE を指定するとテクスチャーの色が物体の色に混合されます。

物体の Material に設定された specularColor (鏡面反射による色) は、テクスチャーの影響を受けずに最後に適用されます。

DECAL.gif

DECAL を指定すると物体の表面にテクスチャーが張り付けられ、テクスチャーが適用されない部分には元の物体の色が残されます。例えば背景が透明なテクスチャーを使用すれば、透明な部分にはもとの物体の色が残ります。テクスチャーが不透明な部分では物体の色はテクスチャーの色で完全に置き換えられます。テクスチャーが不透明な部分には specularColor の影響もありません。

BLEND.gif

BLEND を指定すると、テクスチャーはまず textureBlendColor と混合されます。つぎに MODULATE と同様に物体の表面色と混合されます。

REPLACE.gif

REPLATE を指定すると、物体の表面色はテクスチャーの色で完全に置き換わります。

transform に指定した Transform3D に回転/移動/拡大縮小を適用すると、テクスチャーを回転/移動/拡大縮小させることができます。

textureTransform1.gif

textureTransform2.gif

perspCorrectionMode には遠近法補正モードを指定します。遠近法補正モードはつぎの2つです。

public static final int FASTEST  // 可能な限り高速に遠近法補正を行う
public static final int NICEST   // 可能な限り高品質に遠近法補正を行う

TextureAttributes の使用方法は次の通りです。

  1. TextureAttributes を生成する
  2. Appearance#setTextureAttributes() メソッドで Appearance に設定する
  3. Appearance を物体 (Shape3D など) に設定する

サンプルの実行結果は次のようになります。

TextureAttributesTest.gif

テクスチャーを適用する球体と、テクスチャー座標生成平面の位置の平面ポリゴン、オブジェクト座標の座標軸を表す LineArray を描画しています。

球体はマウスで回転/移動/ズームすることができます。

ウインドウ上部左の Choice でテクスチャー混合モードを MODULATE, DECAL, BLEND, REPLACE に切替えることができます。ウインドウ上部右の Choice で遠近補正モードを FASTEST, NICEST に切替えられます。

ウインドウ上部の TuplePanel で RGBA を入力してテクスチャー混合色を変更できます。このサンプルではテクスチャー混合色のデフォルト値を不透明な赤に設定しています。

ウインドウ上部下段の TextField にZ軸を中心とした回転角を入力すると、テクスチャー座標を回転させることができます。回転角に入力した数値にπを乗じた値が回転角に設定されます。

画面下部の TuplePanel で RGBA を入力して diffuseColor を変更することができます。このサンプルではデフォルトの diffuseColor を青に設定しています。

画面下部下段の TextField に透明度を入力して透明度を変更することができます。その右の Choice で透明度の描画モードを変更することができます。

テクスチャーの混合モードと、物体の色、透明度をいろいろ変化させて、どのように描画されるか確かめてください。

サンプルのソースは次の通りです。

TextureAttributesTest.java (一部)
  1  // Java 3D Test Applet
  2  // TextureAttributesTest.java
  3  //   Copyright (c) 1999 ENDO Yasuyuki
  4  //                      mailto:yasuyuki@javaopen.org
  5  //                      http://www.javaopen.org/j3dbook/index.html
  6  
  7  import java.applet.*;
  8  import java.awt.*;
  9  import java.awt.event.*;
 10  import javax.media.j3d.*;
 11  import javax.vecmath.*;
 12  import com.sun.j3d.utils.applet.MainFrame;
 13  import com.sun.j3d.utils.universe.SimpleUniverse;
 14  import com.sun.j3d.utils.geometry.Primitive;
 15  import com.sun.j3d.utils.geometry.Sphere;
 16  import com.sun.j3d.utils.image.TextureLoader;
 17  import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
 18  import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
 19  import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
 20  
 21  public class TextureAttributesTest extends Applet {
 22    private Canvas3D canvas = null;
 23    private Texture texture = null;
 24    private TextureAttributes txattr = null;
 25    private Color4f blendcolor = new Color4f(1.0f, 0.0f, 0.0f, 1.0f); // 赤 ...(1)
 26    private Transform3D txtrans = new Transform3D();
 27    private Material mat = null;
 28    private Color3f diffusecolor = new Color3f(0.0f, 0.0f, 1.0f);     // 青
 29    private TransparencyAttributes tattr = null;
 30    private TexCoordGeneration texgen = null;
         :
         :
299    private Appearance createAppearance() {
300      Appearance app = new Appearance();
301  
302      // Texture の生成
303      Image image = null;
304      if (this.isStandalone) {
305        // アプリケーションとして実行されている
306        Toolkit toolkit = Toolkit.getDefaultToolkit();
307        image = toolkit.getImage("face.gif");
308      } else {
309        // アプレットとして実行されている
310        image = getImage(getCodeBase(), "face.gif");
311      }
312      MediaTracker mt = new MediaTracker(this);
313      mt.addImage(image, 0);
314      mt.checkAll(true);
315      try { mt.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); }
316      texture = new TextureLoader(image, this).getTexture();
317      texture.setBoundaryModeS(Texture.CLAMP);
318      texture.setBoundaryModeT(Texture.CLAMP);
319      app.setTexture(texture);
320      
321      txattr = new TextureAttributes();.....................................(2)
322      txattr.setCapability(TextureAttributes.ALLOW_BLEND_COLOR_READ);  ┐
323      txattr.setCapability(TextureAttributes.ALLOW_BLEND_COLOR_WRITE); │
324      txattr.setCapability(TextureAttributes.ALLOW_MODE_READ);         ├(3)
325      txattr.setCapability(TextureAttributes.ALLOW_MODE_WRITE);        │
326      txattr.setCapability(TextureAttributes.ALLOW_TRANSFORM_READ);    │
327      txattr.setCapability(TextureAttributes.ALLOW_TRANSFORM_WRITE);   ┘
328      txattr.setTextureBlendColor(blendcolor); // BLEND にしないと反映されない ...(4)
329      txattr.setTextureTransform(txtrans);...................................(5)
330      app.setTextureAttributes(txattr);......................................(6)
331  
332      mat = new Material();
333      mat.setCapability(Material.ALLOW_COMPONENT_READ);
334      mat.setCapability(Material.ALLOW_COMPONENT_WRITE);
335      mat.setDiffuseColor(diffusecolor);
336      app.setMaterial(mat);
337  
338      tattr = new TransparencyAttributes();
339      tattr.setCapability(TransparencyAttributes.ALLOW_VALUE_READ);
340      tattr.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
341      tattr.setCapability(TransparencyAttributes.ALLOW_MODE_READ);
342      tattr.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE);
343      app.setTransparencyAttributes(tattr);
344  
345      texgen =
346        new TexCoordGeneration( TexCoordGeneration.OBJECT_LINEAR,
347                                TexCoordGeneration.TEXTURE_COORDINATE_2,
348                                new Vector4f(1.0f, 0.0f, 0.0f, 0.0f),
349                                new Vector4f(0.0f, 1.0f, 0.0f, 0.0f) );
350      app.setTexCoordGeneration(texgen);
351      
352      return app;
353    }

(2)で TextureAttiributes を生成しています。 実行時にテクスチャー混合モード、テクスチャー混合色、 テクスチャー座標軸を変更したいので、 (3)でそれぞれの capability bit をセットしています。

(4)でテクスチャー混合色を設定しています。テクスチャー混合色は (1)で不透明な赤色として宣言した Color4f オブジェクトでです。

(5)ではテクスチャーの座標軸となる Transform3D を設定しています。

(6)で setTextureAttributes() メソッドを使って、TextureAttributesAppearance に設定しています。

javax.media.j3d.Texture3D

javax.media.j3d.Texture3D は3次元テクスチャーのためのクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.Texture
                    |
                    +--javax.media.j3d.Texture3D

クラス宣言

public class Texture3D
extends Texture

コンストラクター

public Texture3D()

public Texture3D(int mipmapMode, // MipMap形式
                 int format,     // 画像フォーマット
                 int width,      // 幅
                 int height,     // 高さ
                 int depth)      // 奥行き

コンストラクターでは、幅、高さに加えて3次元テクスチャーの奥行きを指定します。
mipMapMode, formatTexture クラスと同様です。

Texture3D には、ポリゴンの頂点に設定した R座標が 0.0〜1.0 の範囲を超えたときに、0.0〜1.0 の範囲だけにテクスチャーを適用するか、それともテクスチャーを繰り返し適用するかを設定するメソッドを持ちます。setBoundaryModeR() メソッドです。

メソッド (一部)

public final void setBoundaryModeR(int boundaryModeR) // R座標方向の境界形式

setBoundaryModeR() メソッドの引数は setBoundaryModeS(), setBoundaryModeT() と同様に CLAMP または WRAP です。
CLAMP を指定するとテクスチャーは 0.0〜1.0 の範囲だけに適用され、WRAP を指定するとテクスチャーは繰り返し適用されます。デフォルトは WRAP です。

なお、OpenGL 1.1 には3次元テクスチャーについての仕様が無いため、Win32 の OpenGL 1.1 実装にも3次元テクスチャーはありません。このため、Java 3D の Texture3D も Win32 では正常に動作しません

Solaris の OpenGL 1.1.1 や Linux の Mesa 3.0.2 では独自の拡張がされているため、3次元テクスチャーが使用できます。Java 3D もこれらとの組合せで Texture3D を動作させることができます。

javax.media.j3d.ImageComponent3D

javax.media.j3d.ImageComponent3D は3次元テクスチャーのための複数の画像を保持するクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.ImageComponent
                    |
                    +--javax.media.j3d.ImageComponent3D

クラス宣言

public class ImageComponent3D
extends ImageComponent

コンストラクター (一部)

public ImageComponent3D(int format,                            // 画像フォーマット
                        java.awt.image.BufferedImage[] images) // 奥行きに応じた BufferedImage配列

メソッド (一部)

public final void set(java.awt.image.BufferedImage[] images)  // 奥行きに応じた BufferedImage 配列

public final void set(int index,                              // 奥行きの index
                      java.awt.image.BufferedImage image)     // テクスチャー画像の BufferedImage

ImageComponent3D には BufferedImage の配列を設定します。BufferedImage の配列は先頭要素 (index = 0) が R座標のゼロになります。R座標は奥行き方向が正方向です。

サンプル・プログラムの実行例を見てください。

Texture3DTest.gif

このサンプルでは、com.sun.j3d.utils.behaviors.pickig パッケージを使って、マウスの左/ 中/右ボタンで回転/ズーム/移動ができるようになっています。

年輪のような3次元テクスチャーを作成し、年輪の色を深さに応じて変化させています。また、通常のテクスチャーは年輪を真上から見たような方向になるため、javax.media.j3d.TextureAttributes を使って X軸を回転軸にテクスチャーを 90°回転させています。ウインドウの下部の TextFieldx, y, z, 回転角 を入力して "RotTex" ボタンを押すと、テクスチャーを回転させることができます。

javax.media.j3d.TexCoordGeneration でテクスチャー座標を生成していますが、テクスチャー座標生成モードが EYE_LINEAR なので、物体を回転させても3次元テクスチャーは回転しません。テクスチャー座標の生成モードは、ウインドウ上部の ChoiceOBJECT_LINEAR, EYE_LINEAR に変更できます。

初期状態では ポリゴンの面が描画されています(POLYGON_FILL モード)。ウインドウ上部の Choice を使って、ポリゴンの辺の描画 (POLYGON_LINE)や、ポリゴンの頂点の描画 (POLYGON_POINT) に切替えることができます。

初期状態ではポリゴンの裏面は描画されません(CULL_BACK モード)。ウインドウ上部の Choice を使って、表面を描画しない(CULL_FRONT)、両方の面を描画する(CULL_NONE)、などのモードに切替えることができます。

条件をいろいろ変更して試してみてください。このサンプルのソースコードは次の通りです。

Texture3DTest.java (一部)
265    private Appearance createAppearance() {
266      Appearance app = new Appearance();
267  
268      BufferedImage[] bimages = new BufferedImage[64];...(1)
269  
270      float dh = 1.0f / 64.0f; ................................................(3) ┐
271      for (int i=0; i<64; i++) {                                                   │
272        bimages[i] = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);...(4) │
273        Graphics g = (Graphics)bimages[i].createGraphics();....................(5) │
274        g.fillRect(0, 0, 64, 64);                                                  │
275        float h = dh * (float)i;                                                   │
276        Color c = Color.getHSBColor(h, 1.0f, 1.0f);//年輪の色                        ├(2)
277        g.setColor(c);                                                             │
278        for (int j=0; j<8; j++) { .............................................(6) │
279          int x = j * 4;                                                           │
280          int w = 64 - x * 2;                                                      │
281          System.out.println("i=" + i + ", j=" + j + ", x=" + x + ", w=" + w + ", h" + h);//DEBUG
282          g.drawOval(x, x, w, w); .............................................(7) │
283        }                                                                          │
284      }                                                                            ┘
285  
286      ImageComponent3D icompo3d =
287        new ImageComponent3D(ImageComponent.FORMAT_RGBA, bimages); ....(8)
288  
289      Texture3D texture3d =
290        new Texture3D(Texture.BASE_LEVEL, Texture.RGBA, 64, 64, 64); ...(9)
291      texture3d.setImage(0, icompo3d);..................................(10)
292  
293      app.setTexture(texture3d);........................................(11)
294  
295      txattr = new TextureAttributes();.................................(12)
296      txattr.setCapability(TextureAttributes.ALLOW_TRANSFORM_WRITE);
297      txattr.setTextureMode(TextureAttributes.FASTEST);
298      Transform3D t3d = new Transform3D();..............................(13)
299      t3d.rotX(Math.PI / 2.0);..........................................(14)
300      txattr.setTextureTransform(t3d);..................................(15)
301      app.setTextureAttributes(txattr);
302      
303      texgen =
304        new TexCoordGeneration( TexCoordGeneration.EYE_LINEAR,
305                                TexCoordGeneration.TEXTURE_COORDINATE_3 ); ...(16)
306      texgen.setCapability(TexCoordGeneration.ALLOW_MODE_READ);
307      texgen.setPlaneS(new Vector4f(1.0f, 0.0f, 0.0f, 0.0f)); ┐
308      texgen.setPlaneT(new Vector4f(0.0f, 1.0f, 0.0f, 0.0f)); ├(17)
309      texgen.setPlaneR(new Vector4f(0.0f, 0.0f, 1.0f, 0.0f)); ┘
310      app.setTexCoordGeneration(texgen);......................................(18)
311  
312      pattr = new PolygonAttributes();........................................(19)
313      pattr.setCapability(PolygonAttributes.ALLOW_MODE_WRITE);
314      pattr.setCapability(PolygonAttributes.ALLOW_CULL_FACE_WRITE);
315      app.setPolygonAttributes(pattr);........................................(20)
316  
317      return app;
318    }

(1)で BufferedImage の配列を生成しています。今回は 64×64 ピクセルの BufferedImage を 64層使用することにします。テクスチャーの縦、横のサイズは 必ず2n のピクセル数である必要があります。

(2)で3次元テクスチャーの元になるグラフィックを BufferedImage に描画しています。色の生成は HSB で行っています。(3)で、ループごとの色相値の増加分を計算しています。(4)で 64×64 ピクセルの BufferedImage のインスタンスを生成し、(5)で Color#getHSBColor() メソッドを使って Color オブジェクトを生成しています。

各層ごとに 8つの同心円を描くため、(6)の for ループを使用しています。(7)で Graphics#drawOval() メソッドを使って円を描画しています。

(8)で javax.media.j3d.ImageComponent3D を生成しています。コンストラクターの引数として、64×64ピクセル×64層の BufferedImage を指定しています。

(9)で javax.media.j3d.Texture3D を生成しています。width, height, depth はすべて 64 です。

(10)でsetImage() メソッドを使って Texture3DImageComponent3D を設定しています。

(11)でsetTexture() メソッドを使って javax.media.j3d.AppearanceTexture3D を設定しています。

テクスチャーに回転を与えるため、TextureAttributes#setTextureTransform() メソッドを使用しています。

(12)で TextureAttributes オブジェクトを生成しています。(13)で javax.media.j3d.Transform3D を生成し、(14)で rotX() メソッドを使って X軸を回転中心に π/2 ラジアン (90°) 回転させています。回転を与えた Transform3D は、(15)で setTextureTransform() メソッドを使って TextureAttributes に設定されています。

(16)で javax.media.j3d.TexCoordGeneration を生成しています。テクスチャー座標は s, t, r の3次元座標です。

(17)で S, T, R 各座標の平面方程式を設定しています。

(18)で setTexCoordGeneration() メソッドを使って TexCoordGenerationAppearance に設定しています。

(19)でjavax.media.j3d.PolygonAttributes を生成し、(20)で setPolygonAttributes() メソッドを使って Appearance に設定しています。

最後に繰り返しますが、現時点では3次元テクスチャーは Win32 では動作しません

javax.media.j3d.Rastar

javax.media.j3d.Rastar は、画像や深度値をシーンに書き込んだり、描画されている色、深度値を読み取ったりするためのクラスです。

クラス継承

java.lang.Object
  |
  +--javax.media.j3d.SceneGraphObject
        |
        +--javax.media.j3d.NodeComponent
              |
              +--javax.media.j3d.Geometry
                    |
                    +--javax.media.j3d.Raster

クラス宣言

public class Raster
extends Geometry

コンストラクター

public Raster()

public Raster(Point3f pos,                   // 左上位置
              int type,                      // 形式
              int xOffset,                   // 画像のコピー開始オフセット X
              int yOffset,                   // 画像のコピー開始オフセット Y
              int width,                     // 幅 (ピクセル)
              int height,                    // 高さ (ピクセル)
              ImageComponent2D image,        // 画像が格納された ImageComponent2D
              DepthComponent depthComponent) // 深度値を設定するための DepthComponent

public Raster(Point3f pos,                   // 左上位置
              int type,                      // 形式
              java.awt.Point offset,         // 画像のコピー開始オフセット XY
              java.awt.Dimension size,       // 画像の大きさ (幅、高さ)
              ImageComponent2D image,        // 画像が格納された ImageComponent2D
              DepthComponent depthComponent) // 深度値を設定するための DepthComponent

定数フィールド (一部)

public static final int RASTER_COLOR       // Raster に色を含む
public static final int RASTER_DEPTH       // Raster に深度値を含む
public static final int RASTER_COLOR_DEPTH // Raster に色と深度値を含む

RasterGeometry のサブクラスです。画像などを設定した RasterShape3D などに設定して使用します。

コンストラクターでは、Raster の左上の位置座標、色/深度値/色と深度値 のどれか、画像のコピーを開始するオフセット (X,Y)、画像の大きさ (幅、高さ)、画像を格納してある ImageComponent2D、深度値を格納してある DepthComponent を指定します。

本書ではイミディエイト・モードには触れないので、Raster を使ったイメージバッファーの読み取りについては解説しません。

Raster の使い方は次のようになります。

  1. 画像を使いたいときは ImageComponent2D に画像を読み込む
  2. 深度値を使いたいときは DepthComponent に深度値を設定する
  3. ImageComponent2D, DepthComponent などをコンストラクター引数にして Rasterを生成する
  4. 生成した Raster をコンストラクターの引数にして Shape3D を 生成する
  5. Shape3D をシーングラフに addChild() する

サンプルの実行結果を見てください。

RasterTest.gif

マウスで回転/移動/ズームができるようになっています。回転/ズームはやっても効果ありません。回転しても Raster は常に視点方向を向いています。また、Z座標を変更しても大きさは変わりません。

元の画像には透明度が含まれているので、TransparencyAttributesBLENDED, FASTEST, NICEST を選ぶと Raster に透明度が適用されます。

TransparencyAttributes の透明度の値も変更できますが、変更しても変化ありません。常に元の画像の透明度が使用されます。

サンプルのソースは次のようになっています。

RasterTest.java
 89    private BranchGroup createSceneGraph() {
 90      BranchGroup root = new BranchGroup();
 91      
 92      TransformGroup trans = new TransformGroup();
 93      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
 94      trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
 95      trans.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
 96  
 97      BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
 98      
 99      MouseRotate rotator = new MouseRotate(trans);
100      rotator.setSchedulingBounds(bounds);
101      root.addChild(rotator);
102      
103      MouseTranslate translator = new MouseTranslate(trans);
104      translator.setSchedulingBounds(bounds);
105      root.addChild(translator);
106      
107      MouseZoom zoomer = new MouseZoom(trans);
108      zoomer.setSchedulingBounds(bounds);
109      root.addChild(zoomer);
110      
111      ImageComponent2D image = readImage();
112  
113      Raster raster = new Raster( new Point3f(0.0f, 0.0f, 0.0f),
114                                  Raster.RASTER_COLOR,
115                                  0, 0, 128, 128,
116                                  image, null );..................(1)
117      Appearance app = createAppearance();
118      Shape3D shape = new Shape3D(raster, app);...................(2)
119      shape.setCapability(Shape3D.ENABLE_PICK_REPORTING);
120      shape.setBounds(new BoundingSphere(new Point3d(), 0.5));
121      trans.addChild(shape);......................................(3)
122      
123      root.addChild(trans);
124      
125      return root;
126    }
127  
128    private ImageComponent2D readImage() {
129  
130      Image image = null;
131      if (this.isStandalone) {
132        // アプリケーションとして実行されている
133        Toolkit toolkit = Toolkit.getDefaultToolkit();
134        image = toolkit.getImage("face.gif");
135      } else {
136        // アプレットとして実行されている
137        image = getImage(getCodeBase(), "face.gif");
138      }
139  
140      MediaTracker mt = new MediaTracker(this);
141      mt.addImage(image, 0);
142      mt.checkAll(true);
143      try { mt.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); }
144  
145      TextureLoader tr = new TextureLoader(image, this);
146      
147      return tr.getImage();
148    }

(1)で Raster を生成しています。形式は RASTER_COLOR で色データのみ、オフセットは (0, 0)、サイズは元の画像と同じ (128, 128) にしました。画像を読み込んだ ImageComponent2Dを指定し、DepthComponent は使わないので null を指定しています。

(2)で Raster をコンストラクター引数に Shape3D を生成しています。この Shape3D を(3)で TransformGroupaddChild() しています。

ImageComponent2D への画像の読み込みは TextureLoader を使っています。読み込み後 TextureLoader#getImage()ImageComponent2D を取得しています。

Raster はポリゴンを生成する必要が無いので、テクスチャーマッピングよりも資源が少なくて済むようです。ただし、Z座標を変えても大きさが変わらない、回転させても常に同じ方向を向いている、などの特徴があります。

本書では説明しませんが、イミディエイト・モードで Canvas3D が描画したイメージを取得するときにも Raster を使用します。

ENDO Yasuyuki