質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

Q&A

解決済

1回答

6577閲覧

法線ベクトルについて

windowsaa

総合スコア16

OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

0グッド

0クリップ

投稿2017/11/09 08:16

これは三角形に法線ベクトルをせっていするプログラムですが
この三角刑をたくさん描画する、STLのポリゴンを表示するときに
法線ベクトルが重なることがあります

その法線ベクトルを平均化するにはどうしたらいいのでしょうか?

C++

1#include <iostream> 2#include <vector> 3#include <numeric> 4#include <algorithm> 5#include <functional> 6#include <cmath> 7#include <GL/glut.h> 8 9 10//--------- 各種データ構造 -----------// 11float a[3]={0,0,3.5}; 12float b[3]={3.5,0,0}; 13float c[3]={0,3.5,0}; 14float v1[3];// b - a を格納 15float v2[3];// c - a を格納 16 17std::vector<float> FaceNormal(3);//外せき格納用+後で正規化して法線にする 18 19 20//--------- プロトタイプ宣言 -----------// 21void display(); 22void reshape(int w, int h); 23void timer(int value); 24 25void DRAW_XYZ(); 26void DrawPolygon(); 27void CalcNormal(); 28 29 30//------------- OpenGLの初期化 -------------// 31void GLUT_CALL_FUNCs() 32{ 33 glutDisplayFunc(display); 34 glutReshapeFunc(reshape); 35 glutTimerFunc(0,timer,17); 36} 37 38 39void OtherMyInit() 40{ 41 glClearColor(1.0, 1.0, 1.0, 1.0); 42 glEnable(GL_DEPTH_TEST); 43 glEnable(GL_LIGHTING);//光源の有効化 44 glEnable(GL_LIGHT0); 45 46 glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);//glColorにより材質設定 47 glEnable(GL_COLOR_MATERIAL); 48 49 glEnable(GL_NORMALIZE); 50} 51 52void GLUT_INITs(int *argcp, char **argv) 53{ 54 glutInit(argcp,argv); 55 glutInitDisplayMode(GLUT_RGBA| GLUT_DOUBLE | GLUT_DEPTH); 56 glutInitWindowSize(640,480); 57 glutCreateWindow("Basic Normal"); 58 GLUT_CALL_FUNCs(); 59 OtherMyInit(); 60 61} 62 63 64 65//------------- メイン関数 ----------------// 66int main(int argc , char **argv) 67{ 68 GLUT_INITs(&argc,argv); 69 70 CalcNormal(); //法線を計算 71 glutMainLoop(); 72 73 return 0; 74} 75 76 77//------------- ここから各種コールバック -----------------// 78void display() 79{ 80 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 81 glLoadIdentity(); 82 83 gluLookAt(-5.0, 20.0, 20.0, 0,0,0, 0.0, 1.0, 0.0); 84 85 86 static float Light0Pos[]={-5.0, 5.0, 5.0,0}; //光源の位置 87 static int r=0; 88 glPushMatrix(); 89 glRotatef(static_cast<float>(r),0,1,0); 90 glLightfv(GL_LIGHT0, GL_POSITION, Light0Pos);//位置だけ設定(あとはデフォルト) 91 glPopMatrix(); 92 93 DRAW_XYZ(); 94 95 96 glColor3f(0,1,1); 97 glutSolidSphere(1,60,60); 98 DrawPolygon(); 99 100 glutSwapBuffers(); 101 if(++r > 360) r = 0; 102 103} 104 105void reshape(int w, int h) 106{ 107 glViewport(0, 0, w, h); 108 glMatrixMode(GL_PROJECTION); 109 glLoadIdentity(); 110 gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0); 111 glMatrixMode(GL_MODELVIEW); 112} 113 114 115 116void timer(int t) 117{ 118 glutPostRedisplay(); 119 glutTimerFunc(t,timer,17); //タイマー関数 120} 121 122 123//-------------- ここから各種関数 ------------------// 124void DRAW_XYZ() 125{ 126 glDisable(GL_LIGHTING);//光源の無効化 127 glDisable(GL_LIGHT0); 128 129 glBegin(GL_LINES); 130 glColor3f(0,1,0);//x 131 glVertex2f(-100,0); 132 glVertex2f(100, 0); 133 134 glColor3f(1,0,0);//y 135 glVertex2f(0,0); 136 glVertex2f(0,100); 137 138 glColor3f(0,0,1);//z 139 glVertex3f(0,0,-100); 140 glVertex3f(0,0, 100); 141 glEnd(); 142 143 glEnable(GL_LIGHTING);//光源の有効化 144 glEnable(GL_LIGHT0); 145 146} 147 148void DrawPolygon() 149{ 150 151 glNormal3fv(&FaceNormal[0]); 152 glBegin(GL_TRIANGLES); 153 glVertex3fv(a); 154 glVertex3fv(b); 155 glVertex3fv(c); 156 glEnd(); 157 158 159} 160 161//法線を計算する 162void CalcNormal() 163{ 164 //ベクトルを計算 165 std::transform(b,b+3,a,v1,std::minus<float>());//v1 = b - a 166 std::transform(c,c+3,a,v2,std::minus<float>());//v2 = c - a 167 168 //外せきを計算(v1 x v2) 169 for(int loop = 0;loop < 3; ++loop) 170 { 171 FaceNormal[loop]=v1[(loop+1)%3] * v2[(loop+2)%3] - v1[(loop+2)%3]*v2[(loop+1)%3]; 172 } 173 174 //正規化用に大きさ|v1 x v2| を計算 175 float length = sqrtf( std::inner_product(FaceNormal.begin(),FaceNormal.end(),FaceNormal.begin(),0.f)); 176 if(length == 0.f) //長さが0の場合は計算できない 177 { 178 std::cerr << "Can't Calc normal\n"; 179 return ; 180 } 181 182 183 //各要素をlengthで割って正規化 184 std::transform(FaceNormal.begin(),FaceNormal.end(),FaceNormal.begin(),std::bind2nd(std::divides<float>(),length)); 185 186 std::cout << "Normal is : "; 187 for(int loop = 0;loop < 3; ++loop) 188 { 189 std::cout << FaceNormal[loop]<<", "; 190 } 191 std::cout <<'\n'; 192}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masaya_ohashi

