■Appearanceの効果(透明度、点の大きさ、線の太さ、など)

javax.media.j3d.Material のところで少し触れましたが、javax.media.j3d.Appearance は物体の外見(見え方)に関する属税を定義するための重要なクラスです。

クラス継承

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

クラス宣言

public class Appearance
extends NodeComponent

コンストラクター

public Appearance()

三次元空間上の物体を定義する Shape3D は、2つのオブジェクトを参照します。

まず最低限必要なのは Geometry です。Geometry は、物体の幾何学的な属性(座標や形状)を定義します。

もうひとつは Appearance です。Appearance は物体の外見(見え方)に関する属性を定義します。

Appearance には、物体の外見についての属性を設定するための、数多くのメソッドがあります。

setMaterial(Material) メソッドはすでに説明しました。

以下では他のメソッドについて説明します。

■■点の属性 (javax.media.j3d.PointAttributes)

javax.media.j3d.PointAttributes は、点の大きさ、点のアンチエリアス処理についての属性を定義します。

PointAttributes は、setPoinAttributes() メソッドで Appearance に設定されます。

Appearance のメソッド

public final void setPointAttributes(PointAttributes pointAttributes)


クラス宣言

public class PointAttributes
extends NodeComponent

コンストラクター

public PointAttributes()

public PointAttributes(float pointSize,           // 点の大きさ(単位・ピクセル)
                       boolean pointAntialiasing) // アンチエリアス処理をするかどうか

Java 3D での点の大きさは、デフォルト値では 1ピクセルです。PointAttributes を使うと、点の大きさを変えることが可能です。点の大きさは float 値で設定します。単位はピクセルです。

アンチエリアス処理とは何でしょうか?

グラフィック表示の最小単位はピクセルです。ピクセルの大きさよりも小さい物体は表示できません。

例えば、画面上に描かれた線分を拡大すると次の図のようになります。

NonAntialiasing.gif

このような「ギザギザ」なものを、なめらかに見せる技法がアンチエリアスです。次の図を見てください。

Antialiasing.gif

ピクセルの大きさは変えられませんが、それぞれのピクセルに階調を持たせることで「ギザギザ」感を緩和しています。

アンチエリアス処理の有無は、デフォルトでは false (処理しない) です。

PointAttributes でアンチエリアスした点の描画サンプルを見てください。(Vine Linux Intel x86, JDK 1.2 pre-v2, Java 3D 1.1.1 pre-v1)

AppearanceTest1.gif

どの点も、大きさを 12.0f にしています。

画面左側の 3つの点はアンチエリアス処理をしていません。画面右側の 3つの点はアンチエリアス処理してあります。

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

AppearanceTest.java (一部)
30   private BranchGroup createSceneGraph() {
31     BranchGroup root = new BranchGroup();
32 
33     Point3d[] vleft = new Point3d[3];
34     
35     vleft[0] = new Point3d(-0.9, 0.0, 0.0);
36     vleft[1] = new Point3d(-0.6, -0.4, 0.0);
37     vleft[2] = new Point3d(-0.2, 0.3, 0.0);
38     
39     PointArray gleft = new PointArray(vleft.length, GeometryArray.COORDINATES); ┐
40     gleft.setCoordinates(0, vleft);                                             │
41                                                                                 │
42     PointAttributes pleft = new PointAttributes();...(2)                        │
43     pleft.setPointSize(12.0f);.......................(3)                        │
44                                                                                 ├(1)
45     Appearance aleft = new Appearance();.............(4)                        │
46     aleft.setPointAttributes(pleft);.................(5)                        │
47                                                                                 │  
48     Shape3D sleft = new Shape3D(gleft, aleft);.......(6)                        │
49                                                                                 │
50     root.addChild(sleft);............................(7)                        ┘
51 
52     Point3d[] vright = new Point3d[3];
53 
54     vright[0] = new Point3d(0.1, 0.0, 0.0);
55     vright[1] = new Point3d(0.3, -0.4, 0.0);
56     vright[2] = new Point3d(0.6, 0.1, 0.0);
57 
58     PointArray gright = new PointArray(vright.length, GeometryArray.COORDINATES); ┐
59     gright.setCoordinates(0, vright);                                             │
60                                                                                   │
61     PointAttributes pright = new PointAttributes();                               │
62     pright.setPointSize(12.0f);                                                   │
63     pright.setPointAntialiasingEnable(true);............(9)                       │
64                                                                                   │
65     Appearance aright = new Appearance();                                         ├(8)
66     aright.setPointAttributes(pright);                                            │
67                                                                                   │
68     Shape3D sright = new Shape3D(gright, aright);                                 │
69                                                                                   │
70     root.addChild(sright);                                                        ┘
71 
72     return root;
73   }

(1)で画面左の 3つの点を定義しています。

(2)で PointAttributes を生成し、(3)で点の大きさを 12.0f に設定しています。

(4)で Appearance を生成し、(5)で setPointAttributes() メソッドを使って PointAttributes を設定しています。

(6)で PointArray, Appearance をコンストラクターの引数に指定して Shape3D を生成し、(7)で BranchGroup に addChild() しています。

(8)で画面右の 3つの点を定義しています。

左側の 3つの点と違い、(9)で setPointAntialiasingEnable() メソッドを使って、アンチエリアス処理を有効にしています。

やっと、点がどこに描画されているのか分かるサンプルになりました。 これ以前のサンプルでは点の大きさが 1ピクセルしか無く、 何が描画されているのかよくわかりませんでした。

■■線の属性 (javax.media.j3d.LineAttributes)

javax.media.j3d.LineAttributes は、線分の太さ、実線、破線など線の種類、アンチエリアス処理の有無などを定義します。

LineAttributes は、setLineAttributes() メソッドで Appearance に設定されます。

Appearance のメソッド

public final void setLineAttributes(LineAttributes lineAttributes)

クラス継承

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

クラス宣言

public class LineAttributes
extends NodeComponent

コンストラクター

public LineAttributes()

public LineAttributes(float lineWidth,          // 線の太さ
                      int linePattern,          // 線の種類
                      boolean lineAntialiasing) // アンチエリアス処理をするかどうか

定数フィールド (一部)

public static final int PATTERN_SOLID    // 実線
public static final int PATTERN_DASH     // 破線
public static final int PATTERN_DOT      // 点線
public static final int PATTERN_DASH_DOT // 一点鎖線

線の太さは、PointAttributes の点の大きさと同様に、デフォルト値は 1ピクセルです。コンストラクターの float lineWidth または setLineWidth(float) メソッドで設定します。

線の種類は実線、破線、点線、一点鎖線です。それぞれ LineAttributes.PATERN_xxxx 定数フィールドで指定します。

アンチエリアス処理は、PointAttributes と同様に、デフォルト値では false です。

AppearanceTest2.gif

このサンプルでは java.awt.TextField, java.awt.Choice, java.awt.Checkbox を使って、各種設定値をいろいろ変化させてテストできるようにしてみました。

