チューブを生成して描画

プログラムでチューブ(円筒)の座標を計算します。
NormalGenerator で法線ベクトルを設定します。

Java Applet のプログラムを実行します。
チューブを描画

前田稔の超初心者のプログラム入門

プログラムの作成

  1. プログラムでチューブ(円筒)の座標を計算します。
    メモ帳などでタイプして Tube.java の名前で保存して下さい。
    //★ プログラムで Tube を作成して描画する    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import javax.media.j3d.*;
    import javax.vecmath.*;
    import com.sun.j3d.utils.universe.*;
    import com.sun.j3d.utils.behaviors.mouse.*;
    
    // Main Class
    public class Tube extends JFrame
    {
        // main Method
        public static void main(String[] args)
        {   new Tube();  }
    
        // Constructor
        public Tube()
        {   // JFrame の初期化
            super("Tube test");
            setSize(300,300);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            // Java3D関係の設定
            GraphicsConfiguration g_config = SimpleUniverse.getPreferredConfiguration();
            Canvas3D canvas = new Canvas3D(g_config);
            add(canvas);
    
            // SimpleUniverseを生成
            SimpleUniverse universe = new SimpleUniverse(canvas);
            universe.getViewingPlatform().setNominalViewingTransform();
    
            // シーンの生成
            universe.addBranchGraph(CreateScene());
            setVisible(true);
        }
    
        // シーンを生成する
        private BranchGroup CreateScene()
        {   int i;
            BranchGroup objRoot = new BranchGroup();
    
            // 18角形のチューブの座標
            Point3d[] vect = new Point3d[18*4];     // 18角形*4角形ポリゴン
            for(i=0; i<18*4; i++)   vect[i] = new Point3d();
            for(i=0; i<18; i++)     RotY(20.0f*i, 0.5f, vect[i*4]);
            for(i=0; i<18; i++)
            {   vect[i*4+1].y = -0.5f;
                vect[i*4+2].y = 0.5f;
                vect[i*4+3].y = 0.5f;
            }
    
            for(i=0; i<18; i++)
            {   vect[i*4+3].x = vect[i*4].x;
                vect[i*4+3].z = vect[i*4].z;
                if (i<17)
                {   vect[i*4+2].x = vect[i*4+4].x;
                    vect[i*4+2].z = vect[i*4+4].z;
                }
                else
                {   vect[i*4+2].x = vect[0].x;
                    vect[i*4+2].z = vect[0].z;
                }
                vect[i*4+1].x = vect[i*4+2].x;
                vect[i*4+1].z = vect[i*4+2].z;
            }
    
            // Mouse 操作の設定
            TransformGroup trans = new TransformGroup();
            SetMouse(objRoot, trans);
    
            // チューブを作成
            QuadArray geometry = new QuadArray(vect.length, GeometryArray.COORDINATES);
            geometry.setCoordinates(0, vect);
    
            // カリングしない
            PolygonAttributes pattr = new PolygonAttributes();
            pattr.setPolygonMode(PolygonAttributes.POLYGON_FILL);
            pattr.setCullFace(PolygonAttributes.CULL_NONE); //両面を描画
    
            // Appearance の設定
            Appearance aleft = new Appearance();
            aleft.setPolygonAttributes(pattr);
            Shape3D shape = new Shape3D(geometry, aleft);
    
            // BranchGroup に登録
            trans.addChild(shape);
            objRoot.addChild(trans);
            return objRoot;
        }
    
        // Y軸で回転計算(rt=度)、中心(0,0)、半径(len)
        public void RotY(float rt, float len, Point3d vect)
        {   vect.x = (float)(Math.sin(rt / 180 * Math.PI)) * len;
            vect.z = (float)(Math.cos(rt / 180 * Math.PI)) * len;
            vect.y = -0.5;
        }
    
        // Mouse 操作の設定
        public void SetMouse(BranchGroup objRoot, TransformGroup trans)
        {
            // Model の修正を許可
            trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
            trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    
            // 回転を設定
            BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
            MouseRotate rotator = new MouseRotate(trans);
            rotator.setSchedulingBounds(bounds);
            objRoot.addChild(rotator);
    
            // 移動を設定
            MouseTranslate translator = new MouseTranslate(trans);
            translator.setSchedulingBounds(bounds);
            objRoot.addChild(translator);
    
            // ズームを設定
            MouseZoom zoomer = new MouseZoom(trans);
            zoomer.setSchedulingBounds(bounds);
            objRoot.addChild(zoomer);
        }
    }
    
  2. ソースプログラムをコンパイルして class オブジェクトを実行して下さい。
    チューブがシルエット(白色)で描画されます。
    マウスの左ボタンを押しながらドラッグするとモデルが回転します。
    マウスの右ボタンを押しながらドラッグするとモデルが移動します。
    マウスの中央ボタンを押しながらドラッグするとモデルがズームイン・ズームアウトします。

