7

OSX で高解像度、高フレームレートでマウスの動きを取得したいと考えています。

「高フレームレート」 = 60 fps 以上 (できれば > 120)
「高解像度」 = サブピクセル値

問題
ほぼモニターのリフレッシュ レートで実行されている opengl ビューを持っているので、約 60 fps です。マウスを使って周りを見回すので、マウス カーソルを非表示にし、マウスのデルタ値に依存しています。

問題は、マウス イベントが非常に低いフレームレートで発生し、値が整数 (ピクセル全体) にスナップされることです。これにより、「途切れ途切れ」の表示エクスペリエンスが発生します。以下は、時間の経過に伴うマウス デルタ値の視覚化です。

    mouse delta X
    ^                xx
  2 |      x    x x     x xx
    | x x x   x             xx x  x x
  0 |x-x-x--xx-x-x-xx--x-x----x-xx-x-----> frame
    |
-2  |
    v

これは、ユーザーがマウスを少し右に動かして作成した典型的な (短縮された) 曲線です。各 x は各フレームの deltaX 値を表し、deltaX 値は整数に丸められるため、このグラフは実際には非常に正確です。ご覧のとおり、deltaX の値はあるフレームでは 0.000 になり、次のフレームでは 1.000 になりますが、その後再び 0.000 になり、次に 2.000、次に再び 0.000、次に 3.000、0.000 などになります。

これは、ビューが 1 フレームで 2.000 単位回転し、次のフレームで 0.000 単位回転し、さらに 3.000 単位回転することを意味します。これは、マウスがほぼ一定の速度でドラッグされているときに発生します。言うまでもなく、これはがらくたのように見えます。

では、どうすれば 1) マウスのイベント フレームレートを上げることができますか? 2) サブピクセル値を取得しますか?

これまで
のところ、次のことを試しました。

- (void)mouseMoved:(NSEvent *)theEvent {
    CGFloat dx, dy;
    dx = [theEvent deltaX];
    dy = [theEvent deltaY];
    // ...
    actOnMouse(dx,dy);
}

なるほど、これは一目瞭然でした。dxここでは float ですが、値は常に丸められます (0.000、1.000 など)。これにより、上のグラフが作成されます。

したがって、次のステップは、マウス イベントが WindowServer に入る前にタップすることだと思いました。だから私は CGEventTrap を作成しました:

eventMask = (1 << kCGEventMouseMoved);
eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
            0, eventMask, myCGEventCallback, NULL);
//...
myCGEventCallback(...){
    double dx = CGEventGetDoubleValueField(event, kCGMouseEventDeltaX);
    double dy = CGEventGetDoubleValueField(event, kCGMouseEventDeltaY);
}

n.000イベントの発火率は少し高いと思いますが、それでも値はです。しかし、それはまだ60 fpsではありません。私はまだ上のチャートを取得します。

また、マウスの感度を非常に高く設定してから、値を縮小してみました。しかし、OSX はある種のアクセラレーションか何かを追加しているようです。値は本当に「不安定」になり、結果として使用できなくなり、発射速度は依然として低すぎます。

運が悪かったので、うさぎの穴を下ってマウス イベントを追跡し始め、IOKit にたどり着きました。これは私にとって恐ろしいことです。マッドハッターです。Apple のドキュメンテーションは奇妙になり、「あなたがこれほど深いところにいるなら、本当に必要なのはヘッダファイルだけだ」と言っているようです。

だから私はヘッダーファイルを読んでいます。で、面白いネタを見つけました。

<IOKit/hidsystem/IOLLEvent.h>377 行目には、次の構造体があります。

struct {    /* For mouse-down and mouse-up events */
    UInt8   subx;       /* sub-pixel position for x */
    UInt8   suby;       /* sub-pixel position for y */
    // ...
} mouse;

ほら、サブピクセル位置と書いてあります!Ok。次に、73行目で<IOKit/hidsystem/IOLLParameter.h>

#define kIOHIDPointerResolutionKey      "HIDPointerResolution"

うーん。

全体として、OSXはサブピクセルのマウス座標を深く知っているように感じます.フレームごとに生のマウスの動きを読み取る方法が必要ですが、それらの値を取得する方法がわかりません.

質問
ええと、私は何を求めていますか?

  • OSXで高フレームレートのマウスイベントを取得する方法はありますか? (サンプルコード?)
  • OSXでサブピクセルのマウス座標を取得する方法はありますか? (サンプルコード?)
  • フレームごとに「生の」マウスデルタを読み取る方法はありますか? (つまり、イベントに依存しません。)
  • または、どのように NXEvents を取得したり、HIDParameters を設定したりできますか? サンプルコード?(だから私は自分でこれをもっと深く掘り下げることができます...)