AppearanceTest.java
  1 // Java 3Dテスト用アプレット
  2 // AppearanceTest.java
  3 //   Copyright (c) 1999 ENDO Yasuyuki
  4 //                      mailto:yasuyuki@timedia.co.jp
  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 
 15 public class AppearanceTest extends Applet {
 16   Appearance ap = null;
 17 
 18   public AppearanceTest() {
 19     this.setLayout(new BorderLayout());
 20 
 21     Panel panel = new Panel();
 22     this.add(panel, BorderLayout.NORTH);
 23 
 24     panel.add(new Label("Width"));
 25     TextField tfield = new TextField("1.0", 8);
 26     tfield.addActionListener(new ActionListener() {
 27       public void actionPerformed(ActionEvent e) {
 28         float width = 1.0f;
 29         try {
 30           width = Float.parseFloat(e.getActionCommand());
 31           System.out.println(width);
 32           ap.getLineAttributes().setLineWidth(width);...(1)
 33         } catch (NumberFormatException ex) { }
 34       }
 35     });
 36     panel.add(tfield);
 37 
 38     panel.add(new Label("Pattern"));
 39     Choice choice = new Choice();
 40     choice.add("PATTERN_SOLID");
 41     choice.add("PATTERN_DASH");
 42     choice.add("PATTERN_DOT");
 43     choice.add("PATTERN_DASH_DOT");
 44     choice.addItemListener(new ItemListener() {
 45       public void itemStateChanged(ItemEvent e) {
 46         String item = (String)e.getItem();
 47         System.out.println(item);
 48         if (item.equals("PATTERN_SOLID")) {                                       ┐
 49           ap.getLineAttributes().setLinePattern(LineAttributes.PATTERN_SOLID);    │
 50         } else if (item.equals("PATTERN_DASH")) {                                 │
 51           ap.getLineAttributes().setLinePattern(LineAttributes.PATTERN_DASH);     │
 52         } else if (item.equals("PATTERN_DOT")) {                                  ├(2)
 53           ap.getLineAttributes().setLinePattern(LineAttributes.PATTERN_DOT);      │     
 54         } else if (item.equals("PATTERN_DASH_DOT")) {                             │
 55           ap.getLineAttributes().setLinePattern(LineAttributes.PATTERN_DASH_DOT); │
 56         }                                                                         ┘
 57       }
 58     });
 59     panel.add(choice);
 60     
 61     Checkbox check = new Checkbox("Antialias", false);
 62     check.addItemListener( new ItemListener() {
 63       public void itemStateChanged(ItemEvent e) {
 64         int state = e.getStateChange();
 65         if (state == ItemEvent.SELECTED) {                         ┐
 66           ap.getLineAttributes().setLineAntialiasingEnable(true);  │
 67         } else if (state == ItemEvent.DESELECTED) {                ├(3)
 68           ap.getLineAttributes().setLineAntialiasingEnable(false); │
 69         }                                                          ┘
 70       }
 71     });
 72     panel.add(check);
 73 
 74     GraphicsConfiguration config =
 75       SimpleUniverse.getPreferredConfiguration();
 76     Canvas3D canvas = new Canvas3D(config);
 77     this.add(canvas, BorderLayout.CENTER);
 78 
 79     SimpleUniverse universe = new SimpleUniverse(canvas);
 80     universe.getViewingPlatform().setNominalViewingTransform();
 81 
 82     BranchGroup scene = createSceneGraph();
 83 
 84     universe.addBranchGraph(scene);
 85   }
 86 
 87   private BranchGroup createSceneGraph() {
 88     BranchGroup root = new BranchGroup();
 89 
 90     Point3d[] vertices = new Point3d[6];
 91     
 92     vertices[0] = new Point3d(-0.7, -0.1, 0.0);
 93     vertices[1] = new Point3d(-0.3, -0.7, 0.0);
 94     vertices[2] = new Point3d(0.2, -0.7, 0.0);
 95     vertices[3] = new Point3d(0.8, 0.0, 0.0);
 96     vertices[4] = new Point3d(0.6, 0.4, 0.0);
 97     vertices[5] = new Point3d(-0.6, 0.6, 0.0);
 98     
 99     LineArray geometry = new LineArray(vertices.length, GeometryArray.COORDINATES);
100     geometry.setCoordinates(0, vertices);
101     
102     Shape3D shape = new Shape3D(geometry);
103     
104     ap = new Appearance();
105     ap.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_READ);........(4)
106 
107     LineAttributes lattr = new LineAttributes();....................(5)
108     lattr.setCapability(LineAttributes.ALLOW_WIDTH_WRITE);        ┐
109     lattr.setCapability(LineAttributes.ALLOW_PATTERN_WRITE);      ├(6)
110     lattr.setCapability(LineAttributes.ALLOW_ANTIALIASING_WRITE); ┘
111     lattr.setLineWidth(1.0f);.......................................(7)
112     lattr.setLinePattern(LineAttributes.PATTERN_SOLID);.............(8)
113     ap.setLineAttributes(lattr);....................................(9)
114    
115     shape.setAppearance(ap);
116    
117     root.addChild(shape);
118
119     return root;
120   }

(1)でTextField に入力された線の太さを setLineWidth() メソッドで LineAttributes に設定しています。

(2)でChoice で選ばれた線の種類を setLinePattern() メソッドで LineAttributes に設定しています。

(3)でCheckbox で選択されたアンチエリアス処理の有無を setLineAntialiasingEnable() メソッドで LineAttributes に設定しています。

(4)で Appearance の setCapability() メソッドを使ってLineAttributes の読み取り許可を設定しています。

(5)で LineAttributes を生成し、(6)でcapability bitを設定しています。

(7)で setLineWidth() メソッドを使って線の太さを 1.0f に設定し、(8)で setLinePattern() メソッドを使って線の種類を LineAttributes.PATTERN_SOLID (実線) に設定しています。

(9)で setLineAttributes() メソッドを使って LineAttributes を Appearance に設定しています。

■■ポリゴンの属性 (javax.media.j3d.PolygonAttributes)

javax.media.j3d.PolygonAttributes は、ポリゴンの描画モードや、カリングの種類、ポリゴン・オフセットなどの属性を定義します。

PolygonAttributes は setPolygonAttributes() メソッドで Appearance に設定されます。

Appearance のメソッド

public final void setPolygonAttributes(PolygonAttributes polygonAttributes)


クラス宣言

public class PointAttributes
extends NodeComponent

コンストラクター

public PolygonAttributes()

public PolygonAttributes(int polygonMode,     // ポリゴンの描画モード
                         int cullFace,        // カリングの種類
                         float polygonOffset) // ポリゴンオフセット  

public PolygonAttributes(int polygonMode,            // ポリゴンの描画モード
                         int cullFace,               // カリングの種類      
                         float polygonOffset,        // ポリゴンオフセット  
                         boolean backFaceNormalFlip) // 法線ベクトルを裏面にも適用?

定数フィールド (一部)

