SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Swingを用いたJava3D用材質エディタ

Java3Dで使用する材質編集用アプリケーションをSwingを使って作成


  • このエントリーをはてなブックマークに追加

Java3Dで使用する材質の設定を簡単な操作で結果を見ながら行うことができるアプリケーションを作成します。アプリケーションで編集した材質を実際のプログラムで使用できるように、Javaのコードの出力も行います。GUIにSwingを、プレビュー部分にJava3Dを使用します。

  • このエントリーをはてなブックマークに追加

はじめに

 前回の記事「3Dモデルを表示するJavaアプレットの作成 」では3Dモデルを表示するためのアプレットを紹介しました。前回は3D表示の基礎を学習することを目的としていたので、敢えてゼロから3D表示のためのプログラムを作りましたが、「Java3D」を使用すると3D表示を簡単に実現できます。今回はこのJava3Dを使用してシンプルなアプリケーションを1つ作成してみます。

 しかし、Java3Dを使用して立体モデルの表示を行うサンプルはJava3Dをインストールすると付いてきますし、他のWebサイトでもたくさん紹介されていますので、Java3Dの導入と基本部分は他の情報元に譲ることとし、今回はせっかくですから今後の3Dアプリの開発に役立つツールを作ってみることにします。

 Java3Dを実際に使ったことがある方は、3Dモデルの表示に使用する材質をどのように設定すべきか悩んだ経験があるかと思います。

 材質を設定するにはMaterialクラスを使用しますが、このクラスの属性に指定できる「拡散光」「環境光」「鏡面光」「放射光」の色にはそれぞれRGBの成分を指定する必要があり全部で4×3=12種類の値を決めなくてはなりません。さらに「輝度」と「透明度」も考慮すると、なんと14種類!

 意図した表示結果を得るには、これらの値を適切に設定する必要があるのですが、なかなかイメージ通りの材質を得るのは容易ではありません。その結果、値を微妙に変更しながら実行結果を確認してやり直し、という試行錯誤の繰り返しになったりして、これはもう大変な作業です。

 そこで今回は、Java3Dで使用する材質を図1のような画面で選択し、さらにはMaterialクラスの属性を設定するために必要なJavaのコードを出力してくれるツールを作ってみることにします(ただし、光源は固定でテクスチャ画像も使用しないものとします)。

 GUIにはSwingを使用するので、Java3DとSwingの両方の学習に役立てていただければ幸いです。

図1:この記事で紹介するアプリケーション MaterialEditor
図1:この記事で紹介するアプリケーション MaterialEditor

対象読者

 Java3D、Swingの基礎を学びたい人、Java3D(またはOpenGLやDirectX)で材質の設定に苦労したことがある人が対象です。

必要な環境

 J2SE Development Kit(JDK) 1.4.x以上、Java3D 1.3.x 以上が必要です。

Materialの属性

 今回作成するアプリケーションはJava3Dで使用するMaterial(材質)クラスの属性を定める各種パラメータを設定するためのものです。

 ここで登場するAmbient(環境光)、Diffuse(拡散光)、Specular(鏡面光)、Emission(放射光)、Shiniess(輝度)は、それぞれMaterialクラスのsetAmbientColorsetDiffuseColorsetSpecularColorsetEmissiveColorsetShininessメソッドで設定できます。Transparency(透明度)は、TransparencyAttributesクラスのsetTransparencyメソッドで設定します。

 これらの用語はCGソフトを扱っていると目にすることがあると思いますが、それぞれ次のような意味を持ちます。

  • Ambient(環境光):物体全体を均一に照らす光(直接光が届かない所をぼんやりと明るくする働きがあります)
  • Diffuse(拡散光):光源からの光が物体に当たって散乱される光
  • Specular(鏡面光):光源からの光が反射した光(「反射光」とも言います)
  • Emission(放射光):物体そのものが発する光
  • Shiniess(輝度):鏡面光の反射の鋭さ(値が大きいと小さな領域で鋭く反射し、値が小さいと広い領域でぼんやり反射します)
  • Transparency(透明度):物体の透明度(値がゼロだとまったく透過せず、値が1になると完全に透明になります)

 これらの全てを手動で設定するのは大変なので、一般によくある材質エディタでは、拡散光の色を決定すると他の色が自動的に決定されたり、放射光の項は最初から無いものとしている場合があります。

 拡散光が赤なのに環境光が青で鏡面光が緑、という材質はあまり現実的でなく、このような妙な性質を持つ材質が必要になることは希なため、拡散光さえ決めてしまえしまえばそれを元に他の光を定めてしまっても問題無いことが多いです。

 しかし、Java3Dでは(OpenGL、DirectXなども)それぞれの光の色を個別に設定できる機能が備わっているので、今回は全て独立して設定できるようにしてみました。

 今回のアプリケーションを触って、いろいろ試してみるとそれぞれの色の意味するものがわかると思います。また、今まで見たことがないような面白い見え方をする材質を発見できるかもしれません。

