Come posso simulare un effetto doppler in un gioco?


14

Sto cercando di simulare l'effetto doppler in un gioco (un gioco di corse automobilistiche). Non sto usando una libreria di suoni specifica che simuli l'effetto, ho solo una funzione di callback in cui mescolo i dati.

Ho già capito come cambiare la frequenza di un campione nella funzione mixer.

Quello che non so è quanto la frequenza dovrebbe cambiare a seconda della posizione e della velocità del lettore e dell'emettitore.

Ecco cosa ho nel gioco:

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

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

1) Secondo Wikipedia , la relazione tra frequenza emessa e frequenza osservata è data da:

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

dove c è una costante, la velocità nel mezzo (in genere un numero elevato) vs e vr sono velocità di sorgente e di ricezione relative al mezzo.

immagino :

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

ma penso che sia sbagliato, non produrrà alcun cambiamento di frequenza, ad esempio: se vr = 0(il giocatore non si muove) e l'emettitore hanno una velocità costante, allora vre vsnon cambieranno (mentre dovrebbero).

forse dovrei calcolare la velocità del lettore relativamente alla velocità dell'emettitore?

come questo :

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

allora come vre vsdovrebbe essere nutrito?


2) Wikipedia fornisce anche un'altra formula per simulare l'effetto di un veicolo che il veicolo passa dall'osservatore:

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

tuttavia, questa formula suppone che il ricevitore non si sposti, il che non è il caso qui. se il giocatore e l'emettitore si muovono alla stessa velocità (o piccola differenza), non dovrebbe esserci alcun effetto doppler. questa funzione è specifica anche per un caso, suppongo che la formula finale dovrebbe essere la stessa, non importa la situazione.


EDIT: sto cercando di trovare la formula corretta, usando SkimFlux post:

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:

Per chi fosse interessato, ecco la formula finale:

vec2 dist = vs.pos - vr.pos;

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

NOTA: utilizza la proiezione vettoriale, descritta qui :

formula di proiezione

quindi vr,se vs,rdovrebbe essere iniettato nella prima formula di Wikipedia:

inserisci qui la descrizione dell'immagine

L'ho provato e funziona con successo, fornendo grandi risultati.


3
È possibile adattare la formula che presuppone che il ricevitore non si muova sostituendo il movimento effettivo della sorgente con il suo movimento rispetto al ricevitore.
yoozer8

Risposte:


9

1) Suppone che entrambi gli oggetti si muovano sulla stessa linea - (questo è spiegato nella pagina di Wikipedia che hai collegato) la tua conclusione è corretta, in questa situazione, con velocità costanti, lo spostamento di frequenza è costante. Affinché lo spostamento di frequenza cambi, le velocità relative devono cambiare, quindi la formula 2), per la situazione in cui Vsè costante ma non lineare con l'asse SR.

La formula 2) è tuttavia fuorviante: Vrva letto come Vs,r, cioè, il componente radiale / relativo della velocità della sorgente.

Si noti che l'effetto Doppler dipende solo dalle velocità, sono necessarie solo le posizioni per trovare l'asse SR.

Modifica : questo dovrebbe aiutarti a capire le velocità, devi usare le quantità Vs,re Vr,rcon la formula 1:

Velocità relative per lo spostamento Doppler


ok grazie per la tua risposta (e foto), aiuta molto. ora è tutto chiaro, dovrei combinare insieme la formula 1 e 2. come hai spiegato, formula2 sarà utile quando gli oggetti non sono nella stessa linea. l'ultima parte è scoprire vr, r e 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)); // c'è un modo più semplice / veloce per scoprirli? // note vr.vel e vs.vel sono vettori, le frecce verdi e rosse sull'immagine di SkimFlux.
Tigrou,

Ho modificato il primo post e ho aggiunto la formula con una formattazione corretta. Puoi controllarli? (la prima volta che utilizzo gamingev stackexchange. Non sapevo che non avrebbe mantenuto i ritorni di riga in risposta, e quel commento è bloccato dopo 5 min ...)
tigrou

@ user1083855 Sì, quelli sembrano giusti. Un modo per renderlo più semplice / veloce sarebbe seguire il suggerimento di Jim e usare la formula 2) con il relativo movimento tra i due. Non penso che sia davvero lo stesso perché il vero effetto Doppler dipende dalle velocità di entrambe le entità rispetto al mezzo sonoro (l'aria), ma in una situazione di gioco sarà probabilmente abbastanza vicino e ti farà risparmiare una costosa operazione cos.
SkimFlux,

bene, in realtà ho trovato un modo molto più semplice per trovare vr, r vs, r: en.wikipedia.org/wiki/Vector_projection
tigrou,

0

Per XACT, è necessario specificare la variabile scalare del pitch doppler, ovvero la velocità relativa, dove 1.0 è la stessa velocità, ma <1.0 è più lento e> 1.0 è più veloce

Grazie ragazzi per il codice, che ho trasferito in questo pezzo di C #, in cui viene calcolato un suono tra la posizione dello schermo e una stecca. Funziona esattamente

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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.