public static final int POLYGON_POINT // ポリゴンの頂点を描画
public static final int POLYGON_LINE  // ポリゴンの辺を描画
public static final int POLYGON_FILL  // ポリゴンの面を描画

public static final int CULL_NONE     // カリングしない
public static final int CULL_BACK     // 裏面をカリング
public static final int CULL_FRONT    // 表面をカリング

ポリゴンの描画モードは、デフォルトでは POLYGON_FILL (ポリゴンの面を描画) です。POLYGON_POINT に設定するとポリゴンの頂点だけが描画され、POLYGON_LINE に設定するとポリゴンの辺だけが描画されます。

カリングとは何でしょうか? cull は「摘む、摘み集める」という意味で、 花などを摘みとって集めるようなときや、 (質の悪い)家畜などをえり分けるときに使われる動詞です。 (研究社「新英和大辞典」より)

CGの分野では、「 不要な描画要素を、あらかじめ切り捨てて描画しない 」ことを言います。たとえば、閉じた多面体のポリゴンの各面の裏面は、 計算しても結局は描画されませんから、 最初から計算しない方が効率は良くなります。

Java 3Dでは、デフォルトでのカリングの設定は CULL_BACK です。つまり、裏面は描画の対象から外されます。

CULL_FRONT に設定すると表面が描画の対象から外されます。CULL_NONE では表面、裏面ともに描画の対象となります。

裏面を描画するには、CULL_NONE に設定するだけでは不十分です。boolean backFaceNormalFlip を true に設定する必要があります。この設定値は、法線ベクトルを裏面にも適用するもので、表面の(頂点の)法線ベクトルと逆方向のベクトルが裏面に適用されます。

ポリゴン・オフセットは特殊な描画処理をやり易くするために導入された設定値です。ポリゴン・オフセットは、次のような描画に有効のようです。

PolygonAttributes を使ったサンプルは次のようなものです。

AppearanceTest3.gif

捻ったリボンのようなポリゴンを描画してあります。ポリゴンの裏面はデフォルトでは描画されません。

CULL_NONE を選んで、チェックボックスで backFaceNormalFlip を true にすると表面、裏面が両方とも描画されます。

POLYGON_POINT, POLYGON_LINE, POLYGON_FILL

を選ぶと描画対象が頂点、辺、面に切り替わります。

