• 締切済み

C++ 法線の計算

Depthmapをもつ画像のピクセルごとに法線を計算する関数を作成したのですが、いまいち計算が遅いです。if文をできるだけ使わないようにしたり、自分なりに変更したのですが、どなたかアドバイスを頂けないでしょうか。 Visual Studio, windows7, C++ です。 Tvector3<float> calc_normal(Tvector3<float> p1, Tvector3<float> p2, Tvector3<float> p3){ Tvector3<float> v1; Tvector3<float> v2; Tvector3<float> cross; float length; Tvector3<float> n; v1 = p1 - p2; v2 = p3 - p2; cross = v2.cross(v1); /* 外積v2×v1の長さ|v2×v1|(= length)を求める */ cross.normalize(); /* 長さ|v2×v1|が0のときは法線ベクトルは求められない */ if (length == 0.0f) { //cout<<"lenth=0"<<endl; //break (); } /* 外積v2×v1を長さ|v2×v1|で割って法線ベクトルnを求める */ //cout<<cross<<endl; return cross; } void calc_all_noraml(Ttexturef dmap, vector<Tvector3<float>> &n){ int w = dmap.getWidth(); int h = dmap.getHeight(); Tvector3<float> pc,pl,pr,pd,pu; int j=0; cout<<j<<endl; for(int i=0; i<w; i++){ vector<Tvector3<float>> temp; pc = Tvector3<float>(i,j,return_depth(dmap,i,j)); if(i-1 > 0)pl = Tvector3<float>(i-1,j,dmap.get(i-1,j).r); if(i+1 < w)pr = Tvector3<float>(i+1,j,dmap.get(i+1,j).r); pu = Tvector3<float>(i,j+1,dmap.get(i,j+1).r); if(i-1 > 0)temp.push_back(calc_normal(pl,pu,pc)); if(i+1 < w)temp.push_back(calc_normal(pc,pu,pr)); Tvector3<float> sum(0,0,0); for(int k=0; k<(int)temp.size(); k++){ sum += temp.at(k); } sum.normalize(); //cout<<sum<<endl; n.push_back(sum); } for(j=1;j<(h-1);j++){ cout<<j<<endl; int i=0; vector<Tvector3<float>> temp; pc = Tvector3<float>(i,j,return_depth(dmap,i,j)); pr = Tvector3<float>(i+1,j,return_depth(dmap,i+1,j)); pu = Tvector3<float>(i,j+1,return_depth(dmap,i,j+1)); pd = Tvector3<float>(i,j-1,return_depth(dmap,i,j-1)); temp.push_back(calc_normal(pc,pu,pr)); temp.push_back(calc_normal(pc,pr,pd)); Tvector3<float> sum(0,0,0); for(int k=0; k<(int)temp.size(); k++){ sum += temp.at(k); } sum.normalize(); n.push_back(sum); temp.clear(); for(int i=1; i<(w-1); i++){ Tvec

みんなの回答

noname#137556
noname#137556
回答No.3

> 一般的にfloatよりdoubleのほうが早いのでしょうか? 一概にどちらが早いとは言えないようです。 ↓実際に試した方のブログ。 http://lazyattribute.sakuratan.com/la/?p=1302

larry0708
質問者

お礼

ありがとうございます。なかなか奥が深そうですが頑張ります!

noname#137556
noname#137556
回答No.2

Relaese ビルドにしてますよね? float から double にするだけで変わるかも。 関数のインライン展開を使ってみる。 ループの中でテンポラリオブジェクトの生成,代入が無駄に多い気がする。

larry0708
質問者

お礼

リリースビルドで作成しています。 一般的にfloatよりdoubleのほうが早いのでしょうか? てっきりfloatのほうが範囲が狭いので早いと思っていました。

  • hitomura
  • ベストアンサー率48% (325/664)
回答No.1

どうもコードが途中で終わっているようので的確なアドバイスになるか不明ですが…… (1) calc_normal()の引数が参照になっていないのでそれを参照にする。内部で値を変更しないので、 Tvector3<float> calc_normal(const Tvector3<float> &p1, const Tvector3<float> &p2, const Tvector3<float> &p3){  …… } というぐあいに。 calc_all_noraml()の引数Ttexturef dmapも同様。 (2) calc_all_noraml()の引数vector<Tvector3<float>> &nで、入るデータ数が分かるならばその個数をresearve()しておくと処理の重いメモリーアロケーションの回数が減る。たぶん入るデータ数は冒頭で計算しているwとhから容易に計算できるのではないかと思われる。 (3) calc_all_noraml()の最初のループ内でvector<Tvector3<float>> tempを使っているが、tempにpush_backされるのは最大2回と分かりきっている。要素数が分かりきっている配列として使うにはvectorは重いしオーバースペックである。 たとえば以下のように、  for(int i=0; i<w; i++){   Tvector3<float> buf[2];   int buf_size = 0;   pc = Tvector3<float>(i,j,return_depth(dmap,i,j));   pu = Tvector3<float>(i,j+1,dmap.get(i,j+1).r);   if(i-1 > 0){    pl = Tvector3<float>(i-1,j,dmap.get(i-1,j).r);    buf[buf_size++] = calc_normal(pl,pu,pc);   }   if(i+1 < w){    pr = Tvector3<float>(i+1,j,dmap.get(i+1,j).r);    buf[buf_size++] = calc_normal(pc,pu,pr);   }   switch (buf_size){   case 1:    //cout<<buf[0]<<endl;    n.push_back(buf[0]);    break;   case 2:    //cout<<(buf[0] + buf[1]).normalize()<<endl;    n.push_back((buf[0] + buf[1]).normalize());    break;   default:    //cout<<"("<<i<<", "<<j<<"): no normal."<<endl;   }  } と配列を使用すればよい(インデントを全角空白で行っています。コピペするときはその後半角空白かタブに変換してください)。イテレーターを持つ配列が必要ならばstd::tr1::arrayを使うという手もある。 なお、bufとbuf_sizeをループの外に出すとより早くなると思われるが、たぶん似たようなコードが後に出てくるであろうから一つにまとめるときのことを考えて外に出していない。 (4) (上の例では消えてしまっているが)for(int k=0; k<(int)temp.size(); k++)というループ内部でat(k)を使って値を取り出しているが、代わりにイテレーターを使うべき。 あるいは、やっていることは要素の加算なので、<numeric>をインクルードして、 Tvector3<float> sum = std::accumulate(temp.begin(), temp.end(), Tvector3<float>(0, 0, 0)); とする。 ……といったことが思い浮かびます。

larry0708
質問者

お礼

複数のアドバイスありがとうございます。 まだまだ勉強中でして、すこしづつ試してみようと思います。

関連するQ&A

  • C++ ソートのやり方

    僕が作ったプログラムで、これはバブルソートなのかわからないので教えてください。 また、ほかのソートの仕方も教えてください。 よろしくお願いします。 汎用関数を使っているのでわかりにくいかもしれないですがお願いします。 #include <iostream> using namespace std; template <class X>void Sort(X *data, int size) { X temp; for (int i = 0; i < size; i++){ for (int j = i + 1; j < size; j++){ if (data[i]>data[j]){ temp = data[i]; data[i] = data[j]; data[j] = temp; } } } } int main() { int i[10]{1, 4, 3, 5, 2, 10, 2, 7, 6, 8}; char c[10]{'c', 'b', 'z', 'a', 'x', 'y', 'j', 'n', 'm', 'r'}; Sort(c, 10); Sort(i, 10); for (int j = 0; j < 10; j++){ cout << i[j] << ' '; } cout << endl; for (int j = 0; j < 10; j++){ cout << c[j] << ' '; } cout << endl; getchar(); return 0; }

  • c++ で *の逆三角旗を作りたいです

    普通の三角旗はできましたが逆三角旗が分かりません 例えば3と入力したら * ** *** ** * こんな感じで出力させたいのですが考え方が分かりません 普通の方のコードはこんな感じです int i, j; cin>>num; for (i = 1;i <= num;++i) { for (j=1;j<=i;j++) { cout << "*"; } cout << endl; } for (i = 1;i <= num-1 ;i++) { for (j=1;j<=num-i;j++) { cout << "*"; } cout << endl; } cout << endl;

  • 100x100行列の平均値を求めたい

    const int SIZE=100; for(int i=0; i< SIZE; i++){ for(int j=0; j< SIZE; j++){ fin >> array[i][j]; } } double sum=0.0; double ave=0.0; int J =3; for(int i=0; i< SIZE; i++){ sum += array[i][J]; } ave = sum / SIZE; cout << sum << endl; cout << ave << endl; 100x100行のファイルを読み込んで、すべての行の足し算および平均値を求めたいと思います。上のようある列(または行)を固定して求めることは出来るのですが、一度に0~99までの足し算および平均値を求めるやり方はどのようにしたらよろしいのでしょうか?たぶんfor文を使えば、できるのかなとは思うのですが、なかなか思うように行きません。 アドバイスよろしくお願いいたします。

  • c++のvector::erase

    vector::eraseについて質問させてください。 vector::eraseでググるとforループ内でのeraseの仕方は 書いていあるのですが、単純に1要素だけeraseする 例が見当たらなかったので教えてください。 1要素を削除する場合、 以下のような書き方でよろしいのでしょうか? いちおう正しく動作しているようです。 ただ、疑問なのは途中の要素[2]を削除しているので、 後半の要素が詰め直されているのか、 そのあたりが自信がなくて不安です。 vector<string> v; v.push_back("0a"); v.push_back("1a"); v.push_back("2a"); v.push_back("3a"); v.push_back("4a"); v.erase( v.begin() + 2 );//v[2]を削除 //結果表示 for(unsigned int i=0; i<v.size(); i++){ cout<<v[i]<<"\n"; } cout<<endl;

  • 再帰関数を用いて配列の合計を求める

    かなり(約三時間)悩んでまだ分からないので質問します。 再帰関数を用いて配列の中の数字(floatで定義されている)の合計を求めるプログラムを作っています。階乗を求めるプログラムの例を見ながらやっているのですがもう降参です。簡単だと思ったんですけど配列と組み合わせでもう頭がパニックです。どなたか答え(またはヒント)を教えてください。 よろしくお願いします。 なお、下のプログラムは私が1から作りましたので完全に間違っている可能性大です。参考にしないでください。(^^ゞ #include <iostream> using namespace std; float recur(int numF); int main() { int num; float sum; cout << "Enter an integer number: "; cin >> num; //再帰関数なんてこうやれば使わなくても出来るのに~! // for(i=0; i<num; i++) // sum += array[i]; sum = recur(num); cout << "The sum of all the numbers: " << sum << endl; return 0; } float recur(int numF) { int i; float array[numF]; //定数式が必要です、と怒られる for(i=0; i<numF; i++) return array[i] += array[i-1]; //ここに再帰の式が必要 }

  • 電卓のJavaScript

    初心者で申し訳ないのですが、電卓のソースを作ってみたのですが、JavaScriptが間違っているためか「ページにエラーが発生しました」となり計算が行われません。 どのように、改変すればいいのでしょうか?どうかご教授お願いします。 <html> <head> <title> 電卓 </title> <script language = "JavaScript"> count = 0; sum= 0; flag =0; list = new Array( "0", "0","0","0","0",); function clist( ) { for( i = 0 ; i < 5 ; i++ ) { list[i] =0; } } function calc1() { num = sum; clist( ); display.value = num; flag = 1; } function calc2() { num = sum; clist( ); display.value = num; flag = 2; } function calc3() { num = sum; clist( ); display.value = num; flag = 3; } function calc4() { num = sum; clist( ); display.value = num; flag = 4; } function calc5() { clist( ); } function equal() { if(flag==1) { sum=num+sum; display.value=sum; clist( ); } else if(flag==2) { sum=num-sum; display.value=sum; clist( ); } else if(flag==3) { sum=num*sum; display.value=sum; clist( ); } else if(flag==4) { sum=num/sum; display.value=sum; clist( ); } } function push0( ) { list[count] = 0; sum = list[count]; for( i = 0 ; i < count ; i++ ) { temp=1; for( j = i ; j < count ; j++ ) { temp=temp*10; } sum+ =list[i]*temp; } count+=1; display.value=sum; } function push1( ) { list[count] = 1; sum = list[count]; for( i = 0 ; i < count ; i++ ) { temp=1; for( j = i ; j < count ; j++ ) { temp=temp*10; } sum+ =list[i]*temp; } count+=1; display.value=sum; } 同様に2~9 </script> </head> <body> <hr><br> <input type = "button" value ="7" onclick = "push7()">&nbsp <input type = "button" value ="8" onclick = "push8()">&nbsp <input type = "button" value ="9" onclick = "push9()">&nbsp&nbsp <input type = "button" value ="+" onclick = "calc1()">&nbsp<br><br> <input type = "button" value ="4" onclick = "push4()">&nbsp <input type = "button" value ="5" onclick = "push5()">&nbsp <input type = "button" value ="6" onclick = "push6()">&nbsp&nbsp <input type = "button" value ="-" onclick = "calc2()">&nbsp<br><br> <input type = "button" value ="1" onclick = "push1()">&nbsp <input type = "button" value ="2" onclick = "push2()">&nbsp <input type = "button" value ="3" onclick = "push3()">&nbsp&nbsp <input type = "button" value ="×" onclick = "calc3()">&nbsp<br><br> <input type = "button" value ="0" onclick = "push0()">&nbsp <input type = "button" value ="=" onclick = "equal()">&nbsp <input type = "button" value ="C" onclick = "calc5()">&nbsp&nbsp <input type = "button" value ="÷" onclick = "calc4()">&nbsp<br><br> <br><br><hr><br>&nbsp&nbsp&nbsp <input type = "text" size ="10" name = "display">&nbsp <br><br><hr><br> </body> </html>

  • C++のreduce

    C++のreduceを使おうとすると error: 'reduce' was not declared in this scope となってしまいます reduceを使うにはどうしたらよいでしょうか? OSはMac GCCのバージョンは9.2.0_3となってます よろしくおねがいします コード #include <iostream> #include <vector> #include <numeric> using namespace std; int main() { const vector<int> v = {1, 2, 3}; int sum = reduce(v.begin(), v.end()); cout << sum << endl; }

  • C++で行列とベクトルの積を求める

    行列とベクトルの掛け算 y=Ax (A(3*3行列以上)とxを適当に初期化) を作成せよ これが分からないんですが誰か分かる人いませんか?下は行列の和と積をそれぞれ求めてるんですが、こんな感じになりそうなんですよね #include<iostream> using namespace std; int main() { double A[3][3]={{1,1,6},{5,3,2},{2,2,2}}; double B[3][3]={{4,1,3},{2,4,3},{5,9,2}}; double temp; int i,j,k; for(i=0;i<3;i++){ for(k=0;k<3;k++){ } } for(i=0;i<3;i++){ for(k=0;k<3;k++){ } } cout<<"和:A+B="<< '\n'; for(i=0;i<3;i++){ cout<<" { "; for(j=0;j<3;j++){ cout<< (A[i][j]+B[i][j]); if(j!=2) cout<<" , "; } cout<< " }" << '\n'; } cout<< '\n'; cout<<"積:A*B="<< '\n'; for(i=0;i<3;i++){ cout<<" { "; for(j=0;j<3;j++){ temp=0.0; for(k=0;k<3;k++) temp += A[i][k]*B[k][j]; cout<< temp; if(j!=2) cout<< " , "; } cout<< " }" << '\n'; } return 0; }

  • char型変数のアドレスを coutで表示するには

    #include <iostream> using namespace std; int main() { bool b; int i; short s; long l; float f; double d; char c; //上で宣言した変数のアドレスを表示 cout << "bool &b " << &b << endl; cout << "int &i " << &i << endl; cout << "short &s " << &s << endl; cout << "long &l " << &l << endl; cout << "float &f " << &f << endl; cout << "double &d " << &d << endl; cout << "char &c " << &c << endl; //「char &c 」とのみ表示される cout << '\n'; //char型のみ printf で再表示 printf("char &c %p\n", &c); //「char &c ********」と表示される return 0; } 上のプログラムを実行すると cout << "char &c " << &c << endl; のところだけ、アドレスが表示されません。 printfを使えば、char型の変数のアドレスも表示されるのですが…。 coutを使ってchar型のアドレスを表示させるにはどうすればいいのでしょうか。 よろしくお願いします。

  • C++の無限ループを解決してください

    アルゴリズムを勉強するときに以下のソースを書きました; void weighted_quick_union_algorithm() { static const int volume = 10; enum status { terminate_, union_, find_ }; string str; status sta; vector<int> system(volume, 0); vector<int> size(volume, 1); for (int index = 0; index != volume; ++index) { system[index] = index; } do { cout<<"cin"<<endl; cin >> str; for (string::size_type index = 0; index != str.size(); ++index) str[index] = toupper(str[index]); if (str == "UNION") sta = union_; else if (str == "FIND") sta = find_; else if (str == "TERMINATE") sta = terminate_; switch (sta) { case(0): { cout << str << endl; break; } case(1): { cout << str << sta << endl; int p(0), q(0), i(0), j(0); while (cin >> p) { cin >> q; for (i = p; i != system[i]; i = system[i]); for (j = q; j != system[j]; j = system[j]); if (i == j) continue; if (size[i] < size[j]) { system[i] = j; size[j] += size[i]; } else { system[j] = i; size[i] += size[j]; } cout << p << " - " << q << endl; } cout<<"break"<<endl; break; } case(2): { cout << str << sta << endl; break; } } } while (sta); } しかし unionを入力しあと ; でwhile(cin>>p)をブレイクしたら cin break UNION1 cin break Union1 で無限ループ 結構時間かかったが間違いがわかりません ちなみに最少は while(cin>>p>>q)と書いていましたが同じ結果です。 どうかお願いします

専門家に質問してみよう