Swingでアプリケーションの外観を作る

 今回は、AWT(Abstract Window Toolkit)ではなくSwingを使用してアプリケーションのGUIを作成することにします。

 Swingを用いたコーディングは基本的にはAWTを使用した場合と大きな違いはありませんが、Swingには色を選択するためのコンポーネントJColorChooserクラスがあります。今回は様々な色を決定する必要があるため、このSwingに含まれるJColorChooserを活用します。

 今回作成するアプリケーションの基本となるクラスをMaterialEditorとし、次のように宣言します。

MaterialEditorクラスの宣言
public class MaterialEditor extends JFrame 
    implements ActionListener, ChangeListener, AdjustmentListener {

// (中略)

}

 上記の宣言の通り、メインのクラスはSwingのJFrameを継承し、後から追加するSwingの部品からイベント通知を受け取るために、ActionListener(ボタンのイベント用)、ChangeListener(JColorChooserのイベント用)、AdjustmentListener(スクロールバーのイベント用)を実装します。

 それぞれのイベントが発生した時に呼ばれるメソッドはactionPerformedstateChangedadjustmentValueChangedです。

 さて、まずはSwingの各UI部品を使ってアプリケーションの外観を決定します。今回のGUI部品のレイアウトの様子は図2のようになります。

図2:GUI部品のレイアウト
図2:GUI部品のレイアウト

 図のようなレイアウトを実現するために、次のようにコードを記述します。

GUI部品の構築
private void buildUI() {
    // 上部パネル
    // (レンダリングキャンパス+パラメータ編集パネル)の作成
    JPanel panelNorth = new JPanel();
    panelNorth.setLayout(new FlowLayout());
    panelNorth.add(createCanvas());
    panelNorth.add(createParamEditPanel());
    
    // カラー選択パネルの作成
    JPanel panelCenter = new JPanel();
    colorChooser = new JColorChooser();
    colorChooser.getSelectionModel().addChangeListener(this);
    panelCenter.add(colorChooser); 

    // 下部パネル(コード出力ボタン)の作成
    JPanel panelSouth = new JPanel();
    panelSouth.add(outputButton = new JButton("コード出力"));
    outputButton.addActionListener(this);
    
    // 上部パネルと下部パネルをフレームに追加
    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(panelNorth, BorderLayout.NORTH);
    getContentPane().add(panelCenter, BorderLayout.CENTER);
    getContentPane().add(panelSouth, BorderLayout.SOUTH);
}    

 まず、ベースとなるJFrameのコンテンツのレイアウトをBorderLayoutに設定し、NORTH("北"つまり上)、CENTER(中央)、SOUTH("南"つまり下)のそれぞれにJPanelを配置します。

 これによって、大きく分けて3つのパネルがアプリケーションに配置されます。

 中央のパネルには、色選択用のJColorChooserを1つ、下のパネルにはコード出力用のJButtonを1つ配置するだけですので、特に難しいことはありません。

 上側のパネルはレイアウトをFlowLayoutにし、左側にレンダリング結果を表示する3Dキャンバス(createCanvasメソッドで作成)を、右側にはパラメータを変更するためのボタンとスクロールバーを配置した別のパネル(createParamEditPanelメソッドで作成)を配置しています。パラメータ編集用のパネルは、次のようにGridLayoutを用いて6行2列の部品の配置を行っています。