AppearanceTest.java
  1 // Java 3Dテスト用アプレット
  2 // AppearanceTest.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.util.*;
 11 import javax.media.j3d.*;
 12 import javax.vecmath.*;
 13 import com.sun.j3d.utils.applet.MainFrame;
 14 import com.sun.j3d.utils.universe.SimpleUniverse;
 15 import com.sun.j3d.utils.geometry.GeometryInfo;
 16 import com.sun.j3d.utils.geometry.NormalGenerator;
 17 
 18 public class AppearanceTest extends Applet {
 19   Appearance ap = null;
 20 
 21   public AppearanceTest() {
 22     this.setLayout(new BorderLayout());
 23 
 24     Panel panel = new Panel();
 25     this.add(panel, BorderLayout.NORTH);
 26 
 27     Checkbox checkbox = new Checkbox("NormalFlip", false);
 28     checkbox.addItemListener(new ItemListener() {
 29       public void itemStateChanged(ItemEvent e) {
 30         System.out.println(e.getStateChange());
 31         switch (e.getStateChange()) {
 32           case ItemEvent.SELECTED:
 33             ap.getPolygonAttributes().setBackFaceNormalFlip(true);....(1)
 34             break;
 35           case ItemEvent.DESELECTED:
 36             ap.getPolygonAttributes().setBackFaceNormalFlip(false);...(2)
 37             break;
 38         }
 39       }
 40     });
 41     panel.add(checkbox);
 42     
 43     Choice cullChoice = new Choice();
 44     cullChoice.add("CULL_NONE");
 45     cullChoice.add("CULL_FRONT");
 46     cullChoice.add("CULL_BACK");
 47     cullChoice.select(2);
 48     cullChoice.addItemListener(new ItemListener() {
 49       public void itemStateChanged(ItemEvent e) {
 50         String item = (String)e.getItem();
 51         if (item.equals("CULL_NONE")) {                                       ┐
 52           ap.getPolygonAttributes().setCullFace(PolygonAttributes.CULL_NONE); │
 53         } else if (item.equals("CULL_FRONT")) {                               │
 54           ap.getPolygonAttributes().setCullFace(PolygonAttributes.CULL_FRONT);├(3)
 55         } else if (item.equals("CULL_BACK")) {                                │
 56           ap.getPolygonAttributes().setCullFace(PolygonAttributes.CULL_BACK); │
 57         }                                                                     ┘
 58       }
 59     });
 60     panel.add(cullChoice);
 61     
 62     Choice modeChoice = new Choice();
 63     modeChoice.add("POLYGON_FILL");
 64     modeChoice.add("POLYGON_LINE");
 65     modeChoice.add("POLYGON_POINT");
 66     modeChoice.addItemListener(new ItemListener() {
 67       public void itemStateChanged(ItemEvent e) {
 68         String item = (String)e.getItem();
 69         if (item.equals("POLYGON_FILL")) {
 70           ap.getPolygonAttributes().setPolygonMode(PolygonAttributes.POLYGON_FILL);
 71         } else if (item.equals("POLYGON_LINE")) {
 72           ap.getPolygonAttributes().setPolygonMode(PolygonAttributes.POLYGON_LINE);
 73         } else if (item.equals("POLYGON_POINT")) {
 74           ap.getPolygonAttributes().setPolygonMode(PolygonAttributes.POLYGON_POINT);
 75         }
 76       }
 77     });
 78     panel.add(modeChoice);
 79     
          :
          :
 94     GraphicsConfiguration config =
 95       SimpleUniverse.getPreferredConfiguration();
 96     Canvas3D canvas = new Canvas3D(config);
 97 
 98     this.add(canvas, BorderLayout.CENTER);
 99 
100     SimpleUniverse universe = new SimpleUniverse(canvas);
101     universe.getViewingPlatform().setNominalViewingTransform();
102 
103     BranchGroup scene = createSceneGraph();
104 
105     universe.addBranchGraph(scene);
106   }
107 
108   private BranchGroup createSceneGraph() {
109     BranchGroup root = new BranchGroup();
110 
111     DirectionalLight light = new DirectionalLight();
112     light.setInfluencingBounds(new BoundingSphere(new Point3d(), 100.0));
113     root.addChild(light);
114     
115     Point3d[] vertices = createPoints();
116     int[] indices = createIndices(vertices.length);
117     
118     GeometryInfo ginfo = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
119     ginfo.setCoordinates(vertices);
120     ginfo.setCoordinateIndices(indices);
121     NormalGenerator ngen = new NormalGenerator();
122     ngen.generateNormals(ginfo);
123     
124     Shape3D shape = new Shape3D(ginfo.getIndexedGeometryArray());
125     
126     ap = new Appearance();
127     ap.setCapability(Appearance.ALLOW_POLYGON_ATTRIBUTES_READ);
128 
129     PolygonAttributes pattr = new PolygonAttributes();..............(4)
130     pattr.setCapability(PolygonAttributes.ALLOW_CULL_FACE_WRITE);   ┐
131     pattr.setCapability(PolygonAttributes.ALLOW_MODE_WRITE);        ├(5)
132     pattr.setCapability(PolygonAttributes.ALLOW_NORMAL_FLIP_WRITE); │
133     pattr.setCapability(PolygonAttributes.ALLOW_OFFSET_WRITE);      ┘
134     ap.setPolygonAttributes(pattr);.................................(6)
135 
136     Material mat = new Material();
137     mat.setDiffuseColor(new Color3f(0.0f, 0.0f, 1.0f));
138     ap.setMaterial(mat);
139     
140     shape.setAppearance(ap);
141     
142     root.addChild(shape);
143 
144     return root;
145   }
146 
147   public Point3d[] createPoints() { ...(7)
       :
       :
173   }

(1), (2)では、チェックボックスの選択状態に応じて setBackFaceNormalFlip() メソッドを使って法線ベクトルを裏面にも適用するかどうかを設定しています。

(3)でChoice の選択状態に応じて setCullMode() メソッドを使ってポリゴンの描画モードを切替えています。

(4)で PolygonAttributes を生成し、(5)で capabitily bit を設定し、(6)で setPolygonAttributes() メソッドを使って Appearance に設定しています。

詳しい解説は省略しますが、(7)で捻ったリボンのようなポリゴンを定義しています。

■■色の属性 (javax.media.j3d.ColoringAttributes)

javax.media.j3d.ColoringAttributes は、色の描画モードや物体の色を定義します。

ColoringAttributes は setColoringAttributes() メソッドで Appearance に設定されます。

Appearance のメソッド

public final void setColoringAttributes(ColoringAttributes coloringAttributes)


クラス宣言

public class ColoringAttributes
extends NodeComponent

コンストラクター

public ColoringAttributes()

public ColoringAttributes(Color3f color,  // 設定する色
                          int shadeModel) // 色の描画モード

public ColoringAttributes(float red,      // 設定する色の赤要素
                          float green,    // 設定する色の緑要素
                          float blue,     // 設定する色の青要素
                          int shadeModel) // 色の描画モード

定数フィールド (一部)

public static final int FASTEST       // 可能な限り高速に描画
public static final int NICEST        // 可能な限り高品質に描画
public static final int SHADE_FLAT    // フラットシェーディング
public static final int SHADE_GOURAUD // グーローシェーディング

GeometryArray で設定する色は頂点ごとに指定しますが、ColoringAttributes で設定する色は Shape3D 全体に適用されます。

すでに GeomeryArray の setColor(), setColors()で頂点に色が設定されていると、ColoringAttributes で色を設定しても適用されません。

Appearance に Material が設定されているとき、ColoringAttributes で色を設定すると ColoringAttributes で設定した色が適用されます。

setColor()/setColors(), ColoringAttributes, Material には次のような関係が成立ちます。

setColor()/setColors() >>優先>> ColoringAttributes >>優先>> Material

色の描画モードのデフォルトは SHADE_GOURAUD (グーローシェーディング) です。頂点ごとに指定された色は、頂点間でスムースに補間されます。

SHADE_FLAT (フラットシェーディング) に設定すると、ポリゴンの最後の頂点に適用した色がポリゴン全体に適用されます。他の頂点に設定した色は無視されます。

SHADE_NICEST, SHADE_FASTEST は SHADE_GOURAUD と同様に頂点間で色を補間しますが、可能な限り高品質に描画したり、可能な限り高速に描画したりする設定になります。ただしこの項目はハードウエア、ソフトウエア環境に依存しており、SHADE_GOURAUD と異なる結果になるとは限りません。

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

AppearanceTest4.gif

画面上の TriangleArray には、頂点ごとの色指定はしてありません。

画面下の TriangleStripArray には、頂点ごとに色を指定しました。

TextField でR, G, Bを入力して、ColoroingAttributes に色を設定したり、Choice から色の描画モードを選択したりしてみてください。

このサンプルのソースコードはCD-ROMを参照してください。

AppearanceTest.java

色はR、G、Bそれぞれを数値入力するようにしました。この入力にはTextFieldを使っても良いのですが、コードが繁雑になるのでjava.awt.Panelを拡張したTuplePanelクラスを書きました。

TuplePanel.gif

TuplePanelはTuple3f, Tuple4fのサブクラスならColor3f, Color4f以外も使用できるようにしました。

TuplePanelはTupleEventというイベントクラスを持ちます。ユーザー入力によって値が変化するとTupleEventが発生します。

TupleEvent.java
 1  // Java 3D Test Program
 2  // TupleEnevt.java
 3  //   Copyright (c) 1999 ENDO Yasuyuki
 4  //                      mailto:yasuyuki@javaopen.org
 5  //                      http://www.javaopen.org/j3dbook/index.html
 6  
 7  
 8  import java.util.EventObject;
 9  import javax.vecmath.Tuple3f;
10  import javax.vecmath.Color3f;
11  import javax.vecmath.Point3f;
12  import javax.vecmath.Vector3f;
13  import javax.vecmath.Tuple4f;
14  import javax.vecmath.Color4f;
15  import javax.vecmath.Point4f;
16  import javax.vecmath.Vector4f;
17  import javax.vecmath.Quat4f;
18  
19  public class TupleEvent extends EventObject {
20    protected Tuple3f tuple3f = null;
21    protected Tuple4f tuple4f = null;
22    public TupleEvent(Object source, Tuple3f tuple) {
23      super(source);
24      tuple3f = tuple;
25    }
26    public TupleEvent(Object source, Tuple4f tuple) {
27      super(source);
28      tuple4f = tuple;
29    }
30    public Tuple3f getTuple3f() { return tuple3f; }
31    public Color3f getColor3f() { return (Color3f)tuple3f; }
32    public Point3f getPoint3f() { return (Point3f)tuple3f; }
33    public Vector3f getVector3f() { return (Vector3f)tuple3f; }
34    public Tuple4f getTuple4f() { return tuple4f; }
35    public Color4f getColor4f() { return (Color4f)tuple4f; }
36    public Point4f getPoint4f() { return (Point4f)tuple4f; }
37    public Vector4f getVector4f() { return (Vector4f)tuple4f; }
38    public Quat4f getQuat4f() { return (Quat4f)tuple4f; }
39  }

TupleEventによるイベント通知を受けるためにはTupleEventListenerインターフェースを実装します。

TupleEventListener.java
  1  // Java 3D Test Program
  2  // TupleEventListener.java
  3  //   Copyright (c) 1999 ENDO Yasuyuki
  4  //                      mailto:yasuyuki@javaopen.org
  5  //                      http://www.javaopen.org/j3dbook/index.html
  6  
  7  public interface TupleEventListener {
  8    void tupleStateChanged(TupleEvent e);
  9  }

TupleEventListenerの実装クラスでは、tupleStateChanged()メソッドの中で変化したTuple3fまたはTuple4fを取得します。取得にはTulpeEventの次のメソッドが使用できます。

TupleEventのメソッド

public Tuple3f getTuple3f()
public Color3f getColor3f()
public Point3f getPoint3f()
public Vector3f getVector3f()
public Tuple4f getTuple4f()
public Color4f getColor4f()
public Point4f getPoint4f()
public Vector4f getVector4f()
public Quat4f getQuat4f()

(1)でTuplePanelを生成しています。(2)でaddTulpeEventListener()メソッドを使って無名内部クラスをイベントリスナーとして登録しています。(3)がtupleStateChanged()メソッドの宣言です。(4)では、TupleEvent#getColor3f()メソッドで変化後のColor3fを取得し、setColor()メソッドでColoringAttributesにセットしています。

 24      TuplePanel tp = new TuplePanel(color);.................(1)
 25      tp.addTupleEventListener( new TupleEventListener() {...(2)
 26        public void tupleStateChanged(TupleEvent e) {      ┐
 27          cattr.setColor(e.getColor3f());..............(4) ├(3)
 28        }                                                  ┘
 29      });
 30      panel.add(tp);

(5)でChoice で選択された項目を元に setShadeModel() メソッドで色の描画モードとして設定しています。

 32      Choice choice = new Choice();
 33      choice.add("FASTEST");
 34      choice.add("NICEST");
 35      choice.add("SHADE_FLAT");
 36      choice.add("SHADE_GOURAUD");
 37      choice.select(3);
 38      choice.addItemListener(new ItemListener() {
 39        public void itemStateChanged( ItemEvent e ){
 40          String item = (String)e.getItem();
 41          if (item.equals("FASTEST")) {                            ┐
 42            cattr.setShadeModel(ColoringAttributes.FASTEST);       │
 43          } else if (item.equals("NICEST")) {                      │
 44            cattr.setShadeModel(ColoringAttributes.NICEST);        │
 45          } else if (item.equals("SHADE_FLAT")) {                  ├(5)
 46            cattr.setShadeModel(ColoringAttributes.SHADE_FLAT);    │
 47          } else if (item.equals("SHADE_GOURAUD")) {               │
 48            cattr.setShadeModel(ColoringAttributes.SHADE_GOURAUD); │
 49          }                                                        ┘
 50        }
 51      });
 52      panel.add(choice);

(6)で ColoringAttributes を生成し、(7)で capability bit を設定し、(8)で setColoringAttributes() メソッドを使って Appearance に設定しています。

125    private Appearance createAppearance() {
126      Appearance ap = new Appearance();
127      ap.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
128      
129      cattr = new ColoringAttributes();..................................(6)
130      cattr.setCapability(ColoringAttributes.ALLOW_COLOR_READ);        ┐
131      cattr.setCapability(ColoringAttributes.ALLOW_COLOR_WRITE);       ├(7)
132      cattr.setCapability(ColoringAttributes.ALLOW_SHADE_MODEL_WRITE); ┘
133      ap.setColoringAttributes(cattr);...................................(8)
134      
135      return ap;
136    }

TuplePanelのソースはCD-ROMを参照してください。イベント処理、イベント通知が大半なので解説は省略します。

TuplePanel.java

なお、TuplePanelはJavaBeansとしての最低限の機能を持っていますが、次の点で汎用性に問題があります。

  1. パッケージ宣言をしていない
  2. java.lang.Serializeableを実装せず、シリアライズのためのメソッドも持たない
  3. BeanInfoが無い

あくまでも、テスト用として最低限の機能を実現するために書いたクラスです。

■■透明度の属性 (javax.media.j3d.TransparencyAttributes)

javax.media.j3d.TransparencyAttributes は、物体の透明度や、その描画モードなどを定義します。

TransparencyAttributes は、setTransparencyAttributes() メソッドで Appearance に設定されます。

Appearance のメソッド

public final void setTransparencyAttributes(TransparencyAttributes transparencyAttributes)


クラス宣言

public class TransparencyAttributes
extends NodeComponent

コンストラクター

public TransparencyAttributes()

public TransparencyAttributes(int tMode,  // 透明度の描画モード
                              float tVal) // 透明度

定数フィールド (一部)

public static final int FASTEST     // 可能な限り高速に描画
public static final int NICEST      // 可能な限り高品質に描画
public static final int BLENDED     // アルファブレンドで描画
public static final int SCREEN_DOOR // 格子状に透過を描画
public static final int NONE        // 透明度を描画しない

透明度の描画モードのデフォルト設定は NONE (透明度を描画しない) です。BLENDED に設定すると透明度が描画されます。FASTEST, NICEST は BLENDED と同じく透明度を描画しますが、FASTEST では可能な限り高速に描画し、NICEST では可能な限り高品質に描画する設定になります。ただしハードウエア、ソフトウエア環境によっては BLENDED と結果が変わらないこともあり得ます。

透明度は 0.0f が不透明で、1.0f に近付くほど透明度は高くなります。デフォルト値は 0.0f (不透明) です。

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

AppearanceTest5.gif

TextField に透明度を入力し、Choice で透明度の描画モードを変化させてみてください。画面上の物体に透明度が適用されます。

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

AppearanceTest.java
  1 // Java 3Dテスト用アプレット
  2 // AppearanceTest.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.Box;
 15 import com.sun.j3d.utils.geometry.Cone;
 16 import com.sun.j3d.utils.geometry.Sphere;
 17 
 18 public class AppearanceTest extends Applet {
 19   TransparencyAttributes tattr = null;
 20 
 21   public AppearanceTest() {
 22     Panel panel = new Panel();
 23     this.setLayout(new BorderLayout());
 24     this.add(panel, BorderLayout.NORTH);
 25     
 26     panel.add(new Label("Transparency"));
 27     TextField tfield = new TextField("0.0", 4);
 28     tfield.addActionListener( new ActionListener() {
 29       public void actionPerformed(ActionEvent e)  {
 30         float value = 0.0f;
 31         try {
 32           value = Float.parseFloat(e.getActionCommand());
 33           if (value > 1.0f) value = 1.0f;
 34           if (value < 0.0f) value = 0.0f;
 35           System.out.println("value=" + value);
 36           tattr.setTransparency(value);.........(1)
 37         } catch (NumberFormatException ex) { }
 38       }
 39     });
 40     panel.add(tfield);
 41     
 42     Choice choice = new Choice();
 43     choice.add("BLENDED");
 44     choice.add("FASTEST");
 45     choice.add("NICEST");
 46     choice.add("NONE");
 47     choice.add("SCREEN_DOOR");
 48     choice.select(3);
 49     choice.addItemListener( new ItemListener() {
 50       public void itemStateChanged(ItemEvent e) {
 51         String item = (String)e.getItem();
 52         if (item.equals("BLENDED")) {                                     ┐
 53           tattr.setTransparencyMode(TransparencyAttributes.BLENDED);      │
 54         } else if (item.equals("FASTEST")) {                              │
 55           tattr.setTransparencyMode(TransparencyAttributes.FASTEST);      │
 56         } else if (item.equals("NICEST")) {                               │
 57           tattr.setTransparencyMode(TransparencyAttributes.NICEST);       ├(2)
 58         } else if (item.equals("NONE")) {                                 │
 59           tattr.setTransparencyMode(TransparencyAttributes.NONE);         │
 60         } else if (item.equals("SCREEN_DOOR")) {                          │
 61           tattr.setTransparencyMode(TransparencyAttributes.SCREEN_DOOR);  │
 62         }                                                                 ┘
 63       }
 64     });
 65     panel.add(choice);
 66     
 67     GraphicsConfiguration config =
 68       SimpleUniverse.getPreferredConfiguration();
 69 
 70     Canvas3D canvas = new Canvas3D(config);
 71     this.add(canvas, BorderLayout.CENTER);
 72     
 73     SimpleUniverse universe = new SimpleUniverse(canvas);
 74     universe.getViewingPlatform().setNominalViewingTransform();
 75     
 76     universe.addBranchGraph(createSceneGraph());
 77   }
 78   
 79   public BranchGroup createSceneGraph() {
 80     BranchGroup root = new BranchGroup();
 81 
 82     Light light1 = createLight( new Color3f(0.7f, 0.7f, 0.7f),
 83                                 new Vector3f(0.5f, -0.5f, -0.7f) );
 84     root.addChild(light1);
 85     
 86     Light light2 = createLight( new Color3f(0.3f, 0.3f, 0.3f),
 87                                 new Vector3f(-0.5f, -0.5f, -0.7f) );
 88     root.addChild(light2);
 89     
 90     Transform3D t3d = new Transform3D();
 91     t3d.rotX(Math.PI / 8.0);
 92     Transform3D roty = new Transform3D();
 93     roty.rotY(Math.PI / 4.0);
 94     t3d.mul(roty);
 95     
 96     TransformGroup trans = new TransformGroup(t3d);
 97     root.addChild(trans);
 98     
 99     Appearance yapp = createAppearance( new Color3f(1.0f, 1.0f, 0.0f) );
100     Appearance rapp = createAppearance( new Color3f(1.0f, 0.0f, 0.0f) );
101     Appearance bapp = createAppearance( new Color3f(0.0f, 0.0f, 1.0f) );
102     
103     Transform3D bt3d = new Transform3D();
104     bt3d.set(new Vector3d(-0.22, -0.02, 0.22));
105     TransformGroup btrans = new TransformGroup(bt3d);
106     btrans.addChild(new Box( 0.16f, 0.16f, 0.16f, yapp ));
107     trans.addChild(btrans);
108     
109     Transform3D ct3d = new Transform3D();
110     ct3d.set( new Vector3d(-0.2, 0.0, -0.2) );
111     TransformGroup ctrans = new TransformGroup(ct3d);
112     ctrans.addChild( new Cone( 0.2f, 0.4f, Cone.BODY | Cone.CAP, rapp ) );
113     trans.addChild(ctrans);
114     
115     Transform3D st3d = new Transform3D();
116     st3d.set( new Vector3d(0.2, 0.0, 0.2) );
117     TransformGroup strans = new TransformGroup(st3d);
118     strans.addChild( new Sphere(0.2f, bapp) );
119     trans.addChild(strans);
120     
121     return root;
122   }
123   
124   private Light createLight(Color3f color, Vector3f vec) {
125     DirectionalLight light = new DirectionalLight( color, vec );
126     light.setInfluencingBounds( new BoundingSphere( new Point3d(), 100.0 ) );
127     
128     return light;
129   }
130   
131   private Appearance createAppearance(Color3f color) {
132     Appearance ap = new Appearance();
133     ap.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
134 
135     Material mat = new Material();
136     mat.setDiffuseColor(color);
137     //mat.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
138     //mat.setShininess(127.0f);
139     ap.setMaterial(mat);
140     
141     if (tattr == null) {
142       tattr = new TransparencyAttributes();...........................(3)
143       tattr.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE);  ┬(4)
144       tattr.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE); ┘
145     }
146     ap.setTransparencyAttributes(tattr);..............................(5)
147 
148     return ap;
149   }

