Javaプログラミング

トップページ      |      目次
←前へ      次へ→

  • 隠面処理(法線ベクトル法)
  •  

    • ワイヤフレーム表示と隠面処理表示
    • ワイヤフレーム表示は3次元形状を構成する頂点を結んだ稜線だけを表示する形式である。
      この方法は頂点同士の接続関係の把握のしやすさや表示が高速に行えるなどの利点があるが、
      本来見えるはずのないが見えてしまうことにより、表示が煩雑になってしまい面の位置関係の把握や立体の内部と外部の区別がつきにくいなどの問題点もある。
      立体の表示において視点から本来見えない面を消去する処理を隠面処理という。 さらに陰影付けをすることでより立体的な表示となる。
      実行結果 実行結果 実行結果
      ワイヤフレーム表示 隠面処理表示 隠面処理+陰影付け
       
    • 法線ベクトル法
    • 隠面処理において見えない面を消去するにあたり、物体の表面について考えると物体の外側を向いた面だけを表示すれば良い。そして視点から見て反対方向を向いている面は不可視となるため表示は不要となる。
      法線ベクトル法はこのような考えに基づいて隠面処理を行う。すなわち視点から見て裏側となる面を削除、もしくは表示しない方法である。そのため法線ベクトル法はバックフェイスカリング(back-face culling)とも呼ばれる。 実行結果
      法線ベクトル法の概要
       
    • 表の面と裏の面の判別方法
    • 表の面と裏の面は、面の法線ベクトルと視点と面を結ぶベクトル(視点方向ベクトル)との内積によって判別することができる。 面の法線ベクトルを\(\mathbf{N}\)、視点方向ベクトルを\(\mathbf{V}\)とすると、表の面と裏の面の判別式\(D_f\)は以下のようになる。 \[ D_f=\mathbf{N} \cdot \mathbf{V} \]
      • \(D_f>0\Rightarrow\) 表の面
      • \(D_f<0\Rightarrow\) 裏の面
      • \(D_f=0\Rightarrow\) 視線と平行
      実行結果  
    • 法線ベクトルの導出
    • 法線ベクトルは3次元形状を構成する面の頂点\(p_i\)を用いて導出できる。
      ここでは形状を構成する面を三角形と仮定して法線の導出を行う。
      三角形を構成する頂点\(p_0,p_1,p_2\)は面の表側からみて反時計回りに定義されているとする。
      右手系の場合、時計回りに定義することで法線は面の表側に向かう。つまり法線ベクトルの向いている方向が面の表になる。 実行結果 ここで\(p_0,p_1\)からなるベクトル\(\mathbf{V}_1\),\(p_0,p_2\)からなるベクトル\(\mathbf{V}_2\)を導入すると法線ベクトル\(\mathbf{N}\)は\(\mathbf{V}_1\)と\(\mathbf{V}_2\)の外積で表される。
      すなわち \[ \mathbf{N}=\mathbf{V}_1\times\mathbf{V}_2 \] ただし \[ \mathbf{V}_1=p_1-p_0\\ \mathbf{V}_2=p_2-p_0 \] 従って、法線ベクトル\(\mathbf{N}\)の\(x,y,z\)の各成分\(n_x,n_y,n_z\)は以下のようになる \[ n_x=\mathbf{V}_1y\mathbf{V}_2z-\mathbf{V}_1z\mathbf{V}_2y\\ n_y=\mathbf{V}_1x\mathbf{V}_2z-\mathbf{V}_1z\mathbf{V}_2x\\ n_z=\mathbf{V}_1y\mathbf{V}_2x-\mathbf{V}_1x\mathbf{V}_2y \] もしくは \[ n_x=(y_1-y_0)(z_2-z_0)-(z_1-z_0)(y_2-y_0)\\ n_y=(x_1-x_0)(z_2-z_0)-(z_1-z_0)(x_2-x_0)\\ n_z=(y_1-y_0)(x_2-x_0)-(x_1-x_0)(y_2-y_0)\\ \] 単位法線ベクトル\(\mathbf{\hat{N}}\)は求めた法線ベクトルで正規化することで次のように求められる。 \[ \mathbf{\hat{N}}=\frac{\mathbf{N}}{|\mathbf{N}|} =\frac{\mathbf{N}}{\sqrt{n_x^2+n_y^2+n_z^2}} \] 今回は三角形の面を例に法線ベクトルを導出したが、四角形以上の多角形に対しても任意の3点を選ぶことで同様に導出することができる。

      コード(法線ベクトルの導出)
      
      public void normal(){
          normal=new double[faces.length][3];
          double[] v1=new double[3];
          double[] v2=new double[3];
      
          for(int i=0;i<faces.length;i++) {
              for(int j=0;j<3;j++) {
                  v1[j]=vertices[faces[i][0]][j]-vertices[faces[i][1]][j];
                  v2[j]=vertices[faces[i][0]][j]-vertices[faces[i][2]][j];
              }
      
              //外積の計算
              normal[i][0]=v1[2]*v2[1]-v2[2]*v1[1];
              normal[i][1]=v1[0]*v2[2]-v2[0]*v1[2];
              normal[i][2]=v1[1]*v2[0]-v2[1]*v1[0];
      
              //ノルムの計算(法線ベクトルの大きさ)
              double norm=Math.sqrt(
                      normal[i][0]*normal[i][0]+
                      normal[i][1]*normal[i][1]+
                      normal[i][2]*normal[i][2]
                      );
              //単位法線ベクトルの計算
              for(int j=0;j<3;j++)
                  normal[i][j]/=norm;
      
          }
      }
      
    • プログラムによる実装
    • 以上のことをプログラムにて実装を行う。おおまかな処理の流れは以下の通りである。
      • 法線ベクトルの計算(今回は予め計算されているものとする)
      • 視点ベクトルの導出
      • 法線ベクトルと視点方向ベクトルの内積の計算
      • 面の表と裏の判別をして、表なら表示する
      なおプログラムでは、隠面処理に加えて法線ベクトルと視点方向ベクトルの内積を利用した陰影付けも行った。 考え方としては視点に光源があると仮定して、内積の値が最大になる時は視点の正面になり、角度が付くに従い減少していき、視点の反対方向になるとき最小になることを利用している。

      コード(法線ベクトル法による隠面処理)
      
      public void backFaceCulling(Graphics g,Cube cube) {
      
          g.setColor(Color.white);
          g.fillRect(0, 0, screenW, screenH);
      
          double[] p=new double[3];
          for(int i=0;i<cube.faces.length;i++) {
              int[] px=new int[cube.faces[i].length];
              int[] py=new int[cube.faces[i].length];
              for(int j=0;j<cube.faces[i].length;j++) {
                  p=cube.vertices[cube.faces[i][j]];
                  px[j]=(int)p[0];
                  py[j]=(int)p[1];
              }
      
              double[] n=new double[3];
              //法線ベクトルの取得
              n=cube.normal[i];
              //視点方向ベクトルの導出
              double[] vv= {
                      p[0]-vp[0],
                      p[1]-vp[1],
                      p[2]-vp[2],
              };
              
              //法線ベクトルと視点方向ベクトルの内積の計算
              double dot=n[0]*vv[0]+n[1]*vv[1]+n[2]*vv[2];
              
              //法線ベクトルと視点方向ベクトルの内積を使って色(明るさ)を決定
              Color c=new Color(
                      (int)clip(dot/vp[2]*255,0,255),
                      (int)clip(dot/vp[2]*64,0,255),
                      (int)clip(dot/vp[2]*168,0,255)
                      );
      
              //面が表なら描画
              if(dot>0) {
                  g.setColor(c);
                  g.fillPolygon(px, py, cube.faces[i].length	);
                  g.setColor(Color.black);
                  g.drawPolygon(px, py, cube.faces[i].length);
              }
      
          }
      }
      
        実行結果
        実行結果
        サンプルプログラム(実行可能JARファイル)
        サンプルコード


      ←前へ      次へ→