Mausglättung

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Mausglättung

Beitrag von Niki »

(Weitergeführt von hier)

So, die pro Frame akkumulierte Bewegungsrichtung habe ich nun einfach mit der Framezeit in Sekunden multipliziert, und dann nochmal mit dem Faktor SPI_GETMOUSESPEED / 10. Darauf wende ich dann noch meine spiellokale Sensitivität und das Smoothing an. Funktioniert wunderbar, und ist schön smooth, ohne das die Maus zu stark nachzieht.
Zuletzt geändert von Krishty am 16.04.2013, 19:12, insgesamt 1-mal geändert.
Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Raw Input für die Maus

Beitrag von Krishty »

Niki hat geschrieben:So, die pro Frame akkumulierte Bewegungsrichtung habe ich nun einfach mit der Framezeit in Sekunden multipliziert
Ist das Einbeziehen der Frame-Dauer nicht unnötig? Wenn du länger rechnest, treffen auch mehr Pakete ein, und deine akkumulierte Bewegungsrichtung wird von allein größer. Ich kann mir vorstellen, dass es sonst einen riesigen gefühlten Unterschied zwischen hohen und niedrigen Aktualisierungsraten gibt …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Raw Input für die Maus

Beitrag von Niki »

Krishty hat geschrieben:
Niki hat geschrieben:So, die pro Frame akkumulierte Bewegungsrichtung habe ich nun einfach mit der Framezeit in Sekunden multipliziert
Ist das Einbeziehen der Frame-Dauer nicht unnötig? Wenn du länger rechnest, treffen auch mehr Pakete ein, und deine akkumulierte Bewegungsrichtung wird von allein größer. Ich kann mir vorstellen, dass es sonst einen riesigen gefühlten Unterschied zwischen hohen und niedrigen Aktualisierungsraten gibt …
Tja, Krishty, da stellst du eine Frage über die ich schon gestern nachgedacht habe. Mein Hirn hat weniger Probleme mit Kollisionsberechnungen als mit diesem einfachen Problem.

Aber eins ist klar. Wenn die Framezeit eine Rolle spielt, dann muss es eine Division sein und keine Multiplikation, denn es interessiert höchstens die Bewegung pro Sekunde. Der Grund warum mein Hirn nun streikt ist folgender. Ich merke mir die akkumulierten Bewegungen von mehreren Frames in einem History-Array. Die tatsächliche gesmoothte Bewegung für einen Frame berechne ich mittels einer gewichteten Summe des History-Arrays. Wenn ich aber nun die Zeit nicht berücksichtige, dann ist doch das Smoothing falsch, oder nicht? (weil die einzelnen Bewegungswerte im History-Array für unterschiedliche Zeitspannen wären)