(3)でTextField に入力された値を元に setTransparency() メソッドを使って透明度を設定しています。

Choice で選択された項目を元に setTransparencyMode() メソッドを使って透明度の描画モードを設定しています。

(5)で TransparencyAttributes を生成し、(6)で capabitily bit を設定し、(7)で setTransparencyAttributes() メソッドを使って Appearance に設定しています。

現実世界とは違い、Java 3Dでの透明度の表現は色の混合によって実現されています。

複数の物体に前後関係があるとき、色の混合がうまく行かない場合があります。うまく行かない場合は、奥にある物体を先に描画しなければならないことがあります。

■■レンダリング属性 (javax.media.j3d.RenderingAttributes)

javax.media.j3d.RenderingAttributes は、デプスバッファーの有効/無効、デプスバッファーへの書き込みの有効/無効、アルファテストのテスト値やテスト方法などを定義します。

RenderingAttributes は setRenderingAttributes() メソッドで Appearance に設定されます。

Appearance のメソッド

public final void setRenderingAttributes(RenderingAttributes renderingAttributes)


クラス宣言

public class RenderingAttributes
extends NodeComponent

コンストラクター

public RenderingAttributes()

public RenderingAttributes(boolean depthBufferEnable,      // デプスバッファーの有効/無効
                           boolean depthBufferWriteEnable, // デプスバッファーへの書き込みの有効/無効
                           float alphaTestValue,           // アルファテストのための値
                           int alphaTestFunction)          // アルファテストの方法