GridLayoutを使用した部品の配置
private JPanel createParamEditPanel() {
    JPanel panel = new JPanel();
    panel.setPreferredSize(new Dimension(300, RENDER_CANVAS_SIZE));
    panel.setLayout(new GridLayout(6, 2, 10, 5));
    
    for(int i = 0; i < 6; i++) {
        // ラベルの追加
        panel.add(paramNameLabels[i]
            = new JLabel(labelNames[i], JLabel.RIGHT));
        
        if(i < 4) {
            // ボタンの追加
            paramButtons[i] = new JButton();
            panel.add(paramButtons[i]);
            paramButtons[i].addActionListener(this);
        } else {
            // スクロールバーの追加
            int barIndex = i - 4;
            paramScrollBars[barIndex]
              = new JScrollBar(JScrollBar.HORIZONTAL, 0, 1, 0, 100);
            paramScrollBars[barIndex].addAdjustmentListener(this);
            panel.add(paramScrollBars[barIndex]);
        }
    }

    return panel;
}

 ここではBorderLayoutFlowLayoutGridLayoutの3つのレイアウトが登場します。

 JavaのAPI仕様書を見ると、それぞれのレイアウト方法の特徴を理解できるでしょう。

アプリケーションのインターフェース

 色の編集は画面中央のJColorChooserを利用した色選択パネルで行います。

 JColorChooserは非常に便利なクラスで、色を選択するために次の3通りの方法を提供します。ユーザーはそれぞれから好きな方法で色を選択できます。

  • サンプルカラーからの選択
  • HSB値の指定
  • RGB値の指定

 ここで選択する色は、Ambient(環境光)、Diffuse(拡散光)、Specular(鏡面光)、Emission(放射光)の4つの光の色の1つに反映されます。対象となる光は、右上のラベルが青い四角の枠で囲まれています(起動時にはAmbient(環境光)が編集対象となっています)。

 この編集対象を変更するには、変更したい色のボタン(ラベルの右隣の四角)を押します。

 Shiniess(輝度)とTransparency(透明度)はスクロールバーで変更します。どちらも右に行くほど値が大きくなります。

Java3Dを用いてレンダリング結果を表示するキャンバス

 3Dオブジェクトを表示するためのキャンバスがCanvas3Dクラスです。Java3Dでは、まずCanvas3Dオブジェクトを作成し、SimpleUniverseクラスのコンストラクタに渡します。その後、SimpleUniverseクラスのaddBranchGraphメソッドを使用して3D要素を追加します。

 Canvas3Dオブジェクトの作成は、次のように行っています。

Canvas3Dの作成
private Canvas3D createCanvas() {
    GraphicsConfiguration config 
        = SimpleUniverse.getPreferredConfiguration();
    Canvas3D canvas = new Canvas3D(config);
    canvas.setSize(RENDER_CANVAS_SIZE, RENDER_CANVAS_SIZE);

    SimpleUniverse universe = new SimpleUniverse(canvas);
    universe.getViewingPlatform().setNominalViewingTransform();
    universe.addBranchGraph(createBranchGraph());
    return canvas;
}

 今回はcreateBranchGraphメソッドで、球体を1つ表示するだけの簡単なシーンを作成します。このシーンを表すBranchGroupには、次の要素を追加します。

  • 平行光源(Lightオブジェクト):右斜め上から球体を照らす白色光源
  • 環境光(Lightオブジェクト):球体全体を均一に照らす光源
  • 背景イメージ(Backgroundオブジェクト):球体の透明度を識別するために追加する黒と白のチェック柄のイメージ
  • 球体(Sphereオブジェクト):材質の設定結果を確認するための球体

 次のコードようにBranchGroupオブジェクトのaddChildメソッドを使用して、これらの要素を登録します。

Canvas3DとBranchGroupの追加
private BranchGroup createBranchGraph() {
    BranchGroup branchGroup = new BranchGroup();

    // 平行光源の追加
    branchGroup.addChild(createDirectionalLight());

    // 環境光の追加
    branchGroup.addChild(createAmbientLight());

    //背景イメージの追加
    branchGroup.addChild(createBackgroundImage());

    // レンダリング対象の球体を追加
    branchGroup.addChild(createSphere());
    
    branchGroup.compile();
    
    return branchGroup;
}

Sphereオブジェクトの作成

 Java3Dに準備されているSphereクラスを使用することで、簡単に球体の3Dモデルを作成できます。

 Sphereクラスには6種類ものコンストラクタがありますが、今回は半径、法線の設定の指定、分割数(値が大きいほど滑らかな球体になります)、外観(Appearance)の指定ができる次のコンストラクタを使用します。

