Comment simuler un effet doppler dans un jeu?

14

J'essaie de simuler l'effet doppler dans un jeu (un jeu de course automobile). Je n'utilise pas de bibliothèque de sons spécifique qui simule l'effet, je n'ai qu'une fonction de rappel où je mixe les données.

J'ai déjà compris comment changer la fréquence d'un échantillon dans la fonction de mixage.

Ce que je ne sais pas, c'est à quel point la fréquence doit changer en fonction de la position et de la vitesse du joueur et de l'émetteur.

Voici ce que j'ai dans le jeu:

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

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

1) Selon wikipedia , la relation entre la fréquence émise et la fréquence observée est donnée par:

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

c est une constante, la vitesse dans le milieu (généralement un grand nombre) vs et vr sont les vitesses source et réceptrice par rapport au milieu.

donc je suppose :

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

mais je pense que c'est faux, cela ne produira aucun changement de fréquence, par exemple: si vr = 0(le joueur ne bouge pas) et l'émetteur ont une vitesse constante, alors vret vsne changera pas (alors qu'ils le devraient).

peut-être devrais-je calculer la vitesse du joueur par rapport à la vitesse de l'émetteur?

comme ça :

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

alors comment vret vsdevrait être nourrir?


2) wikipedia donne également une autre formule pour simuler l'effet d'un véhicule que ce véhicule passe par l'observateur:

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

cependant, cette formule suppose que le récepteur ne bouge pas, ce qui n'est pas le cas ici. si le joueur et l'émetteur se déplacent à la même vitesse (ou à une petite différence), il ne devrait pas y avoir d'effet doppler. cette fonction est également spécifique à un cas, je suppose que la formule finale devrait être la même quelle que soit la situation.


EDIT: j'essaie de trouver la bonne formule, en utilisant le post 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. 

EDIT2:

Pour les intéressés, voici la formule finale:

vec2 dist = vs.pos - vr.pos;

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

REMARQUE: il utilise une projection vectorielle, décrite ici :

formule de projection

puis vr,set vs,rdevrait être injecté dans la première formule wikipedia:

entrez la description de l'image ici

Je l'ai testé et cela fonctionne avec succès, fournissant de grands résultats.

tigrou
la source
3
Vous pouvez adapter la formule qui suppose que le récepteur ne bouge pas en remplaçant le mouvement réel de la source par son mouvement par rapport au récepteur.
yoozer8

Réponses:

9

1) Suppose que les deux objets se déplacent sur la même ligne - (cela est expliqué dans la page wikipedia que vous avez liée) votre conclusion est correcte, dans cette situation, avec des vitesses constantes, le décalage de fréquence est constant. Pour que le décalage de fréquence change, les vitesses relatives doivent changer, d'où la formule 2), pour la situation où Vsest constante mais non colinéaire avec l'axe SR.

La formule 2) est cependant trompeuse: Vrdoit être lue comme Vs,r, c'est-à-dire la composante radiale / relative de la vitesse de la source.

Veuillez noter que l'effet Doppler dépend uniquement des vitesses, vous n'avez besoin que des positions pour trouver l'axe SR.

Edit : cela devrait vous aider à comprendre les vitesses, vous devez utiliser les quantités Vs,ret Vr,ravec la formule 1:

Vitesses relatives pour le décalage Doppler

SkimFlux
la source
ok merci pour votre réponse (et photo), ça aide beaucoup. maintenant tout est clair, je devrais combiner les formules 1 et 2 ensemble. comme vous l'avez expliqué, la formule2 sera utile lorsque les objets ne sont pas sur la même ligne. la dernière partie consiste à découvrir vr, r et 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)); // existe-t-il un moyen plus facile / plus rapide de les découvrir? // note vr.vel et vs.vel sont des vecteurs, les flèches vertes et rouges sur l'image SkimFlux.
tigrou
J'ai édité le premier message et ajouté la formule avec une mise en forme correcte. Pouvez-vous les vérifier? (première fois que j'utilise gamedev stackexchange. Je ne savais pas qu'il ne garderait pas les retours de ligne en réponse, et ce commentaire est verrouillé après 5 min ...)
tigrou
@ user1083855 Oui, ceux-ci semblent corrects. Une façon de le rendre plus simple / plus rapide serait de suivre la suggestion de Jim et d'utiliser la formule 2) avec le mouvement relatif entre les deux. Je ne pense pas que ce soit vraiment la même chose car le véritable effet Doppler dépend de la vitesse des deux entités par rapport au support sonore (l'air), mais dans une situation de jeu, il sera probablement assez proche et vous fera économiser une opération cos coûteuse.
SkimFlux
eh bien, en fait, j'ai trouvé un moyen beaucoup plus facile de trouver vr, r vs, r: en.wikipedia.org/wiki/Vector_projection
tigrou
0

Pour XACT, il y a la variable scalaire de hauteur Doppler à spécifier, c'est-à-dire la vitesse relative, où 1.0 est la même vitesse, mais <1.0 est plus lent et> 1.0 est plus rapide

Merci les gars pour le code que j'ai transféré sur ce morceau de C #, où un son est calculé entre la position de l'écran et un signal. Fonctionne avec précision

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
la source