定数フィールド (一部)

public static final int ALWAYS           // 常に描画する
public static final int NEVER            // 常に描画しない
public static final int EQUAL            // テスト値と等しいとき描画     == alphaTestValue
public static final int NOT_EQUAL        // テスト値と等しくないとき描画 != alphaTestValue
public static final int LESS             // テスト値より小なら描画       < alphaTestValue
public static final int LESS_OR_EQUAL    // テスト値以下なら描画         <= alphaTestValue
public static final int GREATER          // テスト値より大なら描画       > alphaTestValue
public static final int GREATER_OR_EQUAL // テスト値以上なら描画         >= alphaTestValue

デプスバッファーとは何でしょうか?

現実世界で我々がものを見るとき、奥にある物体は手前にある物体に隠れて見えなくなります。

CG でこれを実現する方法はいくつかあります。たとえば、最も奥にあるポリゴンを最初に描画し、最も手前にあるポリゴンを最後に描画すればとりあえずは実現できます。

ただしこの方法では、ポリゴンが交差した場合や、物体が移動/回転/変形したり、視点が移動/回転する場合などに不都合が生じることがあります。

別の方法として、デプスバッファーを使う方法があります。デプスバッファーを使う場合、物体を描画する順番と無関係に実現することができます。

デプスバッファーは、スクリーンの各ピクセルごとに、「視点から物体までの距離」(深度値)と色を保持しています。