プログラムの説明

  1. このプログラムは CreateScene() メソッド以外は Point.java と同じ要領です。
    基本的な説明は Java 3D で点を描画 を参照して下さい。
    またマウスでモデルを操作する方法は 四面体を描画 を参照して下さい。
  2. プログラムでチューブ(円筒)の座標を計算してモデルを作成する CreateScene() メソッドです。
    チューブは4角形ポリゴンを18枚組み合わせて構成します。
    もっと多い方が滑らかになるのですが、これぐらいで十分でしょう。
    Point3d[18*4]; が頂点座標の領域です。
    回転座標を求める専用のメソッド RotY() を使って頂点座標を計算します。
    解りやすいように、4枚一組(4角形ポリゴン)を使っていますが、効率を考えるなら TriangleStripArray の方がお勧めです。
    Y座標がチューブの高さで -0.5 と 0.5 に設定します。
    Y軸を基点に、X座標とZ座標に回転座標を求めます。
        // シーンを生成する
        private BranchGroup CreateScene()
        {   int i;
            BranchGroup objRoot = new BranchGroup();
    
            // 18角形のチューブの座標
            Point3d[] vect = new Point3d[18*4];     // 18角形*4角形ポリゴン
            for(i=0; i<18*4; i++)   vect[i] = new Point3d();
            for(i=0; i<18; i++)     RotY(20.0f*i, 0.5f, vect[i*4]);
            for(i=0; i<18; i++)
            {   vect[i*4+1].y = -0.5f;
                vect[i*4+2].y = 0.5f;
                vect[i*4+3].y = 0.5f;
            }
    
            for(i=0; i<18; i++)
            {   vect[i*4+3].x = vect[i*4].x;
                vect[i*4+3].z = vect[i*4].z;
                if (i<17)
                {   vect[i*4+2].x = vect[i*4+4].x;
                    vect[i*4+2].z = vect[i*4+4].z;
                }
                else
                {   vect[i*4+2].x = vect[0].x;
                    vect[i*4+2].z = vect[0].z;
                }
                vect[i*4+1].x = vect[i*4+2].x;
                vect[i*4+1].z = vect[i*4+2].z;
            }
        
  3. 座標の設定が終われば、後は前回と同じ要領です。
    ただカリングすると外側しか描画されないので「両面描画」に設定しています。
            // カリングしない
            PolygonAttributes pattr = new PolygonAttributes();
            pattr.setPolygonMode(PolygonAttributes.POLYGON_FILL);
            pattr.setCullFace(PolygonAttributes.CULL_NONE); //両面を描画
    
            // Appearance の設定
            Appearance aleft = new Appearance();
            aleft.setPolygonAttributes(pattr);
            Shape3D shape = new Shape3D(geometry, aleft);
        
  4. Y軸を基点にして回転座標を計算する RotY() メソッドです。
    rt が回転角度で len が半径です。
    vect に回転座標を格納します。
    Y座標は、とりあえず -0.5 に設定しています。
        // Y軸で回転計算(rt=度)、中心(0,0)、半径(len)
        public void RotY(float rt, float len, Point3d vect)
        {   vect.x = (float)(Math.sin(rt / 180 * Math.PI)) * len;
            vect.z = (float)(Math.cos(rt / 180 * Math.PI)) * len;
            vect.y = -0.5;
        }
        

法線ベクトルを設定

  1. チューブ(円筒)が描画出来たのは良いのですが、シルエット(白色)だけでは立体に見えません。
    立体的に見せるには、法線ベクトルを設定して、ライトで照らして「光と影」を演出します。
    詳しい説明は 四面体を描画 を参照して下さい。
  2. メモ帳などでタイプして TubeNormal.java の名前で保存して下さい。
    //★ TriangleStripArray で Tube を作成して法線を設定する    前田 稔
    import java.awt.*;
    import javax.swing.*;
    import javax.media.j3d.*;
    import javax.vecmath.*;
    import com.sun.j3d.utils.universe.*;
    import com.sun.j3d.utils.behaviors.mouse.*;
    import com.sun.j3d.utils.geometry.*;
    
    // Main Class
    public class TubeNormal extends JFrame
    {
        int     KAKU = 6;       // チューブの角数
        int     VNUM = KAKU+1;  // 頂点座標の数
    
        // main Method
        public static void main(String[] args)
        {   new TubeNormal();  }
    
        // Constructor
        public TubeNormal()
        {   // JFrame の初期化
            super("Tube Test");
            setSize(300,300);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            // Java3D関係の設定
            GraphicsConfiguration g_config = SimpleUniverse.getPreferredConfiguration();
            Canvas3D canvas = new Canvas3D(g_config);
            add(canvas);
    
            // SimpleUniverseを生成
            SimpleUniverse universe = new SimpleUniverse(canvas);
            universe.getViewingPlatform().setNominalViewingTransform();
    
            // シーンの生成
            universe.addBranchGraph(CreateScene());
            setVisible(true);
        }
    
        // シーンを生成する
        private BranchGroup CreateScene()
        {   int i;
            BranchGroup objRoot = new BranchGroup();
    
            // チューブの座標
            Point3d[] vect = new Point3d[VNUM*2];
            for(i=0; i<VNUM*2; i++) vect[i] = new Point3d();
            for(i=0; i<VNUM; i++)
            {   RotY(360.0f/KAKU*i, 0.5f, vect[i*2+1]);
                vect[i*2].x = vect[i*2+1].x;
                vect[i*2].z = vect[i*2+1].z;
                vect[i*2].y = 0.5f;
            }
            int[]   VertexCounts = { VNUM*2 };
    
            // 平行光源の設定
            BoundingSphere bounds = new BoundingSphere(new Point3d(),100.0);
            DirectionalLight dlight =
                new DirectionalLight(true, new Color3f(1.0f,1.0f,1.0f), new Vector3f(0.3f,-0.3f,-0.3f));
            dlight.setInfluencingBounds(bounds);
            objRoot.addChild(dlight);
    
            // 環境光の設定
            AmbientLight alight = new AmbientLight();
            alight.setInfluencingBounds(bounds);
            objRoot.addChild(alight);
    
            // Mouse 操作の設定
            TransformGroup trans = new TransformGroup();
            SetMouse(objRoot, trans);
    
            // チューブを作成
            TriangleStripArray geometry = new TriangleStripArray(
              vect.length, GeometryArray.COORDINATES, VertexCounts);
            geometry.setCoordinates(0, vect);
    
            // 法線ベクトルの設定
            GeometryInfo ginfo = new GeometryInfo(geometry);
            NormalGenerator gen = new NormalGenerator();
            gen.generateNormals(ginfo);
    
            // Material を設定して BranchGroup に追加
            Shape3D shape = new Shape3D(ginfo.getGeometryArray());
            shape.setAppearance(createAppearance());
    
            // BranchGroup に登録
            trans.addChild(shape);
            objRoot.addChild(trans);
            return objRoot;
        }
    
        // Y軸で回転計算(rt=度)、中心(0,0)、半径(len)
        public void RotY(float rt, float len, Point3d vect)
        {   vect.x = (float)(Math.sin(rt / 180 * Math.PI)) * len;
            vect.z = (float)(Math.cos(rt / 180 * Math.PI)) * len;
            vect.y = -0.5;
        }
    
        // Material の設定
        private Appearance createAppearance()
        {
            Appearance ap = new Appearance();
            // Material の設定
            Material mat = new Material();
            mat.setDiffuseColor( new Color3f(0.8f, 0.8f, 0.0f) );
            ap.setMaterial(mat);
    
            // カリングしない
            PolygonAttributes pattr = new PolygonAttributes();
            pattr.setPolygonMode(PolygonAttributes.POLYGON_FILL);
            pattr.setCullFace(PolygonAttributes.CULL_NONE); //両面を描画
            //pattr.setCullFace(PolygonAttributes.CULL_BACK); //裏面をカリング
            ap.setPolygonAttributes(pattr);
            return ap;
        }
    
        // Mouse 操作の設定
        public void SetMouse(BranchGroup objRoot, TransformGroup trans)
        {
            // Model の修正を許可
            trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
            trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    
            // 回転を設定
            BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
            MouseRotate rotator = new MouseRotate(trans);
            rotator.setSchedulingBounds(bounds);
            objRoot.addChild(rotator);
    
            // 移動を設定
            MouseTranslate translator = new MouseTranslate(trans);
            translator.setSchedulingBounds(bounds);
            objRoot.addChild(translator);
    
            // ズームを設定
            MouseZoom zoomer = new MouseZoom(trans);
            zoomer.setSchedulingBounds(bounds);
            objRoot.addChild(zoomer);
        }
    }
    
  3. 大域領域で KAKU と VNUM を定義します。
    KAKU の初期値は6なので、6角形のチューブが作成されます。
    円筒にするには、16以上の値を設定して下さい。
        public class TubeNormal extends JFrame
        {
            int     KAKU = 6;       // チューブの角数
            int     VNUM = KAKU+1;  // 頂点座標の数
        
  4. シーンを生成する CreateScene() メソッドです。
    今回は TriangleStripArray を使って三角形ポリゴンを組み合わせて作成しました。
        // シーンを生成する
        private BranchGroup CreateScene()
        {   int i;
            BranchGroup objRoot = new BranchGroup();
    
            // チューブの座標
            Point3d[] vect = new Point3d[VNUM*2];
            for(i=0; i<VNUM*2; i++) vect[i] = new Point3d();
            for(i=0; i<VNUM; i++)
            {   RotY(360.0f/KAKU*i, 0.5f, vect[i*2+1]);
                vect[i*2].x = vect[i*2+1].x;
                vect[i*2].z = vect[i*2+1].z;
                vect[i*2].y = 0.5f;
            }
            int[]   VertexCounts = { VNUM*2 };
        
  5. DirectionalLight() で平行光源を設定します。
    AmbientLight() で環境光を設定します。
    SetMouse() メソッドでマウスの操作を設定します。
    説明は 四面体を描画 を参照して下さい。
            // 平行光源の設定
            BoundingSphere bounds = new BoundingSphere(new Point3d(),100.0);
            DirectionalLight dlight =
                new DirectionalLight(true, new Color3f(1.0f,1.0f,1.0f), new Vector3f(0.3f,-0.3f,-0.3f));
            dlight.setInfluencingBounds(bounds);
            objRoot.addChild(dlight);
    
            // 環境光の設定
            AmbientLight alight = new AmbientLight();
            alight.setInfluencingBounds(bounds);
            objRoot.addChild(alight);
    
            // Mouse 操作の設定
            TransformGroup trans = new TransformGroup();
            SetMouse(objRoot, trans);
        
  6. TriangleStripArray で三角形ポリゴンを組み合わせてチューブを作成します。
    次に GeometryInfo を取得して法線ベクトルを設定します。
    法線ベクトルの設定は NormalGenerator を生成して generateNormals(ginfo); を実行するだけです。
            // チューブを作成
            TriangleStripArray geometry = new TriangleStripArray(
              vect.length, GeometryArray.COORDINATES, VertexCounts);
            geometry.setCoordinates(0, vect);
    
            // 法線ベクトルの設定
            GeometryInfo ginfo = new GeometryInfo(geometry);
            NormalGenerator gen = new NormalGenerator();
            gen.generateNormals(ginfo);
        
  7. createAppearance() メソッドでマテリアルとカリングの設定を行います。
    DiffuseColor を黄色にして、ポリゴンの両面を描画しています。
        // Material の設定
        private Appearance createAppearance()
        {
            Appearance ap = new Appearance();
            // Material の設定
            Material mat = new Material();
            mat.setDiffuseColor( new Color3f(0.8f, 0.8f, 0.0f) );
            ap.setMaterial(mat);
    
            // カリングしない
            PolygonAttributes pattr = new PolygonAttributes();
            pattr.setPolygonMode(PolygonAttributes.POLYGON_FILL);
            pattr.setCullFace(PolygonAttributes.CULL_NONE); //両面を描画
            //pattr.setCullFace(PolygonAttributes.CULL_BACK); //裏面をカリング
            ap.setPolygonAttributes(pattr);
            return ap;
        }
        
  8. 回転座標を求める RotY() メソッドとマウスで操作する SetMouse() は前回と同じです。

超初心者のプログラム入門(Java2)