複雑な形の図形を3D描画

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Ma

複雑な形の図形を3D描画

#1

投稿記事 by Ma » 15年前

いつもお世話になってます。
高校3年の数学に出てくる、体積の問題を視覚的に表現するプログラムを作りました。
(課題ではありません、好きでやってます。)

その問題とは、関数 f(x)= sin(x) を、x軸に対して回転したときにできる図形の体積を求めよ。
のような問題です。

添付画像のように、この段階までは成功できました。
これは、半径1、長さ1の円柱を描画時に好きな倍率に拡大し何度も描画することで近い形を再現しています。

ここまではいいのですが、
今度は f(x) = x*x と g(x) = sqrt(x) の間をx軸に対して回転してできた図形の体積の面積を求める問題です。
(分かりにくかったら画像をみてください。)

ただの計算なら、外側体積から内側の体積を引くだけです。
でも、描画をどうしよう・・・と悩むことになりました。

ふと思いついた方法は、 
    [ぜんぜん駄目だった方なので略]
だ、だめだ・・・。

なんとか、この複雑な図形を3D表示するいい方法はないでしょうか?

(添付画像を修正しました。)


追記
環境
DXライブラリ
VisualC++ 2008 Express

現在の仕様でUIより入力できる情報
・xの関数1
・xの関数2
・xの範囲(xの最小値と最大値)
・近時体積を求めるときのx範囲の分割数

解答はコードでなくても、アイディアでも結構です。 画像

たいちう

Re:複雑な形の図形を3D描画

#2

投稿記事 by たいちう » 15年前

例えば、
1 <= sqrt(x^2 + y^2) <= 2
0 <= z <= 1
を満たす図形を表示できれば良いのかな?

上下の平面の部分と内側外側の曲面の部分を、
それぞれ3角形に分割して描画するのが基本ではないかな。
これが難しいなら、更にその前のステップとして、
正四面体や正八面体の描画はできますか?

Ma

Re:複雑な形の図形を3D描画

#3

投稿記事 by Ma » 15年前

>例えば、
>1 <= sqrt(x^2 + y^2) <= 2
>0 <= z <= 1
>を満たす図形を表示できれば良いのかな?

そうですね。
円柱から中をくりぬいた感じです。

>上下の平面の部分と内側外側の曲面の部分を、
>それぞれ3角形に分割して描画するのが基本ではないかな。

つまり、円柱から中がくりぬかれたものをたくさん描画する、っていうことですね。
いっていることはわかりますが、かなり面倒ですね。。。
特にその円柱(中がくりぬかれた状態)のまわりをぐるりとまわるときの計算が特に。


・おかげでひとつ思いついた方法。
メタセコで中がくりぬかれた円柱が基本図形にあるので、これを使う。
頂点データから、内側の頂点位置をいじることで内側と外側の幅をコントロール。


とすると、DXライブラリでモデルの頂点位置をいじることができる関数があればいけるのかな?
ど、どれでしたっけ・・・。 画像

Ma

Re:複雑な形の図形を3D描画

#4

投稿記事 by Ma » 15年前

参照用メッシュ関数
とかでしょうか?
このへんからメッシュ情報を取得して計算するプログラムはみたことがあるんですが、
メッシュ情報をいじって、それに対応したものを描画できるのでしょうか?
参照用 とあるので、書き込みはできない・・・のかな?

たいちう

Re:複雑な形の図形を3D描画

#5

投稿記事 by たいちう » 15年前

> 特にその円柱(中がくりぬかれた状態)のまわりをぐるりとまわるときの計算が特に。

ここの計算は別に面倒ではないです。
// 外周の下側
float r = 2;
for (int i = 0; i < n; i++) {
    float theta = 2. * PI * i / n;
    x = r * cos(theta);
    y = r * sin(theta);
    z = 0;
}

// 外周の上側
for (int i = 0; i < n; i++) {
    float theta = 2. * PI * (2 * i + 1) / 2 / n;
    x = r * cos(theta);
    y = r * sin(theta);
    z = 1;
}


実際は用意したバッファに直接計算し、indexを設定して
DrawPolygonIndexed3Dなどを呼び出すことになるでしょう。

Ma

Re:複雑な形の図形を3D描画

#6

投稿記事 by Ma » 15年前

>> 特にその円柱(中がくりぬかれた状態)のまわりをぐるりとまわるときの計算が特に。
>ここの計算は別に面倒ではないです。