Sphereクラスのコンストラクタ
Sphere(float radius, int primflags, int divisions, Appearance ap) 

 ここで指定するAppearanceが、Ambient(環境光)などに関係するMaterialと、Transparency(透明度)に関係するTransparencyAttributesを保持します。つまり、Sphereオブジェクトを作成する前に、各種設定を行ったAppearanceオブジェクトを準備しておく必要があります。

 そのままではAppearanceMaterialの属性は後から変更できないようになっているので、setCapabilityメソッドを使って、後から変更できるようにしておく必要があります。

 文章では少しわかりにくいでしょうが、次のコードを見ると内容がわかると思います。

Sphereオブジェクトの作成
private Sphere createSphere() {
    // Material の作成
    Material material = new Material();
    material.setCapability(Material.ALLOW_COMPONENT_READ);
    material.setCapability(Material.ALLOW_COMPONENT_WRITE);
    material.setLightingEnable(true);
    
    // TransparencyAttributes の作成
    TransparencyAttributes tansparency = new 
      TransparencyAttributes(TransparencyAttributes.BLENDED, 0.0f);
    tansparency
      .setCapability(TransparencyAttributes.ALLOW_VALUE_READ );
    tansparency
      .setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE );

    // Appearance の作成
    m_appearance = new Appearance();
    m_appearance.setCapability(Appearance.ALLOW_MATERIAL_READ);
    m_appearance.setCapability(Appearance.ALLOW_MATERIAL_WRITE);
    m_appearance.setCapability(
        Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_READ);    
    m_appearance.setCapability(
        Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE);

    // Appearance に Material を設定
    m_appearance.setMaterial(material);

    // Appearance に TransparencyAttributes を設定
    m_appearance.setTransparencyAttributes(tansparency);
    
    // Sphere の作成
    Sphere sphere = new 
        Sphere( 0.7f, Sphere.GENERATE_NORMALS, 128, m_appearance );
    sphere.getShape().setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
    sphere.getShape().setCapability(Shape3D.ALLOW_APPEARANCE_READ);
    
    return sphere;
}

材質設定用のコードを出力する

 各パラメータを変更してレンダリング結果の変化を見るだけでも楽しいですが、せっかくなのでここで編集した材質をJava3Dを用いたプログラムで使用できるように、Javaのコードを出力する機能を追加してみます。

 今回は、アプリケーションの下の方にある「コード出力」ボタンを押すと次のようなフレームが現れ、テキストエリアにコードが表示されるようにします。

図3:コードの出力
図3:コードの出力

 コードをマウスドラッグで選択して、[Ctrl]+Cボタンでクリップボードにコピーすれば、任意のテキストエディタにコピーすることができます。

 コードの出力は、テキストエリアに値を書き出すだけですので大したことは行っていませんが、DecimalFormatクラスを使用して小数点を含む値の出力形式を整えています。

 次のようにDecimalFormatクラスのコンストラクタに"0.0000"という文字列を指定すると、小数点以下4桁までを数字で出力するように設定できます。

DecimalFormatクラスのコンストラクタ
    DecimalFormat form = new DecimalFormat("0.0000");

 このコンストラクタで作成したオブジェクトのformatメソッドを使用すると、指定したフォーマットで値を文字列に変換できます。

 今回はJava言語で材質を設定するためのコードを出力するようにしましたが、必要に応じてC言語などでOpenGLやDirectXを使用するためのコードに変更してもよいでしょう。

材質を設定するコードの出力
private void outputCode() {
     Material material = m_appearance.getMaterial();
    Color3f color = new Color3f();
    
    JFrame frame = new JFrame("Code");
    frame.setBounds( 100, 100, 500, 250);
    JTextArea textArea = new JTextArea();
    textArea.setLineWrap(true);
    DecimalFormat form = new DecimalFormat("0.0000");
    
    textArea.append("Material material = new Material();\n");

    material.getAmbientColor(color);
    textArea.append("material.setAmbientColor("
                     + form.format(color.x) + "f, " 
                     + form.format(color.y) + "f, " 
                     + form.format(color.z) + "f);\n");

    // (中略)
    
    frame.add(textArea);
    frame.setVisible(true);
}

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
全コード

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

三谷 純(ミタニ ジュン)

Javaとの出会いは1996年にJDK1.0が登場した時までさかのぼります。それ以降、アプレットやスタンドアロンのアプリケーション、JSPを用いたサーバサイドのサービスや携帯電話で動くJavaアプリの開発など、広い範囲でJavaに関するプログラミングを行っています。拙著『独りで習うJava』は初めてJava...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/74 2006/10/10 14:53

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング