Jak symulować efekt Dopplera w grze?


14

Próbuję symulować efekt Dopplera w grze (gra wyścigowa). Nie używam konkretnej biblioteki dźwięków, która symuluje efekt, mam tylko funkcję zwrotną, w której miksuję dane.

Już wymyśliłem, jak zmienić częstotliwość próbki w funkcji miksera.

Nie wiem, jak często częstotliwość powinna się zmieniać w zależności od pozycji i prędkości odtwarzacza i emitera.

Oto co mam w grze:

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

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

1) Według Wikipedii związek między częstotliwością emitowaną a częstotliwością obserwowaną podaje:

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

gdzie c jest stałą, prędkość w ośrodku (zwykle duża liczba) vs i vr są prędkościami źródła i odbiornika w stosunku do ośrodka.

zgaduję :

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

ale myślę, że to źle, nie spowoduje żadnej zmiany częstotliwości, na przykład: jeśli vr = 0(gracz nie porusza się) i emiter mają stałą prędkość, to vri vsnie zmieni się (podczas gdy powinny).

może powinienem obliczyć prędkość odtwarzacza w stosunku do prędkości emitera?

lubię to :

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

jak vri jak vsnależy karmić?


2) wikipedia podaje także inną formułę symulującą działanie pojazdu, który pojazd mija obserwatora:

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); ?

jednak ta formuła zakłada, że ​​odbiornik się nie porusza, co nie ma miejsca w tym przypadku. jeśli gracz i emiter poruszają się z tą samą prędkością (lub małą różnicą), nie powinno być efektu dopplerowskiego. ta funkcja jest również specyficzna dla jednego przypadku, przypuszczam, że ostateczna formuła powinna być taka sama bez względu na sytuację.


EDYCJA: próbuję znaleźć poprawną formułę, używając SkimFlux:

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. 

EDYCJA 2:

Dla zainteresowanych, oto ostateczna formuła:

vec2 dist = vs.pos - vr.pos;

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

UWAGA: wykorzystuje projekcję wektorową, opisaną tutaj :

wzór projekcji

wtedy vr,si vs,rpowinien zostać wstrzyknięty w pierwszą formułę wikipedii:

wprowadź opis zdjęcia tutaj

Przetestowałem to i działa z powodzeniem, zapewniając świetne wyniki.


3
Możesz dostosować formułę, która zakłada, że ​​odbiornik się nie porusza, zastępując rzeczywisty ruch źródła jego ruchem względem odbiornika.
yoozer8,

Odpowiedzi:


9

1) Zakłada się, że oba obiekty poruszają się po tej samej linii - (jest to wyjaśnione na stronie, którą połączyłeś na Wikipedii) twój wniosek jest prawidłowy, w tej sytuacji, przy stałych prędkościach, przesunięcie częstotliwości jest stałe. Aby przesunięcie częstotliwości uległo zmianie, prędkości względne muszą się zmienić, stąd wzór 2), w sytuacji, gdy Vsjest stały, ale nie współliniowy z osią SR.

Wzór 2) jest jednak mylący: Vrnależy go rozumieć jako Vs,rskładową radialną / względną prędkości źródła.

Pamiętaj, że efekt Dopplera zależy tylko od prędkości, potrzebujesz tylko pozycji, aby znaleźć oś SR.

Edycja : to powinno pomóc ci obliczyć prędkości, musisz użyć Vs,ri Vr,rilości w formule 1:

Prędkości względne dla przesunięcia Dopplera


ok dziękuję za odpowiedź (i zdjęcie), to bardzo pomaga. teraz wszystko jest jasne, powinienem połączyć formułę 1 i 2 razem. jak wyjaśniono, formuła 2 przyda się, gdy obiekty nie są w tej samej linii. ostatnia część to dowiedzieć się vr, r i vs, r. 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)); // czy istnieje łatwiejszy / szybszy sposób na ich znalezienie? // note vr.vel i vs.vel to wektory, zielone i czerwone strzałki na obrazku SkimFlux.
tigrou

Zredagowałem pierwszy post i dodałem formułę z poprawnym formatowaniem. Czy możesz to sprawdzić? (Pierwszy raz używam gamedev stackexchange. Nie wiedziałem, że nie zwróci linii w odpowiedzi, a ten komentarz zostanie zablokowany po 5 minutach ...)
tigrou

@ user1083855 Tak, wyglądają dobrze. Jednym ze sposobów na uproszczenie / przyspieszenie byłoby zastosowanie się do sugestii Jima i użycie formuły 2) z względnym ruchem między nimi. Nie sądzę, żeby było tak samo, ponieważ rzeczywisty efekt Dopplera zależy od prędkości obu istot w stosunku do medium dźwiękowego (powietrza), ale w sytuacji gry prawdopodobnie będzie wystarczająco blisko i pozwoli ci zaoszczędzić na kosztownej operacji.
SkimFlux,

cóż, właściwie znalazłem łatwiejszy sposób na znalezienie vr, r vs, r: en.wikipedia.org/wiki/Vector_projection
tigrou

0

W przypadku XACT należy podać zmienną skalarną skoku dopplerowskiego, tj. Prędkość względną, gdzie 1,0 to ta sama prędkość, ale <1,0 jest wolniejsze, a> 1,0 jest szybsze

Dziękuję wam za kod, który przesłałem do tego fragmentu C #, w którym dźwięk jest obliczany między pozycją ekranu a wskazówką. Działa dokładnie

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.