Hier mal ein Code Snippet von dem was ich tue, nachdem eine neue Bewegungsrichtung akkumuliert wurde (also nachdem alle WM_INPUT's des aktuellen Frames summiert wurden). Ich hoffe der Code und die Variablen sind selbsterklärend. Wenn nicht, dann bitte fragen. Nur soviel: m_sensitivity und m_smoothingStrength sind Werte die der Spieler später im Optionsmenü beeinflussen kann. In m_motionVector steht das gesmoothte Ergebnis für den neuen Frame.

Code: Alles auswählen

void QxMouseInputDevice::EndUpdating(double elapsedSeconds)
{
    int nativeMouseSpeed;
    ::SystemParametersInfo(SPI_GETMOUSESPEED, 0, &nativeMouseSpeed, 0);
    double navtiveMouseSensitivity = double(nativeMouseSpeed) / 10.0;

    // Add the new motion vector to the motion history
    for (int i = MotionHistorySize - 1; i >= 1; i--)
    {
        m_motionHistory[i] = m_motionHistory[i - 1];
    }

    m_motionAccumulator.x /= elapsedSeconds;
    m_motionAccumulator.y /= elapsedSeconds;

    m_motionHistory[0].x = navtiveMouseSensitivity * m_motionAccumulator.x * m_sensitivity.x;
    m_motionHistory[0].y = navtiveMouseSensitivity * m_motionAccumulator.y * m_sensitivity.y;

    QxVector2d weightModifier;
    weightModifier.x = ::qxClamp(m_smoothingStrength.x, 0.0, 1.0);
    weightModifier.y = ::qxClamp(m_smoothingStrength.y, 0.0, 1.0);

    // Calculate the motion vector
    QxVector2d weight(1.0, 1.0);

    m_motionVector = QxVector2d::Zero;

    for (int i = 0; i < MotionHistorySize; i++)
    {
        m_motionVector.x += m_motionHistory[i].x * weight.x;
        m_motionVector.y += m_motionHistory[i].y * weight.y;
        
        weight.x *= weightModifier.x;
        weight.y *= weightModifier.y;
    }
}
EDIT: Noch ein kleiner Anhang, der wahrscheinlich in die ganze Überlegung einfließen muss. Im Spiel selbst habe ich dann so etwas wie (Pseudo-Code):

Code: Alles auswählen

camera.heading += elapsedSeconds * angularVelocity * pMouseDevice->GetMotionVector().x;
camera.elevation += elapsedSeconds * angularVelocity * pMouseDevice->GetMotionVector().y;
Wenn ich die Zeit beim Smoothing einbeziehe, dann muss ich die angularVelocity um ein vielfaches kleiner wählen um ein ordentliches Ergebnis zu erzielen.
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Raw Input für die Maus

Beitrag von Niki »

Hier mein neuestes Resultat, welches zumindest bei mir erstklassig funktioniert. Allerdings habe ich nur eine Maus zum Testen. Die Hauptänderung ist die Multiplikation mit elapsedSeconds am Ende der Methode.

Mich würde wirklich mal interessieren was ein Mathematiker darüber denkt. Haben wir hier ein paar davon? :) Dann könnte man sich auch sicher sein ob die Framezeit überhaupt drin sein darf, oder vielleicht sogar drin sein muss.

Code: Alles auswählen

void QxMouseInputDevice::EndUpdating(double elapsedSeconds)
{
    // Get the system's mouse sensitivity, which is an integer between 1 and 20.
    int nativeMouseSensitivity = 0;
    ::SystemParametersInfo(SPI_GETMOUSESPEED, 0, &nativeMouseSensitivity, 0);
    nativeMouseSensitivity = ::qxClamp(nativeMouseSensitivity, 1, 100); 

    // Calculate the final mouse sensitivity by combining the system's mouse sensitivity
    // with the game's custom mouse sensitivity.
    double mouseSensitivityX = m_sensitivity.x * static_cast<double>(nativeMouseSensitivity) / 10.0;
    double mouseSensitivityY = m_sensitivity.y * static_cast<double>(nativeMouseSensitivity) / 10.0;

    // Add the new accumulated motion vector to the motion history. The vector is remapped such
    // that it represents motion per second.
    for (int i = MotionHistorySize - 1; i >= 1; i--)
    {
        m_motionHistory[i] = m_motionHistory[i - 1];
    }

    m_motionAccumulator.x /= elapsedSeconds;
    m_motionAccumulator.y /= elapsedSeconds;

    m_motionHistory[0].x = m_motionAccumulator.x * mouseSensitivityX;
    m_motionHistory[0].y = m_motionAccumulator.y * mouseSensitivityY;

    // Calculate a weight modifier from the smoothing strength. This value should be chosen to be
    // relatively small (e.g., 0.5).
    QxVector2d weightModifier;
    weightModifier.x = ::qxClamp(m_smoothingStrength.x, 0.0, 0.8);
    weightModifier.y = ::qxClamp(m_smoothingStrength.y, 0.0, 0.8);

    // Calculate the final smooted motion vector, as the weighted sum of all motion vectors in
    // history.
    QxVector2d weight(1.0, 1.0);

    m_motionVector = QxVector2d::Zero;

    for (int i = 0; i < MotionHistorySize; i++)
    {
        m_motionVector.x += m_motionHistory[i].x * weight.x;
        m_motionVector.y += m_motionHistory[i].y * weight.y;
        
        // Modify the weight with the weight modifier. This has the effect that older motion 
        // vectors have less weight than newer motion vectors.
        weight.x *= weightModifier.x;
        weight.y *= weightModifier.y;
    }

    // Finally, multiply by the number seconds that elapsed during the last frame. This is the
    // same as multiplying each history motion vector by the elapsed time during the weighting
    // calculation.
    m_motionVector.x *= elapsedSeconds;
    m_motionVector.y *= elapsedSeconds;
}
Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Mausglättung

