Wie simuliere ich einen Doppler-Effekt in einem Spiel?

14

Ich versuche, den Doppler-Effekt in einem Spiel (einem Autorennspiel) zu simulieren. Ich verwende keine bestimmte Soundbibliothek, die den Effekt simuliert. Ich habe nur eine Rückruffunktion, mit der ich die Daten mische.

Ich habe bereits herausgefunden, wie man die Frequenz eines Samples in der Mixer-Funktion ändert.

Was ich nicht weiß, ist, wie sehr sich die Frequenz in Abhängigkeit von der Position und der Geschwindigkeit des Players und des Senders ändern sollte.

Folgendes habe ich im Spiel:

//player 
vec3 p.pos; 
vec3 p.vel;

//emitter 
vec3 e.pos;
vec3 e.vel;

1) Laut Wikipedia ist die Beziehung zwischen der emittierten Frequenz und der beobachteten Frequenz gegeben durch:

float f = (c + vr) / (c + vs) * fo

wobei c eine Konstante ist, ist die Geschwindigkeit in Medium (typischerweise eine große Zahl) vs und vr die Quell- und Empfängergeschwindigkeit relativ zu Medium.

also denke ich:

float vr = p.vel.length; //player speed 
float vs = e.vel.length; //emitter speed

aber ich denke, es ist falsch, es wird keine Frequenzänderung hervorrufen, zum Beispiel: wenn vr = 0(Spieler bewegt sich nicht) und Emitter eine konstante Geschwindigkeit haben, dann vrund wird sich vsnicht ändern (solange sie sollten).

Vielleicht sollte ich die Geschwindigkeit des Spielers relativ zur Geschwindigkeit des Emitters berechnen?

so was :

relative_speed = distance(p.pos + p.vel, e.pos + e.vel) -
distance(p.pos, e.pos);

wie vrund vssollte dann gefüttert werden?


2) wikipedia gibt auch eine andere Formel an, um die Wirkung eines Fahrzeugs zu simulieren, das der Beobachter passiert:

vr = vs * cos(theta);

//theta is angle between observer and emitter
//theta = atan2(e.pos.y-p.pos.y, e.pos.x-p.pos.x); ?

Diese Formel setzt jedoch voraus, dass sich der Empfänger nicht bewegt, was hier nicht der Fall ist. Wenn sich Player und Emitter mit der gleichen Geschwindigkeit (oder einem geringen Unterschied) bewegen, sollte kein Doppler-Effekt auftreten. Diese Funktion ist auch spezifisch für einen Fall, ich nehme an, die endgültige Formel sollte in jeder Situation gleich sein.


BEARBEITEN: Ich versuche die richtige Formel zu finden, indem ich SkimFlux poste:

vr,r = vr.vel * cos(shortest_angle_between ( vr.vel , vs.pos - vr.pos)); 
vs,r = vs.vel * cos(shortest_angle_between ( vs.vel , vr.pos - vs.pos)); 

//is there a easier/faster way to find them out ? 
//note: vr.vel and vs.vel are vectors, the green and red arrows on SkimFlux picture. 

EDIT2:

Für Interessierte ist hier die endgültige Formel:

vec2 dist = vs.pos - vr.pos;

vr,r = dotproduct(vr.vel, dist) / length(dist)
vs,r = dotproduct(vs.vel, dist) / length(dist)

HINWEIS: Es wird die hier beschriebene Vektorprojektion verwendet :

Projektionsformel

dann vr,sund vs,rsollte in der ersten Wikipedia-Formel injiziert werden:

Bildbeschreibung hier eingeben

Ich habe es getestet und es funktioniert erfolgreich und liefert großartige Ergebnisse.

tigrou
quelle
3
Sie können die Formel anpassen, die davon ausgeht, dass sich der Empfänger nicht bewegt, indem Sie die tatsächliche Bewegung der Quelle durch die Bewegung relativ zum Empfänger ersetzen.
yoozer8
Seite 14-15 beschreibt
OpenALs

Antworten:

9

1) Angenommen, beide Objekte bewegen sich auf derselben Linie - (dies wird auf der von Ihnen verlinkten Wikipedia-Seite erklärt), ist Ihre Schlussfolgerung richtig. In dieser Situation ist die Frequenzverschiebung bei konstanten Geschwindigkeiten konstant. Damit sich die Frequenzverschiebung ändert, müssen sich die relativen Geschwindigkeiten ändern, daher Formel 2) für die Situation, in der Vskonstant, aber nicht kolinear zur SR-Achse ist.