デプスバッファーを使った描画では、次のようなことが行われます。

  1. 視点から描画する物体までの距離を計算する
  2. すでにデプスバッファーに描画された深度値と、これから描画する物体の深度値をピクセルごとに比較する
  3. これから描画する物体のピクセルが視点に近ければ、そのピクセルを上書きする
  4. すでに描かれたピクセルが視点に近ければ、そのピクセルはそのまま (上書きしない)

RenderingAttributes では、このデプスバッファーによる描画を有効にしたり、無効にしたりすることができます。デフォルト値では有効です。

デプスバッファーを無効にしたい場合とはどんな場合でしょうか?

透明の物体と不透明の物体があり、その前後関係が変化するようなとき、デプスバッファーを使うとうまく描画できないときがあるようです。このような場合には奥にあるポリゴンを先に描画し、手前にあるポリゴンを後から描画する必要が生じることがあります。

このような場合はデプスバッファーを無効にし、javax.media.j3d.OrderedGroup などを使って奥から順番に物体を描画してやる、といった方法が考えられます。

アルファテストとは何でしょうか?

ここで言う「アルファ」とは、物体の「不透明の度合」です。不透明の度合の範囲は 0.0f〜1.0f です。1.0f では完全に不透明、0.0f では完全に透明です。

註:
Java 3D には時間による変化を抽象化した javax.media.Alpha というクラスがあります。物体の「不透明の度合」に「アルファ」という用語を使用するのは混乱の元ではないかと思います。このことを意識したのか、物体の「透明度」に関しては Transparency という用語が使われています。
RenderingAttributes の alhpaTest ではアルファという用語が使われていますが、このアルファは javax.media.Alpha とは関係ありません。

アルファテストは物体のピクセルごとに行われます。

  1. まず、比較のためのテスト値を決めます。範囲は 0.0〜1.0 です。
  2. テスト値と、物体のピクセルごとの不透明度を比較します
  3. テスト方法に従って、そのピクセルを描画するかしないかを判定します

テスト方法にには次のようなものがあります。

ALWAYS 常に描画する
NEVER 常に描画しない
EQUAL テスト値と等しいとき描画
NOT_EQUAL テスト値と等しくないとき描画
LESS テスト値より小なら描画
LESS_OR_EQUAL テスト値以下なら描画
GREATER テスト値より大なら描画
GREATER_OR_EQUAL テスト値以上なら描画

ALLWAYS, NEVER では、ピクセルは常に描画されるか、全く描画されないかになります。

EQUAL, NOT_EQUAL では、テスト値と等しいピクセルだけが描画されるか、またはそれだけ描画されないかになります。一度に描けるのは一つだけになりますが、等高線のようなものを描くことも可能です。

LESS, LESS_OR_EQUAL, GRATER, GLEATER_OR EQUAL では、大小比較した結果によってピクセルを描画するかしないかを判定できます。

アルファテストを利用すると、マスク合成のような効果を得ることもできます。

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

AppearanceTest6.gif

このサンプルはフリーの地図データを元にモデリングしました。

タイトル:標高データ 立山カルデラ周辺
著作権者:NABE さん
http://www.vector.co.jp/soft/data/home/se021130.html

read.me より:

転載等について
・このアーカイブファイルに含まれるデータの著作権は著者に属するものとします。
・このデータを使用したことによる損害等には著者は一切の責任を負いません。
・データの転載や再配布については、自由に行ってもかまいませんが、以下の3点を
 明記して下さい。 

 1. 転載・再配布に金銭の授受を伴わないこと(ただし手数料を除く)
 2. (筆者の読みとり能力から)このデータには100%の信頼性が伴わないこと
 3. このデータは、国土地理院発行の25,000分の1地形図”立山”(H3.7.1)
   の一部を、100m毎に、その等高線から標高を読んでいったものであること

このサンプルでは、javax.vecmath.Color4f を使って各頂点に不透明度を設定しています。

最も標高が低い頂点の不透明度を 0.0f に、最も標高が高い頂点の不透明度を 1.0fにしてあります。

この実行例では、アルファテストのテスト方法を GREATER (テスト値より大なら描画) にし、テスト値を 0.3f にしてあります。これで、不透明度が 0.3f 以下のピクセルは描画されなくなります。

テスト方法をいろいろ変えてみてください。たとえば EQUAL を選ぶと等高線のようなものが描けます。

