Re: MakeScreenで作成した画面の合成について ( No.1 ) |
- 名前:管理人 日時:2014/12/06 12:16
MakeScreen を使用してZバッファの精度を上げる方法ですが、
載せて頂いたプログラムでは SetDrawScreen で描画先を変更した後、
SetCameraPositionAndTarget_UpVecY などでカメラの位置と向きを設定されていませんが、
こちらは実際のプログラムでもそのようになっていますでしょうか?
もしそうだとしますと、SetDrawScreen の後に SetCameraPositionAndTarget_UpVecY などで
カメラの位置と向きを設定する処理を加えれば意図した結果が得られると思います
( SetDrawScreen をするとカメラの設定がリセットされてしまうので )
> もう一点、ウインドウモードで起動した場合に常にそれを
> 他のウインドウよりも最前面に表示する方法はありますでしょうか?
あれ? こちらのお書き込みが無くなったということは解決されたのでしょうか?
とりあえずDXライブラリの関数にはウインドウのZオーダーを変更する機能が無かったので、追加しました
よろしければこちらの関数を追加したバージョンをダウンロードしてください
http://homepage2.nifty.com/natupaji/DxLib/DxLibVCTest.exe // VisualC++ 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibBCCTest.exe // BorlandC++ 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibGCC_DevCppTest.exe // Dev-C++ 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibGCC_MinGWTest.exe // MinGW 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibDotNet.zip // .NET用
http://homepage2.nifty.com/natupaji/DxLib/DxLibMakeTest.exe // ソース
(中身を既存のライブラリのファイルに上書きして、BCCをお使いの
場合は『再構築』を、VCをお使いの場合は『リビルド』を、
Dev-C++をお使いの方は「Rebuild All(Ctrl+F11)」をして下さい)
以下の関数を追加しました
// メインウインドウの奥行き位置を変更する
int SetWindowZOrder( int ZType ) ;
ZType に指定できる値は以下の通りです
// ウインドウの奥行き位置設定タイプ
DX_WIN_ZTYPE_NORMAL // 通常設定
DX_WIN_ZTYPE_BOTTOM // 全てのウインドウの一番奥に配置する
DX_WIN_ZTYPE_TOP // 全てのウインドウの一番手前に配置する
DX_WIN_ZTYPE_TOPMOST // 全てのウインドウの一番手前に配置する( ウインドウがアクティブではないときも最前面に表示される )
こちらの関数は DxLib_Init 呼び出しの前でも後でも使用できますので
最前面にしたいタイミングの箇所に
SetWindowZOrder( DX_WIN_ZTYPE_TOPMOST ) ;
という記述を追加すればウインドウを常に最前面に表示することができます
独自に解決されたのでしたら必要ないかもしれませんが、一応ご紹介ということで
|
Re: MakeScreenで作成した画面の合成について ( No.2 ) |
- 名前:ゆーすけ 日時:2014/12/06 14:02
管理人様
早速の回答ありがとうございます。
ご指摘のとおり、SetDrawScreenの後にはカメラの位置と向きの
設定処理は行っていませんでした。
後ほど試してみて結果はここで報告させていただきます。
2つ目の質問については、元々メインフォームから子フォームと
DXライブラリのウインドウを同時に開くアプリケーションだったのですが、
子フォームでDXのウインドウが隠されることがあるのが面倒だったので
そのような方法を探していました。
その後調べていったところ、フォーム上のピクチャボックスの
ハンドルを取得してそれをUserSetWindow関数に渡すことで、
ウインドウモードではなく直接ピクチャボックス内に描画できましたので、
そちらの方で問題が解決できたということで取り下げさせていただきました。
DirectInputは使えなくなるようですが、私のアプリケーションでは
Windowsフォーム上のコンポーネントとキーボード入力があれば
入力については事足りるので、とりあえずこちらの方法でいこうと思います。
取り下げた質問に対してもわざわざ対応していただいて
本当にありがとうございます。
追加された関数については別の場面で必要になりましたら
使わせていただきます。
|
Re: MakeScreenで作成した画面の合成について ( No.3 ) |
- 名前:ゆーすけ 日時:2014/12/06 15:48
先ほど試してみたところ、管理人様ご指摘の箇所を修正して
若干の変更を加えたところ表示そのものは上手くいきました。
ただしまだ問題はあるようです。
元々1メートル〜10万キロレンジの空間を描画したくてこの方法を使うつもりだったのですが、
ワールド空間での座標をメートル単位と想定した場合(物体のワールド座標の
絶対値のレンジは1から数千万程度になります)、その空間のワールド座標を
ConvWorldPosToScreenPos関数で座標変換してDrawLine関数に渡した際に
オーバーフローが発生します。
Visual Studioのデバッガで変換後のVECTOR型変数の値を確認してみたところ
QNAN(非数?)になっており、これがDrawLine関数に渡されてエラーになっているようです。
ひょっとしてConvWorldPosToScreenPos関数で引数として渡せるVECTOR型の変数には
Single型ということ以外に範囲上の制約があるのでしょうか?
座標単位をキロメートルと想定して座標のスケールを1000分の1に縮めれば
とりあえずエラーは発生しなくなりますが、そうすると数百メートル以内の
近距離の描画時にはまだ表示が崩れてしまう(手前のクリップ面までの距離に
1以下の小さな値を指定しなければならないから?)ので、
オブジェクトの物理演算に使う座標をワールド座標に変換する手間も考えると
メートルをそのままワールド座標として使いたいところです。
もし何かよい方法をご存知でしたらご教示いただけると幸いです。
|
Re: MakeScreenで作成した画面の合成について ( No.4 ) |
- 名前:管理人 日時:2014/12/07 15:28
> 元々1メートル〜10万キロレンジの空間を描画したくてこの方法を使うつもりだったのですが、
> ワールド空間での座標をメートル単位と想定した場合(物体のワールド座標の
> 絶対値のレンジは1から数千万程度になります)、その空間のワールド座標を
> ConvWorldPosToScreenPos関数で座標変換してDrawLine関数に渡した際に
> オーバーフローが発生します。
DrawLine 関数内でオーバーフローが発生するとのことですが、ConvWorldPosToScreenPos 関数から
返ってくる座標はどのような値なのでしょうか?
あと、ConvWorldPosToScreenPos 関数は SetCameraNearFar の設定の範囲外のワールド座標を渡すと
正常な値が返ってきませんので、描画をするとき以外は SetCameraNearFar の範囲を作品世界全体が
収まる値にしておいてください
|
Re: MakeScreenで作成した画面の合成について ( No.5 ) |
- 名前:ゆーすけ 日時:2014/12/07 19:44
返信ありがとうございます。
ConvWorldPosToScreenPos関数の返り値を代入している
VECTOR型の変数の中身はx,y,z全て「1.#QNAN」となっています。
計算が正しく行われていないということだけは分かるのですが、
具体的な対処法については見当もつかない状態で頭を抱えています。
>ConvWorldPosToScreenPos 関数は SetCameraNearFar の設定の範囲外のワールド座標を渡すと
>正常な値が返ってきませんので、描画をするとき以外は SetCameraNearFar の範囲を
>作品世界全体が
収まる値にしておいてください
試しにConvWorldPosToScreenPos関数の時にSetCameraNearFarの範囲を
全体に設定してみましたが結果は変わりませんでした。
ただ、1000分の1スケールの時はこれをやらなくても普通に動いているので
なぜ座標の値を1000倍しただけで動かなくなるのか全く見当もつかない状態です。
|
Re: MakeScreenで作成した画面の合成について ( No.6 ) |
- 名前:管理人 日時:2014/12/08 00:57
> 試しにConvWorldPosToScreenPos関数の時にSetCameraNearFarの範囲を
> 全体に設定してみましたが結果は変わりませんでした。
> ただ、1000分の1スケールの時はこれをやらなくても普通に動いているので
> なぜ座標の値を1000倍しただけで動かなくなるのか全く見当もつかない状態です。
SetCameraNearFar の設定が原因かと思いましたが、単純に ConvWorldPosToScreenPos 関数の
処理の過程で発生する値が float で表現できる値の最大値を超えてしまっている可能性が高そうです
1000分の1にすると正常に動作するのは、単純に 1000分の1にすることで計算の過程で発生する
最大値が小さくなるからだと思います
ただ、DXライブラリが使用している Direct3D9 では使用を開始すると高速化の為に
勝手に CPU の浮動小数点数の計算精度を落としています
これは Direct3D9 内での処理に加えて、Direct3D9 と関係ないプログラムで行っている
浮動小数点数の処理の計算精度まで下げています
今回の様な高い計算精度が必要な場合はそれでは困るので、対応策として Direct3D9 には
「浮動小数点数処理の計算精度を下げない」というオプションがあり、DXライブラリでは DxLib_Init 呼び出しの前に
SetUseFPUPreserveFlag( TRUE ) ;
を記述することでそのオプションが有効になります
これで 1.#QNAN になってしまう現象が回避できるくらい精度が上がるかはわかりませんが、
少なくともこのオプションを使用しない場合よりは確実に計算精度は上がりますので、
よろしければお試しになってみてください m(_ _)m
|
Re: MakeScreenで作成した画面の合成について ( No.7 ) |
- 名前:ゆーすけ 日時:2014/12/08 07:17
ありがとうございます。
SetUseFPUPreserveFlagも試してみましたが残念ながら結果は変わりませんでした。
これを有効にするとfloat型ではなくdouble型でそのまま演算を行うという理解でよろしいでしょうか?
double型はfloat型と比べると実質無限といえる範囲を扱えると思っているのですが、
それでもQNANが出てしまうというのは驚きです。
ConvWorldPosToScreenPos内部でどのような処理がなされているかは分かりませんが、
よほど値が大きくなるような計算方法が使われているのでしょうね。
このオプションを使ってもダメとなるとメートル級の領域は諦めるほかないでしょうか??
|
Re: MakeScreenで作成した画面の合成について ( No.8 ) |
- 名前:管理人 日時:2014/12/09 02:07
SetUseFPUPreserveFlag( TRUE ) ; を使用しても駄目でしたか・・・
> これを有効にするとfloat型ではなくdouble型でそのまま演算を行うという理解でよろしいでしょうか?
はい、厳密には double型である倍精度( 64bit )よりもビット数の多い拡張倍精度( 80bit )で演算が行われます
ただ、「演算を行っている間」の精度が上がるだけで、値をメモリに記憶する際は相変わらず float型を使用します
つまり、変数( メモリ )から CPU が値を読み取り、計算をして、また変数( メモリ )に書き戻す過程の
「計算」の部分のみ精度が上がる( 拡張倍精度( 80bit )になる )、という感じです
( SetUseFPUPreserveFlag( TRUE ) ; を実行していないと、「計算」の部分も単精度( 32bit )で行われます )
なので、DXライブラリ内部で浮動小数点数を記憶する変数の型を float型から double型に変更すれば
メモリに記憶している値の精度も単精度から倍精度に上がるので、精度が本件の原因であればそれで
解決する可能性は高いです
が、もしかしたら精度の問題ではないかもしれませんので、その機能( double型で値を保存する機能 )を
作成する前に、最終確認として私の手元でも現象を確認しておきたいと思います
なので、お手数で申し訳ありませんが QNAN の値が返ってきてしまう場合の
・SetCameraNearFar の値
・SetCameraPositionAndTarget_UpVecY などのカメラの設定の値
・ConvWorldPosToScreenPos に渡した座標の値
を教えて頂けないでしょうか? m(_ _;m
|
Re: MakeScreenで作成した画面の合成について ( No.9 ) |
- 名前:ゆーすけ 日時:2014/12/09 05:52
了解しました。
すぐには無理かもしれませんが試してみて結果をここで報告させていただきます。
|
Re: MakeScreenで作成した画面の合成について ( No.10 ) |
- 名前:ゆーすけ 日時:2014/12/09 22:17
サンプルプログラムを作って試していたところ、以下の条件で
ConvWorldPosToScreenPosの返り値がQNANとなってエラーが発生しました。
なお、DX.SetUseFPUPreserveFlag(DX.TRUE)をDX.DxLib_Init()の前に
指定しています。
カメラの設定
SetupCamera_Perspective(45*π/180)
SetCameraPositionAndTargetAndUpVec(CamPos,TgtPos,TgtPos)
SetCameraNearFar(10^6,10^8)
ConvWorldPosToScreenPos関数に引数として与えたワールド座標
Pos0 DxLibDLL.DX.VECTOR
x -3967703.25 Single
y 3721825.75 Single
z 3329298.25 Single
カメラの座標
CamPos DxLibDLL.DX.VECTOR
x -6025285.5 Single
y 3762868.25 Single
z 3508726.0 Single
注視点の座標
TgtPos DxLibDLL.DX.VECTOR
x -3952720.75 Single
y 3748894.25 Single
z 3316726.5 Single
ConvWorldPosToScreenPos関数の返り値
Pos1 DxLibDLL.DX.VECTOR
x 1.#QNAN Single
y 1.#QNAN Single
z 1.#QNAN Single
どうぞよろしくお願いします。
|
Re: MakeScreenで作成した画面の合成について ( No.11 ) |
- 名前:管理人 日時:2014/12/10 00:52
条件を教えて頂きありがとうございます
そして、こちらでも QNAN になることが確認できたのですが、一つ気になるところが・・・
こちらの SetCameraPositionAndTargetAndUpVec の第三引数ですが
SetCameraPositionAndTargetAndUpVec(CamPos,TgtPos,TgtPos)
第三引数にはカメラの上方向をベクトルで指定します
例えば Y軸方向が上方向の場合は x=0, y=1, z=0 の VECTOR 構造体を渡します
とりあえず第三引数を TgtPos ではなく VGet( 0.0f, 1.0f, 0.0f ) にしたところ、
ConvWorldPosToScreenPos の戻り値が QNAN になることはなくなりましたので、
よろしければお試しになってみてください
|
Re: MakeScreenで作成した画面の合成について ( No.12 ) |
- 名前:ゆーすけ 日時:2014/12/10 05:58
ありがとうございます。
第三引数をVGet(0,1,0)にしたところ確かにQNANが出ることはなくなったようです。
ただし作っているプログラムの関係上この回避策は使うことができません。
プログラム自体はいわゆる地球儀のようなもので、地球の中心を
座標系の原点に、注視点を地球表面に置き、カメラの位置を
注視点を基準にした垂直・水平方向の偏角と距離で指定します。
ただしここでいう垂直・水平方向とはxy平面・xz平面のことではなく、
注視点を通り地球に接する接平面が水平面、それに垂直な法線(地球の中心を通る)が
垂直方向を指すようになっています。
ですので、カメラの上方向は天の北極方向ではなく、その地点における
注視点の座標をベクトルとして与えてやらないとカメラから見た映像が
意図した通りにならないのです。
というわけで第三引数には任意のベクトルを指定したままで
QNANを回避できれば・・・と思っています。
|
Re: MakeScreenで作成した画面の合成について ( No.13 ) |
- 名前:ゆーすけ 日時:2014/12/10 21:51
先ほどサンプルを弄っていて気づいた点がありましたので
追加で質問させていただきます。
上記の条件でZバッファの精度を上げた状態で、注視点のすぐ目の前
(カメラからの距離は1〜100くらいの間)に長さ2、底面の半径0.5の円錐を
DrawCone3Dで描画すると表示がガタガタに崩れます。
教えていただいた方法でクリップ面までの距離は最低でも
1.0f以上の値は確保してあるので大丈夫かなと思っていたのですが、
ひょっとしてこれもZバッファの精度の問題なのでしょうか?
ちなみに擬似コード中のClip(i)の値は
Clip(0)=10^8
Clip(1)=10^6
Clip(2)=10^4
Clip(3)=10^2
Clip(4)=1
のように対数的に2桁ずつ変化するようにしています。
問題の円錐形はClip(3)とClip(4)の間に挟まれた領域に
概ねいることになりますが、Near面までの距離が1でFar面までが100という条件で
表示が崩れるものなのでしょうか?
こちらについても教えていただければ幸いです。
|
Re: MakeScreenで作成した画面の合成について ( No.14 ) |
- 名前:ゆーすけ 日時:2014/12/13 09:51
その後についての続報です
ご指摘があった「カメラの上方向ベクトルを(0,1,0)にしたら
QNANが出なくなった」という点を思い出し、先ほどカメラの上方向ベクトルを
単純にTgtPosとするのではなくTgtPosを正規化して大きさを1にしたベクトルで
与えてやったところ、それまでQNANが発生していた条件でもQNANが発生せず
正常に描画できそうなことを確認しました。
カメラの上方向ベクトルの大きさが大きすぎるとConvWorldPosToScreenPos関数が
正しい値を返さなくなる場合がある、ということなのでしょうか?
|
Re: MakeScreenで作成した画面の合成について ( No.15 ) |
- 名前:管理人 日時:2014/12/14 22:46
> カメラの上方向ベクトルの大きさが大きすぎるとConvWorldPosToScreenPos関数が
> 正しい値を返さなくなる場合がある、ということなのでしょうか?
はい、SetCameraPositionAndTargetAndUpVec の内部計算の結果が桁が大きくなりすぎて
NAN になってしまい、それが原因で ConvWorldPosToScreenPos も正常な値を返せなくなっていました
とりあえず本件が float型では数値の精度が足りないことに因るものだと分かったので、
使用するデータ型や内部の計算を double 型で扱う機能を作ってみました
その機能を追加したバージョンをアップしましたので、よろしければこちらをダウンロードしてください m(_ _)m
http://homepage2.nifty.com/natupaji/DxLib/DxLibVCTest.exe // VisualC++ 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibBCCTest.exe // BorlandC++ 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibGCC_DevCppTest.exe // Dev-C++ 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibGCC_MinGWTest.exe // MinGW 用
http://homepage2.nifty.com/natupaji/DxLib/DxLibDotNet.zip // .NET用
http://homepage2.nifty.com/natupaji/DxLib/DxLibMakeTest.exe // ソース
(中身を既存のライブラリのファイルに上書きして、BCCをお使いの
場合は『再構築』を、VCをお使いの場合は『リビルド』を、
Dev-C++をお使いの方は「Rebuild All(Ctrl+F11)」をして下さい)
こちらのバージョンでは以下の関数を追加しました
// 3D処理で使用する座標値が 10000000.0f などの大きな値になっても描画の崩れを小さく
// 抑える処理を使用するかどうかを設定する、DxLib_Init の呼び出し前でのみ使用可能
// ( TRUE:描画の崩れを抑える処理を使用する( CPU負荷が少し大きくなります )
// FALSE:描画の崩れを抑える処理は使用しない( デフォルト ) )
int SetUseLarge3DPositionSupport( int UseFlag ) ;
こちらの関数を DxLib_Init を呼び出す前に TRUE を渡して実行しておくと、若干の処理負荷増の代わりに
内部で行われる計算が一部 double 型で行われるようになります( DrawCone3D のガタガタも直り、
SetCameraPositionAndTargetAndUpVec の件も以前の通り引数 UpVector に TgtPos を渡しても
問題なく動作するようになります )
更に、現在ゆーすけさんが作成されている作品の空間規模になると NAN にならないように
計算を工夫しても最終的には float型では精度が足りなくなり、DrawCone3D で発生したような
描画結果の歪みがモデルの描画でも発生することになる可能性が高いので、各種座標系のパラメータを
double 型にした関数と構造体を追加しました
まず VECTOR 構造体のメンバー変数が double型になった VECTOR_D 構造体と
MATRIX 構造体のメンバー変数が double型になった MATRIX_D 構造体を追加しました
そしてそれらを引数や戻り値として扱う関数も追加しました
基本的に既存の VGet や SetCameraPositionAndTargetAndUpVec などの関数名の末尾に
D が加わり VGetD や SetCameraPositionAndTargetAndUpVecD となった関数です
VAdd → VAddD
VCross → VCorssD
MGetRotY → MGetRotYD
MMult → MMultD
SetCameraPositionAndTargetAndUpVec → SetCameraPositionAndTargetAndUpVecD
ConvWorldPosToScreenPos → ConvWorldPosToScreenPosD
ConvScreenPosToWorldPos → ConvScreenPosToWorldPosD
MV1SetPosition → MV1SetPositionD
MV1GetFramePosition → MV1GetFramePositionD
VECTOR や MATRIX を使う全ての関数で上記の D 付き関数を追加したわけではありませんので、
追加された関数の詳細は直に DxLib.h をご覧になってみてください
あと、すべての関数に D 付きの関数が追加されたわけではない関係で、VECTOR 構造体や MATRIX 構造体の
代わりに VECTOR_D 構造体と MATRIX_D 構造体をメインで使用していると、一部で VECTOR や MATRIX に
変換する必要が発生します
なので、そのようなときの為に VECTOR 構造体を VECTOR_D 構造体に変換する関数や、
MATRIX 構造体を MATRIX_D 構造体に変換する関数なども追加しました
// float型要素のベクトルをdouble型要素のベクトルに変換する
VECTOR_D VConvFtoD( VECTOR In ) ;
// double型要素のベクトルをfloat型要素のベクトルに変換する
VECTOR VConvDtoF( VECTOR_D In ) ;
// float型要素の行列をdouble型要素の行列に変換する
int ConvertMatrixFtoD( MATRIX_D *Out, const MATRIX *In ) ;
// double型要素の行列をfloat型要素の行列に変換する
int ConvertMatrixDtoF( MATRIX *Out, const MATRIX_D *In ) ;
というわけで、よろしければ今回追加した double型を扱う関数と構造体をメインで使用してみてください
恐らく現在扱われている数値の規模でも精度によるエラーに悩まされることはなくなると思います
( ただ、Direct3D 自体が扱う浮動小数点数の精度は相変わらず単精度( float型 )なので、
Zバッファの精度に関しては以前と同じように分割描画をして頂かなければなりませんが・・・ (_ _; )
|
Re: MakeScreenで作成した画面の合成について ( No.16 ) |
- 名前:ゆーすけ 日時:2014/12/15 00:55
ありがとうございます。
ConvWorldPosToScreenPosのオーバーフローの問題は先日報告した通り
何とか回避できるようになったのですが、それ以外の物理計算等でも
double型の精度が欲しいと思っていましたので使わせていただきたいと思います。
ただ導入の仕方がいまいちよく分かっていないのですが、.NET版の場合は
プロジェクトが参照しているDLLファイルを今回UPされたバージョンのものと
単純に差し替えるだけでOKなのでしょうか?
試しに.NET版をダウンロードして中のDLLファイルを差し替えてみたのですが、
DX.のメンバーではないと怒られてしまい使うことができません。
一応プロジェクトのリビルドはやってみたのですが・・・
|
Re: MakeScreenで作成した画面の合成について ( No.17 ) |
- 名前:ゆーすけ 日時:2014/12/15 23:32
すみません。
先ほど再度ダウンロードし直してDLLファイルを差し替えたところ使えるようになっていました。
本格的な動作チェックには数日かかると思いますが、カメラの直前にDrawCone3Dで描いた円錐が
ガタガタに崩れる現象は解消されているのが確認できました。
これから他の関数についても確認していきたいと思います。
特に回転行列関係についてはfloat型では明らかに精度が足りないことが分かっていましたので、
最悪自分で同じ役割をする関数を書く必要があるかなと思っていたところで、DXライブラリ側で
double版関数が用意されたことは非常に有難いです。
対応どうもありがとうございました。
また疑問点が出てきましたら質問させていただきたいと思います。
|
Re: MakeScreenで作成した画面の合成について ( No.18 ) |
- 名前:管理人 日時:2014/12/16 01:03
> 先ほど再度ダウンロードし直してDLLファイルを差し替えたところ使えるようになっていました。
すみません、それは私が先ほど修正版をアップしたからです(汗
昨日の段階では .NET用の dll ファイルのビルドに失敗していました orz
あまりテストができていませんので、不具合や不明点がありましたらお訊ねください m(_ _;m
|