ちょっと複雑に考えていたみたいです。
DrawPolygonIndexed3D( VERTEX3D *Vertex, int VertexNum, unsigned short *Indices, int PolygonNum, int GrHandle, int TransFlag )

ここで、四角形ポリゴンを使えば(VertexNum に 4)、簡単に計算できますね。
↑三角形で考えてたから、難しく考えてた。

なるほど。
このへんの関数の使い方はぜんぜん覚えてないので、次に時間ができたとき(1,2日後とか)に挑戦開始します。
ありがとうございました。

Ma

重い

#7

投稿記事 by Ma » 15年前

ちょちょいといじってみたら、いがいとすんなりできました。
ありがとうございます。
ですが、新しい問題にあたってしまいました。

以下のように実装できましたが、
ちょっと重い感じです。
速度改善のための提案をおねがいします。
(Vertex,Index はグローバル変数として宣言し、それ以来は変更しません。)


void drawHollowCylinder(float xStart,float xEnd,float R,float r){

int n = 10;
for(int i = 0; i < n; i++){
float theta = 2.0f*PHI_F*i/n;
float thetaPre = 2.0f*PHI_F*(i-1)/n;
Vertex[ 0 ].pos = VGet( xStart*100, sin(theta)*R*100, cos(theta)*R*100) ;//外側
Vertex[ 1 ].pos = VGet( xStart*100, sin(thetaPre)*R*100, cos(thetaPre)*R*100 ) ;//外側
Vertex[ 2 ].pos = VGet( xStart*100, sin(theta)*r*100, cos(theta)*r*100 ) ;//内側
Vertex[ 3 ].pos = VGet( xStart*100, sin(thetaPre)*r*100, cos(thetaPre)*r*100 ) ;//内側
// 2ポリゴンの描画
DrawPolygonIndexed3D( Vertex, 4, Index, 2, DX_NONE_GRAPH, FALSE ) ;//底1
for(int i = 0; i < 4;i++){
Vertex[ i ].pos.x = xEnd*100;//反対側の底
}
DrawPolygonIndexed3D( Vertex, 4, Index, 2, DX_NONE_GRAPH, FALSE ) ;//底2
Vertex[ 2 ].pos = VGet( xStart*100, sin(theta)*R*100, cos(theta)*R*100 ) ;//反対側の内側にあったものを、元側の外側にする。
Vertex[ 3 ].pos = VGet( xStart*100, sin(thetaPre)*R*100, cos(thetaPre)*R*100 ) ;
DrawPolygonIndexed3D( Vertex, 4, Index, 2, DX_NONE_GRAPH, FALSE ) ;//外側の外壁
if(r < 0.01f){//もし、小さい半径が0に近いならば内側の外壁は描画しない。
for(int i = 0; i < 4;i++){
Vertex.pos.y *= r/R;Vertex.pos.z *= r/R;
}

DrawPolygonIndexed3D( Vertex, 4, Index, 2, DX_NONE_GRAPH, FALSE ) ;//内側の外壁
}
}

}



大体モデルで描画するときより、(面数で言えば8分の1ぐらいなのに)感覚的に4倍ぐらい時間がかかってしまいました。
描画は↑の関数を毎フレーム200回ぐらい呼び出しています。
円柱から中をくりだしたものをいくつもならべることで、曲線2本の間をぐるっとx軸に対して回して作られた3D図形を、中が綺麗に曲線に沿って空洞になっているような再現をしています。

(各円柱の底ふたつの半径を別々にすれば数が少なくてもより綺麗な描画になりますが、各円柱のふたつの底の半径が一致しているのは(積分を意識した)意図的表現です。)

たいちう

Re:重い

#8

投稿記事 by たいちう » 15年前

まず最初に行うべきことは処理時間の計測です。
感覚に頼るのではなく正確に計測してこそ、
最適化の効果を評価できます。

次に、DrawPolygonIndexed3Dを使いこなしましょう。
drawHollowCylinderでは、4角形を40回描画しているようですが、
連続した3角形を4回描画すれば済むはずです。

見えない面の描画はしなくても良いでしょう。
法線ベクトルで判定できます。

こっから先はケースバイケース。
有意な効果が出るとは限りません。もっと情報が欲しいところです。
自作関数への置き換えなど、最適化のテクニックは諸刃の剣です。
効果があってもプログラムを本来よりも複雑にしてしまいます。
闇雲にやらず、効果が確認できなければ元に戻すべきです。

・ループ内でsin(theta)等を複数回計算しているが、
コンパイラがこれを最適化していない可能性がある。
・sin()を自作のもっと速い関数に置き換える。
必要な精度で作ればよいので速くなる可能性はある。
・VGetは汎用的に作られた関数だろうから、これも必要な
機能と精度に絞って自作する。その過程で、ループ外に出せる
計算や、起動時に一回すればよい計算が見つかるかもしれない。

ソースを見ただけではこれ以上は今は思いつきません。
後はもっと詳しい人におまかせです。

最後に、プログラムの目的をよく考えて見ましょう。
何が必要なのですか?

Ma

Re:重い

#9

投稿記事 by Ma » 15年前

>drawHollowCylinderでは、4角形を40回描画しているようですが、
>連続した3角形を4回描画すれば済むはずです。
なるほど。
四角形より三角形がメジャーなことと、何度も同じ関数を呼ぶよりよりひとつにまとめたほうが
(関数内の初期化処理や後処理が減るおかげで)描画がはやくできる可能性が高いわけですね。

つまり、円柱の底ふたつと、外側の皮と内側の皮、計四回で描画。
それぞれの描画に三角形の数はn*2 ということですね。(四角形n回をふたつに割るから)(nは360度の分割数。
挑戦してみますが、しばらく時間かかりそうです。


>見えない面の描画はしなくても良いでしょう。
>法線ベクトルで判定できます。
すいません、見えない面というのは内側の面のことでしょうか?

そもそも DirectX の基礎知識が足りないので、どの頂点のどのメンバーをいじったらDrawPolygonIndexed3D関数で片面表示できるかが分かりません・・・。
公式のサンプルをやってみて、両面表示していたので、片面はどうやるんだろう。。。って思っていました。
法線ベクトルというと、norm めんばーのことでしょうか?でも、これって光の反射率の計算ですよね。

まずは、DrawPolygonIndexed3D関数で片面表示の仕方からご教授お願いできますか?
それから、よければ、頂点データの各メンバーの私なりの解釈があっているかとか教えていただけるとうれしいです。
typedef struct tagVERTEX3D
{
VECTOR pos ;//頂点の位置x,y,z
VECTOR norm ;//法線ベクトルx,y,z(光の反射?)
COLOR_U8 dif ;//ディフューズカラー。基本色調?
COLOR_U8 spc ;;//スペキュらカラー。反射色調?
float u, v ;//テクスチャのどの位置を選択するか。でも、この場合のuv は違う意味な気がする・・・。
float su, sv ;//???
} VERTEX3D, *LPVERTEX3D ;


>ケースバイケースの方法
あまり、大きな影響があるのか分からないので、ひとまずあとまわしにさせていただきます。
まずは、片面化から・・・。


>最後に、プログラムの目的をよく考えて見ましょう。
何が必要なのですか?

ちょっと漠然とした質問ですねwなんと答えていいのやら・・・
目的といえば円柱から中をくりぬいた図形の描画ですけど、そういうことじゃないですよね。 画像

Ma

無題

#10

投稿記事 by Ma » 15年前

追加で質問なのですが、
透明度は、モデルのときは以下の関数をつかっていました。
MV1SetOpacityRate
今回の描画方法の場合は、どの値を調整すればいいのでしょうか?
または、どのような方法をとればいいでしょうか? 画像

たいちう

Re:無題

#11

投稿記事 by たいちう » 15年前

処理時間の計測についての反応がありませんが、
当然やってくれているわけですよね?
これがないと話になりませんので、現段階での実測時間を教えてください。


> すいません、見えない面というのは内側の面のことでしょうか?

立方体を外から見た図を表示する場合、
6つの正方形(変形している)を描画する必要はありません。
視点からは最大でも3つしか見えませんので、見えない面の描画は無駄です。
もし可能ならば、座標の計算も省きたいところですが、
とりあえず法線ベクトルを使って判定するのが簡単でしょう。


> 法線ベクトルというと、norm めんばーのことでしょうか?
> でも、これって光の反射率の計算ですよね。

normメンバーであっていると思いますが、反射率の計算のためだけにあるわけではありません。
図形的な意味を調べてください。


> まずは、DrawPolygonIndexed3D関数で片面表示の仕方からご教授お願いできますか?

知りません。私はDxライブラリは殆ど使用経験がないもので。
他の方からの回答を待つか、もっとよいのは、自分で調べてください。
何でもすぐに人に聞くようだと力は付きませんよ。
自分でできることがなくなってから質問するのが良いと思います。

それと私が意味しているのは、隠れることがわかっている面については、
DrawPolygonIndexed3Dを呼び出さない、ということです。


隠れる面の計算と描画を省いたとしても、せいぜい処理時間は半分になるだけですので、
DrawPolygonIndexed3Dを4回だけ呼び出すように変更するのが先でしょう。
もちろん処理時間の計測後ですが。


> ちょっと漠然とした質問ですねwなんと答えていいのやら・・・

どの位の処理速度が必要なのか、とか、動画である必要があるのか、とか、
全く判りませんので、何のためのプログラムなのかを質問したのです。

Ma

Re:無題

#12

投稿記事 by Ma » 15年前

>どの位の処理速度が必要なのか、とか、動画である必要があるのか、とか、
>全く判りませんので、何のためのプログラムなのかを質問したのです。
なるほど。
このプログラムは、任意の(数学的意味での)関数をx軸に対して回転した時の結果の図形を表示するプログラムで
目的は、より図形の形が理解または想像できるようにするためのものです。
また、今回の質問は、ふたつの任意の(数学的意味での)関数を用いて、その間の部分を回転してできた図形を表示するためにしています。
また、見る角度やカメラ位置はFPSのように任意で移動できるようにしたいので、表示は動画的である必要があります。


>処理時間の計測についての反応がありませんが、
>当然やってくれているわけですよね?
>これがないと話になりませんので、現段階での実測時間を教えてください。

失礼しました。
さっそく計測結果を報告します。
と、その前に、以前話していた4倍も時間がかかるというのは、描画ではなく計算部分の間違いによるものでした。
修正後、割とスムーズに動いてくれることが分かりました。
それでも計測してみました。


1 <= sqrt(x^2 + y^2) <= 2
0 <= z <= 1

この図形で実験することとします。

xの範囲を分割する数(今回の場合0から1を分割する数)を以降 分割数と呼ぶことにします。
また、それぞれの円柱の360度を何分割するかという数を n=[自然数] と表現します。
また、これらの計測結果は、↑のDrawHollowCylinder にかかった時間です。

n=50での結果。
以下(分割数,計測結果の平均(ms))
(1,0.5ms)
(10,2.9ms)
(100,29ms)
(200,57.7ms)

n=100での結果。
以下(分割数,計測結果の平均(ms))
(1,0.9ms)
(10,6.1ms)
(100,58.1ms)
(200,116.0ms)




以上が、DrawHollowCylinder の結果でした。
以下、モデル(元mqoデータ)を使用して、
0 <= sqrt(x^2 + y^2) <= 2
0 <= z <= 1
を描画した結果です。

モデルは、n=100です。
(分割数,計測結果の平均(ms))
(1,0.4ms)
(10,1.5ms)
(100,106.2ms)
(200,423.6ms)


なぜか不思議な結果に。。。。。
もしかしたら、計算部分が影響しているのかもしれません。

計測してわかったことは、分割数(面の数)がふえるごとに、DrawHollowCylinder は比例するのに対して
モデル描画では、まるで二次関数のように上昇しているが、2桁以下の分割数までならDrawHollowCylinder よりもだいたい早い描画ができる。

すでに、現状のDrawHollowCylinder だけでも、十分目的を達成できる速度になっていることがわかりましたが、後学とさらなるパフォーマンス向上のためにたいちうさんが教えてくださった二つの重要な改良方法を実装する方向しようとおもいます。

1.四角形から三角形へなおし、4回の描画ですませる。
2.法線ベクトルから描画するか判定する。
3. なぜか両面表示になっているので、これを片面表示へ。(DXライブラリの関数なので、公式の掲示板で質問。ここで答えが得られたならラッキー。)


追記
ちょっと今時間がとれそうにないので、また後日やります。 画像

Ma

コーディングミス

#13

投稿記事 by Ma » 15年前

すいません。前回の計測結果は(mqoモデル描画側のコードが)間違っていました。

↓これが間違い


モデルは、n=100です。
(分割数,計測結果の平均(ms))
(1,0.4ms)
(10,1.5ms)
(100,106.2ms)
(200,423.6ms)


まさにforループが二重になってて、文字通り二次関数状態になってました。

正しくは、
モデルは、n=100です。
(分割数,計測結果の平均(ms))
(1,0.1ms)
(10,0.9ms)
(100,1.2ms)
(200,2.3ms)

(1000,11.5ms)

やはり、驚異的な差がありました。(DrawHollowCylinderと比べて)約50倍の差です。
今回はうまく比例してくれました。
おそらく毎フレーム頂点計算があるのも影響しているのかもしれません。
(マイフレーム関数が変わるわけじゃないので、データの保存も考えておきます。)


以下、経過報告

1.四角形から三角形へなおし、4回の描画ですませる。
 すいません、まだ(時間的に)できません。週末になるかも?

2.法線ベクトルから描画するか判定する。
 すいません、まだ(時間的に)できません。週末になるかも?

3. なぜか両面表示になっているので、これを片面表示へ。(DXライブラリの関数なので、公式の掲示板で質問。ここで答えが得られたならラッキー。)
 DXライブラリの公式掲示板で解答待ちです。

以上です。
あまり進歩していないのにあげてすいません。 画像

Ma

Re:コーディングミス

#14

投稿記事 by Ma » 15年前

1.四角形から三角形へなおし、4回の描画ですませる。

↑これやろうとしてます。



void drawHollowCylinder(float xStart,float xEnd,float R,float r){


int n = 50;
VERTEX3D *Vertex = new VERTEX3D[n*2];
unsigned short *Index = new unsigned short[n*6];
for(int i = 0; i < n; i++){
Vertex[i*2] = initialVertex;
Vertex[i*2+1] = initialVertex;
float theta = 2.0f*PHI_F*2*i/n;
Vertex[ i*2 ].pos = VGet( xStart*100, sin(theta)*R*100, cos(theta)*R*100) ;//外側
Vertex[ i*2+1 ].pos = VGet( xStart*100, sin(theta)*r*100, cos(theta)*r*100 ) ;//内側

/*Indexの並びがわかりません・・・・。
if(i == 0){
Index[ 0 ] = 0;
Index[ n*6-3 ] = 0;
Index[ n*6-4 ] = 0;
Index[ 1 ] = 2;
Index[ 4 ] = 1;
Index[ n*6-1 ] = 3;
}
else{
Index[ i*6 ] = i*2;
Index[ i*6-3 ] = i*2;
Index[ i*6-4 ] = i*2;
Index[ i*6+1 ] = i*4+2;
Index[ i*6+4 ] = i*4+1;
Index[ i*6-1 ] = i*4+3;
}*/



}



// 2ポリゴンの描画
DrawPolygonIndexed3D( Vertex, n*6, Index, 2*n, DX_NONE_GRAPH, FALSE ) ;//底1

delete Vertex;
delete Index;

}

↑Index の求め方がもうちんぷんかんぷんでした。。
一応図に5回ぐらい描いて挑戦したんですが、とんでもない図形になるばかりでしたorz
ちなみに、まだ底一つ目の描画の段階です。

Index の正しい並び方の考え方を教えてください。

たいちう

Re:コーディングミス

#15

投稿記事 by たいちう » 15年前

> Index の正しい並び方の考え方を教えてください。

http://homepage2.nifty.com/natupaji/DxL ... html#R14N8


まず、n=4くらいで、forループなしでやってみて下さい。
これならできますか?

Ma

Re:コーディングミス

#16

投稿記事 by Ma » 15年前

で、できましたー!

初心に戻って、データをリスト化してみたら、なんとなくパターンが見えました。

経過報告でした。

void drawHollowCylinder(float xStart,float xEnd,float R,float r){


int n = 50;
VERTEX3D *Vertex = new VERTEX3D[n*2];
unsigned short *Index = new unsigned short[n*6];
for(int i = 0; i < n; i++){
Vertex[i*2] = initialVertex;
Vertex[i*2+1] = initialVertex;
float theta = 2.0f*PHI_F*i/n;
Vertex[ i*2 ].pos = VGet( xStart*100, sin(theta)*R*100, cos(theta)*R*100) ;//外側
Vertex[ i*2+1 ].pos = VGet( xStart*100, sin(theta)*r*100, cos(theta)*r*100 ) ;//内側
}
/*パターン分析
Index[0] = 0;
Index[1] = 1;
Index[2] = 2;

Index[3] = 1;
Index[4] = 2;
Index[5] = 3;

Index[6] = 2;
Index[7] = 3;
Index[8] = 4;

Index[9] = 3;
Index[10] = 4;
Index[11] = 5;

Index[12] = 4;
Index[13] = 5;
Index[14] = 6;

Index[15] = 5;
Index[16] = 6;
Index[17] = 7;
*/
for(int i= 0; i < n*6; i++){
Index = i/3+i%3;
if(Index >= n*2){
Index %= n*2;
}
}


// 2ポリゴンの描画
DrawPolygonIndexed3D( Vertex, n*6, Index, 2*n, DX_NONE_GRAPH, FALSE ) ;//底1

delete Vertex;
delete Index;

}


続きいきます!

Ma

Re:コーディングミス

#17

投稿記事 by Ma » 15年前

1.四角形から三角形へなおし、4回の描画ですませる。

ようやく、これが終わりました。
なお、描画するときは、計算時間がかからないように、最初(図形に変更があったとき)にすべて先に計算しデータとして保存するようにしました。
(つまり、頂点の位置を計算するのは図形を変更したときだけ)



で比較してみます。

以下、結果。

n=50での結果。
以下(分割数,計測結果の平均(ms))
(1,0.5ms)
(10,2.9ms)
(100,29ms)
(200,57.7ms)
↓(改良後)
(1,0.1ms)
(10,0.8ms)
(100,8.5ms)
(200,13.5ms)

(1000,62ms)

n=100での結果。
以下(分割数,計測結果の平均(ms))
(1,0.9ms)
(10,6.1ms)
(100,58.1ms)
(200,116.0ms)

↓(改良後)

(1,0.2ms)
(10,1.9ms)
(100,12.6ms)
(200,24.0ms)

(1000,120ms)



結論
・約、5倍ほど早くなりました。





これで、1番は完了にします。

二番にとりかかります。
一応、先に考えていることをかいておきます。

各面の頂点3つから外積で求めた法線ベクトルと、頂点からカメラへのベクトルの内積を使って、間の角度を求める。
それが、90度以上だったら描画しない。 画像

Ma

Re:コーディングミス

#18

投稿記事 by Ma » 15年前

法線ベクトルを求めている途中、以前のインデックス計算時、表裏が交互になっていたことが判明しましたorz


for(int i= 0; i < n*6; i++){
Index = i/3+i%3;
if(Index >= n*2){
Index %= n*2;
}
}

↑こんな感じで、頂点の順番を定義していました。


それで、↓が、法線ベクトルと頂点からカメラの内積を求めながら法線ベクトルを描画する部分。

for(int faceNum = 0; faceNum < 2*divN;faceNum++){
VECTOR v1 = VertexStart[Index[faceNum*3]].pos;
VECTOR v2 = VertexStart[Index[faceNum*3+1]].pos;
VECTOR v3 = VertexStart[Index[faceNum*3+2]].pos;
VECTOR normalVec = VCross(VSub(v1,v3), VSub(v3,v2));
VECTOR center = VGet((v1.x+v2.x+v3.x)/3,(v1.y+v2.y+v3.y)/3,(v1.z+v2.z+v3.z)/3);
VECTOR dotCamVec = VSub(center,CamPos);
float theta = acos(VDot(normalVec,dotCamVec)/(sqrt(VSquareSize(normalVec))*sqrt(VSquareSize(dotCamVec))));
//printfDx("theta=%4.2f\n");
DrawLine3D(v1,v2,GetColor(255,255,255));
DrawLine3D(v2,v3,GetColor(255,255,255));
DrawLine3D(v3,v1,GetColor(255,255,255));
DrawLine3D(center,VAdd(center,normalVec),GetColor(255,255,255));

}


結果は、画像のとおりで、表と裏が交互になっていたみたいですorz
調べたところ、頂点が時計周りなら裏で、頂点が半時計周りなら表らしいので、インデックスの計算方法を直す必要があるみたいでした。
挑戦してみます。

Ma

Re:コーディングミス

#19

投稿記事 by Ma » 15年前

例の問題も修正して、2番できました。

何をやったかというと。。。って言葉で説明するとすごく長くなりそうです(汗
基本的には、前書いたことです。
>各面の頂点3つから外積で求めた法線ベクトルと、頂点からカメラへのベクトルの内積を使って、間の角度を求める。
>それが、90度以上だったら描画しない。

底面は、それぞれ1つの三角形の法線だけで表示チェックしましたが、側面はそうもいかないので、すべての面をチェックすることになりました・・・。
おそらく、これがスピードのネックになりそうです。


ともかくこの方法で、二番
>2.法線ベクトルから描画するか判定する。
を、完了しました。


計測結果の比較をします。
今回、比較するのは、1番修正後と1番、2番両方修正後です。



n=50での結果。
以下(分割数,計測結果の平均(ms))
【1番修正後】
(1,0.1ms)
(10,0.8ms)
(100,8.5ms)
(200,13.5ms)
(1000,62ms)
↓【二番修正後】
(1,0.2ms)
(10,3.2ms)
(100,26.5ms)
(200,55ms)
(1000,268ms)


n=100での結果。
だいたい予想できるので略


結論
今のコードではぜんぜんだめだったorz
コードの組み方がわるいんでしょうけど、潔く判定しないほうがいいと思い始めてます。
とりあえず、1番修正後のコードと2番修正後のコードを添付します。

あと、VC++の出力画面にアクセス違反が大量発生しているので(プログラムは落ちないが、表示している。)、それの原因がもしありそうなら教えていただけると幸いです。

もし余裕がない方がいれば、2番修正後は長いので、1番修正後だけを見ていただけるだけでもいいです。

Ma

Re:コーディングミス

#20

投稿記事 by Ma » 15年前

修正点2番ですが、
なんだか、とんでもない勘違いをしていたみたいです。

法線ベクトルは、頂点情報に保存しておいて、表示するかどうかはその法線ベクトルから求めればいいから、
毎フレーム法線ベクトルを求める必要なんてなかったんですね。

ようやく、VECTOR.norm メンバーの意味が分かりました。
解説サイトがないと、こうもてこずるものなんですねw
ここの2Dの解説ページには感謝です。
この点を改良しようと思います。
(あーでも、アクセス違反の件は引き続き探してますので、分かる方がいればうれしいです。)

なんか一人芝居やってるみたいで、ほんとすいませんorz



で、
http://sky.geocities.jp/freakish_osprey ... vecotr.htm
ここのページみながら、頂点法線ベクトルを求める段階までこれました。


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

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


よく理解しました。。。。が。
頂点を共有する面のリスト、ってどうやって・・・・
またデータ解析かorz
うわぁ、面倒ですねこれは・・・。 画像

Ma

Re:コーディングミス

#21

投稿記事 by Ma » 15年前

どうも、うまく描画判定ができないです。(遅くなってしまう)
とりあえず、判定なしで当初の目的の高速化ができたのでよしとします。

たいちう

Re:コーディングミス

#22

投稿記事 by たいちう » 15年前

VertexStart = new VERTEX3D[n*2];
...

//頂点情報を出していく。
for(int i = 0; i < n; i++){
    //頂点を初期化
    VertexStart[i*2] = initialVertex;
    VertexStart[i*2+1] = initialVertex; ← 明らかにおかしい
パッと見ただけですが、おかしいところがありますね。
このような所が他にもあるのでしょう。

経験を積みプログラムが短かければ一目で気付きますが、
プログラムがもっと複雑になると相当経験があっても、
ソースコードからバグの原因に気付くことが困難になってきます。
実行時エラーの対処法を工夫すべきでしょう。

今回の場合は、ソースコードの一部をコメントアウトして試すことで、
アクセス違反をおこしている箇所を絞り込むことができるでしょう。
私の指摘を聞かなかった振りをして、コメントアウトして絞り込む練習を
してみてはいかがでしょうか。

# この説明で分からなければ、時間のあるときにもっと丁寧な説明をしてみます。

たいちう

Re:コーディングミス

#23

投稿記事 by たいちう » 15年前

「アクセス違反の原因が分からない。」のスレに気付かず、こっちに回答しました。
向こうも解決済みになっていますし、しばらくはこのスレを使いましょう。


> どうやら、一度に作れる個数に制限があるみたいなんです。

という推測は間違いでしょう。私が指摘した種類のことを修正してから
再度試してください。

Ma

Re:コーディングミス

#24

投稿記事 by Ma » 15年前

>今回の場合は、ソースコードの一部をコメントアウトして試すことで、
>アクセス違反をおこしている箇所を絞り込むことができるでしょう。

一応 VertexInner か VertexOuter を描画するときにアクセス違反が発生するのが確認できてます。
DrawPolygonIndexed3D( VertexInner, divN*6, Index, 2*divN, DX_NONE_GRAPH, FALSE ) ;//内側の側面(ここでアクセス違反発生)
DrawPolygonIndexed3D( VertexOuter, divN*6, Index, 2*divN, DX_NONE_GRAPH, FALSE ) ;//外側の側面

ただ、一番よくわからないのは、(n角柱の)個数をふやすとアクセス違反が発生するんですよね・・・。
(過去に、2、3個DXライブラリのバグを発見したことがあるので、今回も一応DXライブラリのバグも検討しながらチェックをすすめてみます。)


> VertexStart[i*2] = initialVertex;
> VertexStart[i*2+1] = initialVertex; ← 明らかにおかしい

うーん指摘されても、なにがおかしいのかよくわかりません。
ただ、コードがわかりづらいのはたしかなので、この部分をわかりやすく書き直すと

↓これから
//頂点情報を出していく。
for(int i = 0; i < n; i++){
//頂点を初期化
VertexStart[i*2] = initialVertex;
VertexStart[i*2+1] = initialVertex; ← 明らかにおかしい

↓こうなります。
//すべての頂点を初期化する。
for(int i = 0; i < n*2;i++){
//頂点を初期化(色情報など)
VertexStart = initialVertex;

(今回添付した新しいソースを見ると分かりやすいかも)

ちょっとここにのせているソースが古くなっているので、現在のソースを添付させていただきます。

ところで、VC++のデバッガー(自動変数)でnew を使った値を確認できないのって仕様なんでしょうか?
私の環境では、どうもエラーみたいな表示になるんですよね。。。
実際にDXライブラリのprintf とかで表示すると、エラーになんてなってないんですけどね。。


あ、あと仕様に変更がありました。
半径を底1と底2でばらばらで指定できるようにしました。
つまり、n角柱なのだが、底面がそれぞれ合同してない感じです。

たいちう

Re:コーディングミス

#25

投稿記事 by たいちう » 15年前

> うーん指摘されても、なにがおかしいのかよくわかりません。

申し訳ありません。これは私の勘違いでした。
経験を積み、、、のくだりが特に痛いですね。


> (過去に、2、3個DXライブラリのバグを発見したことがあるので、
> 今回も一応DXライブラリのバグも検討しながらチェックをすすめてみます。)

念のために確認しますが、DXライブラリのバグだとどのようにして確認しましたか?
今回の場合だと、

分割数が多くなると不具合が発生する

分割数以外に自分のプログラムは変更していない

ライブラリのバグに違いない

という思い込みに落ち込まないように気をつけてください。
ライブラリのバグの可能性はありますが、自分のプログラムに
原因があることが殆どです。
ご承知のことと思いますが念のため。


今回のアクセス違反の原因については、時間が取れればもう少し見てみます。
少なくとももっと有益なアドバイスは差し上げられるように。

Ma

Re:コーディングミス

#26

投稿記事 by Ma » 15年前

ご丁寧にありがとうございます。

>思い込みに落ち込まないように気をつけてください。
はい、もちろんまだまだ詳しく調べて自分がミスっていないか調べて、おもいこまないように気をつけます。


さて、さっそくもっと原因をほっていきたいところなんですが、
しばらくちょっと手があかなさそうなので
次の投稿はしばらく後になりそうです><

たいちう

Re:コーディングミス

#27

投稿記事 by たいちう » 15年前

http://www.play21.jp/board/formz.cgi?action=res&resno=47132&page=&id=dixq&rln=47491

↑のサンプルと比較してみると、DrawPolygonIndexed3Dの2つめの引数は、
divN*2ではないかな。

Ma

Re:コーディングミス

#28

投稿記事 by Ma » 15年前

ん?リンクミスでしょうか?
そのURLはここのリンクのようです。

http://homepage2.nifty.com/natupaji/DxL ... html#R14N8
きっとここのことですよね。


…読んでます・・・。



なおったぁぁあああ、うわぁぁあああ!!!って感じですね。(悲しみと喜び
まさかの、まさかの引数ミス・・・。
リファレンスをもう一度確認しておけばよかったです。

こんなミスはもうしないように、リファレンスは何度でも読むようにこころがけるようにします。
おはずかしいミスですいませんでした。


そして、今回の成果の記念に一枚、添付しましたw

こんな複雑な図形(x方向を100分割、YZ方向(360度)を50分割)を、なんと! 約 2ms で描画してしまう!
最初と比べると、15倍早くなりましたww

いやぁ、爽快ですねw ありがとうございました。 画像

閉鎖

“C言語何でも質問掲示板” へ戻る