AppearanceTest.java(一部)
  1 // Java 3Dテスト用アプレット
  2 // AppearanceTest.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.io.*;
 11 import java.net.*;
 12 import java.util.*;
 13 import javax.media.j3d.*;
 14 import javax.vecmath.*;
 15 import com.sun.j3d.utils.applet.MainFrame;
 16 import com.sun.j3d.utils.universe.SimpleUniverse;
 17 import com.sun.j3d.utils.geometry.Box;
 18 import com.sun.j3d.utils.geometry.Cone;
 19 import com.sun.j3d.utils.geometry.Sphere;
 20 import com.sun.j3d.utils.behaviors.mouse.*;
 21 
 22 public class AppearanceTest extends Applet {
 23   boolean isStandalone = false;
 24 
 25   SimpleUniverse universe = null;
 26   BranchGroup scene = null;
 27   
 28   TransparencyAttributes tattr = null;
 29   RenderingAttributes rattr = null;
 30 
 31   public AppearanceTest() {
 32     this(false);
 33   }
 34 
 35   public AppearanceTest(boolean isStandalone) {
 36     this.isStandalone = isStandalone;
 37 
 38     this.setLayout(new BorderLayout());
        :
        :
127     Choice achoice = new Choice();
128     achoice.add("ALWAYS");
129     achoice.add("NEVER");
130     achoice.add("EQUAL");
131     achoice.add("NOT_EQUAL");
132     achoice.add("LESS");
133     achoice.add("LESS_OR_EQUAL");
134     achoice.add("GREATER");
135     achoice.add("GREATER_OR_EQUAL");
136     achoice.addItemListener(new ItemListener() {
137       public void itemStateChanged(ItemEvent e) {
138         String item = (String)e.getItem();
139         if (item.equals("ALWAYS")) {                                        ┐
140           rattr.setAlphaTestFunction(RenderingAttributes.ALWAYS);           │
141         } else if (item.equals("NEVER")) {                                  │
142           rattr.setAlphaTestFunction(RenderingAttributes.NEVER);            │
143         } else if (item.equals("EQUAL")) {                                  │
144           rattr.setAlphaTestFunction(RenderingAttributes.EQUAL);            │
145         } else if (item.equals("NOT_EQUAL")) {                              │ 
146           rattr.setAlphaTestFunction(RenderingAttributes.NOT_EQUAL);        │
147         } else if (item.equals("LESS")) {                                   ├(1)
148           rattr.setAlphaTestFunction(RenderingAttributes.LESS);             │
149         } else if (item.equals("LESS_OR_EQUAL")) {                          │
150           rattr.setAlphaTestFunction(RenderingAttributes.LESS_OR_EQUAL);    │
151         } else if (item.equals("GREATER")) {                                │
152           rattr.setAlphaTestFunction(RenderingAttributes.GREATER);          │
153         } else if (item.equals("GREATER_OR_EQUAL")) {                       │
154           rattr.setAlphaTestFunction(RenderingAttributes.GREATER_OR_EQUAL); │
155         }                                                                   ┘
156       }
157     });
158     downpanel.add(achoice);
159     
160     downpanel.add( new Label("AlphaTestValue") );
161     TextField afield = new TextField("0.0", 4);
162     afield.addActionListener(new ActionListener() {
163       public void actionPerformed(ActionEvent e) {
164         float value = 0.0f;
165         try {
166           value = Float.parseFloat(e.getActionCommand());
167           System.out.println("value=" + value);
168           rattr.setAlphaTestValue(value);....................(2)
169         } catch (NumberFormatException ex) {}
170       }
171     });
172     downpanel.add(afield);
173   }
174 
175   public void init() {
176     GraphicsConfiguration config =
177       SimpleUniverse.getPreferredConfiguration();
178 
179     Canvas3D canvas = new Canvas3D(config);
180     this.add(canvas, BorderLayout.CENTER);
181     
182     universe = new SimpleUniverse(canvas);
183     universe.getViewingPlatform().setNominalViewingTransform();
184     //universe.getViewer().getView().setDepthBufferFreezeTransparent(false);
185     
186     scene = createSceneGraph();
187     
188     universe.addBranchGraph(scene);
189   }
190   
191   public BranchGroup createSceneGraph() {
192     BranchGroup root = new BranchGroup();
193     root.setCapability(BranchGroup.ALLOW_DETACH);
194 
195     GeometryArray geom = null;
196     try {
197       geom = createGeometry();
198     } catch (IOException ex) {
199       ex.printStackTrace();
200     }
201 
202     Appearance app = createAppearance();
203 
204     Shape3D mesh = new Shape3D(geom, app);
205 
206     root.addChild(mesh);
207 
208     return root;
209   }
210   
211   private GeometryArray createGeometry() throws IOException { ...(3)
212     BufferedReader in = null;
213     
214     if (isStandalone) {
215       in = new BufferedReader( new FileReader("tatecal.txt") );
216     } else {
217       URL url = new URL(getCodeBase() + "tatecal.txt");
218       URLConnection conn = url.openConnection();
219       in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
220     }
221     final float XWEST = -0.8f;
222     float x = XWEST;
223     float y = 0.64f;
224     
225     float dx = 2.0f * -x / 100.0f;
226     float dy = 2.0f * y / 80.0f;
227     float zmax = 0.0f;
228     float zmin = 0.0f;
229     String line = null;
230     Point3f[] vertices = new Point3f[101*81];
231     int i = 0;
232     int l = 0;
233     while ( (line = in.readLine()) != null ) {
234       StringTokenizer st = new StringTokenizer(line, " ,", false);
235       String height = null;
236       while (st.hasMoreTokens()) {
237         float z = 0.0f; 
238         height = st.nextToken();
239         try {
240           z = (Float.parseFloat(height) - 1500.0f) / 3125.0f;
241           if (z > zmax) zmax = z;
242           if (z < zmin) zmin = z;
243           vertices[i] = new Point3f(x, y, z);
244           i++;
245         } catch (NumberFormatException ex) {}
246         x += dx;
247       }
248       x = XWEST;
249       y -= dy;
250       l++;
251     }
252 
253     float range = zmax - zmin;
254     Color4f[] colors = new Color4f[101*81];
255     for (i=0; i<(101*81); i++) colors[i] = createColor(vertices[i].z, zmin, range);
256 
257     int[] indices = new int[100*79*4];
258     int n = 0;
259     for (i=0; i<79; i++) {
260       for (int j=0; j<100; j++) {
261         indices[n++] = i       * 101 + j;
262         indices[n++] = (i + 1) * 101 + j;
263         indices[n++] = (i + 1) * 101 + j + 1;
264         indices[n++] = i       * 101 + j + 1;
265       }
266     }
267     
268     IndexedQuadArray geometry =
269       new IndexedQuadArray( vertices.length,
270                             GeometryArray.COORDINATES |
271                             GeometryArray.COLOR_4,
272                             indices.length );
273     geometry.setCoordinates(0, vertices);
274     geometry.setCoordinateIndices(0, indices);
275     geometry.setColors(0, colors);
276     geometry.setColorIndices(0, indices);
277 
278     return geometry;
279   }
280 
281   private Color4f createColor(float z, float zmin, float range) { ...(4)
282     final Color4f green = new Color4f(0.0f, 1.0f, 0.0f, 0.0f);
283     final Color4f brown = new Color4f(0.5f, 0.3f, 0.1f, 0.0f);
284     final Color4f white = new Color4f(1.0f, 1.0f, 1.0f, 0.0f);
285     Color4f color = new Color4f();
286     float a = (z - zmin) / range;
287     if (a < 0.5f) color.interpolate(green, brown, a * 2.0f);
288     else          color.interpolate(brown, white, (a - 0.5f) * 2.0f);
289     color.w = a;
290 
291     return color;
292   }
293   
294   private Appearance createAppearance() {
295     Appearance ap = new Appearance();
296     ap.setCapability(Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);
297 
298     tattr = new TransparencyAttributes();
299     tattr.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE);
300     tattr.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
301 
302     ap.setTransparencyAttributes(tattr);
303     
304     rattr = new RenderingAttributes();........................................(5)
305     rattr.setCapability(RenderingAttributes.ALLOW_ALPHA_TEST_VALUE_WRITE);    ┐ 
306     rattr.setCapability(RenderingAttributes.ALLOW_ALPHA_TEST_FUNCTION_WRITE); ├(6)
307     rattr.setCapability(RenderingAttributes.ALLOW_DEPTH_ENABLE_READ);         ┘
308     //rattr.setDepthBufferWriteEnable(false);
309     //rattr.setDepthBufferEnable(false);
310 
311     ap.setRenderingAttributes(rattr);.........................................(7)
312 
313     return ap;
314   }

(1)でChoice での選択項目に応じて、setAlphaTestFunction() メソッドでアルファテストのテスト方法を設定しています。

(2)でTextField に入力されたテスト値を setAlphaTestValue() メソッドで設定しています。

(3)でファイルから数値データを読み取り、IndexedQuadArray を生成し、頂点データ、色と不透明度を設定しています。

(4)で標高に応じた色と不透明度を計算しています。最も標高が低い頂点の不透明度は 0.0f、最も標高が高い頂点の不透明度は 1.0f になるように計算しています。色は、最も標高が低い頂点は緑、中間では茶色、最も標高が高い頂点は白になるように、Color4f の interpolate() メソッドを使って補間しています。

(5)で RenderingAttributes を生成し、(6)で capability bit を設定し、(7)で setRenderingAttributes() メソッドを使って Appearence に設定しています。

ENDO Yasuyuki