Beitrag von Krishty »

Hmmmmm, das ist knifflig. Durch die Division rechnest du die Maus-Samples (die in Bruchteilen von Inches gegeben sind) in Geschwindigkeiten um, also m÷s.

Deine History muss eine feste Länge haben; je nachdem, wie viele Samples pro Sekunde eintreffen. Samplet die Maus ihre Position mit 100 Hz und deine History hat eine Länge von 10, ergibt das eine Zehntelsekunde. Das setzt aber voraus, dass du Null-Samples darin speicherst – und zwar exakt mit der Frequenz, wie sonst die Bewegungen eintreffen, und nicht mit der Aktualisierungsrate deiner Engine! Ich weiß nicht, wie du das schaffen willst, weil Raw Input bei mir keine Null-Aktualisierungen der Maus schickt.

Jedenfalls: Wenn du das gemacht hast, kannst du eine Durchschnittsgeschwindigkeit (immernoch m÷s) mit abnehmender Gewichtung zur Vergangenheit berechnen. Die nochmal mit der aktuellen Engine-Aktualisierungsrate multipliziert sollte dann ein weiches Delta (m÷s•s == m) geben, das du als Rotation auf deinen Betrachter anwenden kannst.

Wichtig ist jedenfalls bei der ganzen Glättung, dass du dir bewusst bist, dass die Maus-Eingaben mit Maus-Aktualisierungsrate kommen und die Ausgaben in Engine-Aktualisierungsrate rauskommen müssen. Deine Funktion braucht also definitv zwei Parameter, sonst verhält sie sich bei unterschiedlicher Framerate oder unterschiedlich schneller Maus auch deutlich anders, bzw. glättet nicht richtig falls nicht exakt einmal pro Frame ein Maus-Sample eintrifft.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Mausglättung

Beitrag von Niki »

Wo ist der Zug? :shock: Den muss ich wohl verpasst haben. Na, ja, zumindest teilweise, aber Danke das du's versuchst :)

Also frage ich einfach mal andersrum. Wie würdest du denn ein Mouse-Smoothing realisieren (mit nur den Informationen die uns einfach zugänglich sind)? Mir geht es ja nur darum, dass ich einen einstellbar weichen Mouse-Look habe der sich bei unterschiedlichen Mäusen nicht komplett unterschiedlich verhält. Irgendwie muss das doch recht einfach zu machen sein, denn es gibt ja genug Spiele die das unterstützen. Und die werden die Abtastrate wohl genauso wenig kennen wie wir.

EDIT: Danke fürs Trennen der Posts. Wir sind durchaus vom Thema abgekommen.
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Mausglättung

Beitrag von Niki »

Ich könnte jetzt wohl in den Tisch beißen... Da war ein Fehler in meiner Kamera! Auch ohne Smoothing ist's jetzt prima... also ab damit in die Tonne! Nur die Sensitivität bleibt drinnen. So eine Zeitverschwendung...

@Krishty: Sorry, wenn ich dir damit deine Zeit geraubt habe, aber damit hatte ich nicht gerechnet.
Antworten