Formel 2) ist jedoch irreführend: Vrsollte als Vs,rdie radiale / relative Komponente der Quellgeschwindigkeit gelesen werden.

Bitte beachten Sie, dass der Doppler-Effekt nur von den Geschwindigkeiten abhängt. Sie benötigen nur die Positionen, um die SR-Achse zu finden.

Bearbeiten : Dies soll Ihnen helfen, die Geschwindigkeiten herauszufinden, die Sie benötigen, um die Vs,rund Vr,rMengen mit Formel 1 zu verwenden:

Relative Geschwindigkeiten für die Doppler-Verschiebung

SkimFlux
quelle
ok danke für deine antwort (und bild), es hilft sehr. jetzt ist alles klar, ich sollte formel 1 und 2 miteinander kombinieren. Wie Sie erklärt haben, ist formula2 will nützlich, wenn sich Objekte nicht in derselben Zeile befinden. Der letzte Teil ist es, vr, r und vs, r herauszufinden. vr, r = vr.vel * cos (kürzester Winkel zwischen (vr.vel, vs.pos - vr.pos)); vs, r = vs.vel * cos (kürzester Winkel zwischen (vs.vel, vr.pos - vs.pos)); // Gibt es einen einfacheren / schnelleren Weg, sie herauszufinden? // beachte, dass vr.vel und vs.vel Vektoren sind, die grünen und roten Pfeile auf dem SkimFlux-Bild.
Tigrou
Ich habe den ersten Beitrag bearbeitet und die Formel mit der richtigen Formatierung hinzugefügt. Kannst du sie überprüfen? (Zum ersten Mal benutze ich gamedev stackexchange. Ich wusste nicht, dass es keine Zeilenumbrüche als Antwort gibt, und dieser Kommentar ist nach 5 Minuten gesperrt ...)
tigrou
@ user1083855 Ja, die sehen richtig aus. Eine Möglichkeit, es einfacher / schneller zu machen, wäre, Jims Vorschlag zu folgen und Formel 2) mit der relativen Bewegung zwischen beiden zu verwenden. Ich denke nicht, dass es wirklich dasselbe ist, da der echte Doppler-Effekt von den Geschwindigkeiten beider Einheiten relativ zum Klangmedium (der Luft) abhängt, aber in einer Spielsituation wird es wahrscheinlich nahe genug sein und Ihnen eine teure Cos-Operation ersparen.
SkimFlux
Nun
0

Für XACT gibt es die Doppler-Tonhöhen-Skalarvariable, die angegeben werden soll, dh relative Geschwindigkeit, wobei 1,0 die gleiche Geschwindigkeit ist, aber <1,0 langsamer und> 1,0 schneller ist

Vielen Dank für den Code, den ich auf dieses Stück C # übertragen habe, bei dem ein Sound zwischen Bildschirmposition und einem Cue berechnet wird. Funktioniert genau

soundElements.ForEach(e =>
            {
                var cuePosition = new Vector3(e.PhysicPosition, 0);
                var distance = cuePosition - ScreenCenter;
                var distanceLength = distance.Length();
                e.Cue.SetVariable("Distance", distanceLength);
                var dopplerPitchScalar = 1.0f;
                if (e.AssociatedBody != null)
                {
                    ///gamedev/23583/how-do-i-simulate-a-doppler-effect-in-a-game
                    var screenVelocity = Vector3.Dot(ScreenVelocity, distance) / distanceLength;
                    var cueVelocity = Vector3.Dot(new Vector3(e.AssociatedBody.LinearVelocity, 0), distance) / distanceLength;
                    var relativeVelocity = screenVelocity - cueVelocity;
                    dopplerPitchScalar = (1f + relativeVelocity / SoundEffect.SpeedOfSound) / (1f - relativeVelocity / SoundEffect.SpeedOfSound);
                    //Console.WriteLine($"C: {ScreenCenter}, V: {ScreenVelocity}, D: {dopplerPitchScalar}");
                }
                e.Cue.SetVariable("DopplerPitchScalar", dopplerPitchScalar);
            });

Btw.

Oleg Skripnyak
quelle