(長文すみません)

4

4 に答える 4

7

(これは非常に遅い答えですが、これに出くわした他の人にとってはまだ役立つと思います。)

マウス入力をフィルタリングしてみましたか? フィルタリングはラグと精度の間のトレードオフになる傾向があるため、これは注意が必要です。ただし、何年も前に、マウスの動きをフィルタリングする方法を説明する記事を書き、ゲーム開発サイトの記事を書きました。リンクはhttp://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtmlです。

そのサイトはもはや活発な開発が行われていないため (そしてなくなる可能性があります)、関連する抜粋を以下に示します。


ほとんどの場合、フィルタリングは平均化を意味します。ただし、時間の経過とともにマウスの動きを単純に平均すると、ラグが発生します。では、副作用なしでフィルタリングするにはどうすればよいでしょうか。平均化は引き続き使用しますが、ある程度の知性を持って実行します。同時に、ユーザーがフィルタリングを細かく制御できるようにして、ユーザーが自分で調整できるようにします。

経時的な平均マウス入力の非線形フィルターを使用します。古い値は、フィルター処理された結果に与える影響が少なくなります。

使い方

マウスを動かすかどうかに関係なく、フレームごとに、現在のマウスの動きを履歴バッファーに入れ、最も古い履歴値を削除します。したがって、履歴には常に X 個のサンプルが含まれます。ここで、X は「履歴バッファー サイズ」であり、時間の経過とともにサンプリングされた最新のマウスの動きを表します。

10 のヒストリ バッファ サイズを使用し、バッファ全体の標準的な平均を使用した場合、フィルタによって多くの遅延が発生します。60FPS マシンでは、マウスの高速移動は 1/6 秒遅れます。高速アクション ゲームでは、これは非常にスムーズですが、事実上使用できません。同じシナリオで、履歴バッファ サイズを 2 にすると、遅延はほとんど発生しませんが、フィルタリングが非常に貧弱になります (プレイヤーの反応が荒くてぎくしゃくします)。

非線形フィルターは、この相互排他的なシナリオに対処することを目的としています。アイデアはとてもシンプルです。履歴バッファ内のすべての値を盲目的に均等に平均化するのではなく、重みを付けて平均化します。重み 1.0 から始めます。したがって、ヒストリ バッファの最初の値 (現在のフレームのマウス入力) の重みが最大になります。次に、この重みに「重み修飾子」(0.2 など) を掛けて、履歴バッファーの次の値に進みます。(履歴バッファーを介して) 時間をさかのぼるほど、最終結果に対する値の重み (影響) が少なくなります。

詳細に説明すると、0.5 のウェイト モディファイアを使用すると、現在のフレームのサンプルのウェイトは 100%、前のサンプルのウェイトは 50%、次に古いサンプルのウェイトは 25%、次のサンプルのウェイトは 12.5% というようになります。これをグラフにすると曲線に見えます。したがって、重みモディファイアの背後にある考え方は、履歴のサンプルが古くなるにつれて曲線がどれほど急激に低下するかを制御することです。

ラグを減らすことは、重量修飾子を減らすことを意味します。重み修飾子を 0 に減らすと、生のフィルタリングされていないフィードバックがユーザーに提供されます。これを 1.0 に増やすと、結果は履歴バッファー内のすべての値の単純な平均になります。

細かい制御のために、履歴バッファ サイズと重み修飾子の 2 つの変数をユーザーに提供します。私は 10 のヒストリ バッファ サイズを使用する傾向があり、満足するまでウェイト モディファイアをいじります。

于 2014-02-26T17:27:43.727 に答える
1

Mac OS Xは解像度に依存しないように設計されているため、サブピクセル座標の可能性があります。画面上の2x2ハードウェアピクセルの正方形は、ソフトウェアの単一の仮想ピクセルを表すことができ、カーソルをに置くことができます(x + 0.5, y + 0.5)

通常の1倍スケーリングを使用する実際のMacでは、マウスカーソルを画面上のピクセル位置に移動できないため、サブピクセル座標は表示されません。マウスの移動量は正確に1ピクセルです。

于 2011-07-22T14:11:54.570 に答える
1

イベント ディスパッチ システムが提供するレベルよりも低いレベルでポインター デバイスのデルタ情報にアクセスする必要がある場合は、おそらくユーザー空間の USB APIを使用する必要があります。

于 2011-07-22T14:53:24.320 に答える