2017/11/10 00:38

法線ベクトルが重なる、といっているのはどういう状態を指しますか?また、平均化とはなにとなにの平均ですか?
guest

回答1

0

ベストアンサー

法線の平均化のためには「頂点単位の法線」を求めなければなりません。頂点法線ベクトルの求め方は以下のページが参考になります。
http://sky.geocities.jp/freakish_osprey/opengl/opengl_normalvecotr.htm

頂点法線ベクトルを求める

基本は面法線ベクトル。
面法線を元に頂点法線を求めます。
ある頂点の頂点法線を求める場合に、
おおざっぱに言ってどのように計算していくかというと、

頂点法線を求める頂点を共有している面をリストアップする。
リストアップした面の面法線を足しあわせて規格化(=平均化)したものが、頂点法線。
という具合です。
各面の面法線が求められていて、頂点を共有する面のリストアップさえできてしまえば、
後は、各面の面法線を足しあわせて規格化するだけなので、
たいして難しいことはないです。

共有する面のリストアップについては、「そもそもバラバラの頂点を3つ使ってポリゴンを作っている」という時点で難しいです。例えば以下のように、xy各座標が1の四角形があったとします。

C++

1// 左上の三角 2float v0_0[3] = {0, 0, 0}; // 左上 3float v0_1[3] = {1, 0, 0}; // 右上 4float v0_2[3] = {0, 1, 0}; // 左下 5// 右下の三角 6float v1_0[3] = {1, 0, 0}; // 右上 7float v1_1[3] = {1, 1, 0}; // 右下 8float v1_2[3] = {0, 1, 0}; // 左下

このとき、頂点を共有しているのは同じ右上であるv0_1v1_0、および左下であるv0_2v1_2ですね?しかし、これでは同一性が座標を見比べることでしかわかりません。これではリストアップとは言えません。

このようなすべてのポリゴンごとに頂点がバラバラに定義されているデータと違い、もっときれいに「共有している頂点」を書く方法として頂点インデックス方式というものがあります。この方法であれば頂点法線の計算もわかりやすくなります。例えば先程の例の四角形を頂点インデックス方式で書いてみましょう。

C++

1float v[4][3] = { 2 {0, 0, 0}, // 左上 3 {1, 0, 0}, // 右上 4 {0, 1, 0}, // 左下 5 {1, 1, 0}, // 右下 6}; 7int index[2][3] = { 8 {0, 1, 2}, // 左上の三角 9 {1, 3, 2}, // 右下の三角 10}; 11for(int i=0;i<2;i++) { 12 glBegin(GL_TRIANGLES); 13 glVertex3fv(v[index[i][0]]); 14 glVertex3fv(v[index[i][1]]); 15 glVertex3fv(v[index[i][2]]); 16 glEnd(); 17}

indexという変数はvの「何番目」の頂点を使うかという数値が入っています。これを使うことで同じ座標にある頂点を再利用しているわけです。
あとは計算ですが、頂点インデックスがあるなら話は簡単です。

C++

1float n[4][3] = {0}; // 頂点の数だけ頂点法線用変数を用意 2 3for(int i=0;i<2;i++) { 4 float temp[3] = {0, 0, 0}; // ここは本来面法線の計算結果 5 6 // 頂点法線用変数に面法線を加算 7 n[index[i]][0] += temp[0]; 8 n[index[i]][1] += temp[1]; 9 n[index[i]][2] += temp[2]; 10} 11 12// 頂点法線の正規化 13for(int i=0;i<4;i++) { 14 float length = sqrtf((n[i][0] * n[i][0]) + (n[i][1] * n[i][1]) + (n[i][2] * n[i][2])); 15 n[i][0] /= length; 16 n[i][1] /= length; 17 n[i][2] /= length; 18}

C++

1for(int i=0;i<2;i++) { 2 glBegin(GL_TRIANGLES); 3 4 // 頂点ごとに法線も設定する 5 glNormal3fv(n[index[i][0]]); 6 glVertex3fv(v[index[i][0]]); 7 8 glNormal3fv(n[index[i][1]]); 9 glVertex3fv(v[index[i][1]]); 10 11 glNormal3fv(n[index[i][2]]); 12 glVertex3fv(v[index[i][2]]); 13 glEnd(); 14}

投稿2017/11/10 01:43

編集2017/11/13 00:32
masaya_ohashi

総合スコア9206

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

windowsaa

2017/11/12 16:54

頂点法線を求める頂点を共有している面をリストアップの仕方がわかりません おしえてください
masaya_ohashi

2017/11/13 00:33

普通「データ定義」の時点で共通している頂点がわかっているので、後から「どこが頂点を共有しているか」という探し方はしません。その点も含めて追記しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問