Bagaimana cara mensimulasikan efek doppler dalam game?

14

Saya mencoba untuk mensimulasikan efek doppler dalam permainan (permainan balap mobil). Saya tidak menggunakan pustaka suara tertentu yang mensimulasikan efek, saya hanya memiliki fungsi panggilan balik di mana saya mencampur data.

Saya sudah menemukan cara mengubah frekuensi sampel dalam fungsi mixer.

Apa yang saya tidak tahu adalah berapa frekuensi harus berubah tergantung posisi pemain dan emitor dan kecepatan.

Inilah yang saya miliki dalam permainan:

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

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

1) Menurut wikipedia , hubungan antara frekuensi yang dipancarkan dan frekuensi yang diamati diberikan oleh:

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

di mana c adalah konstanta, kecepatan dalam medium (biasanya dalam jumlah besar) vs dan vr adalah kecepatan sumber dan penerima relatif terhadap medium.

jadi saya kira:

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

tetapi saya pikir itu salah, itu tidak akan menghasilkan perubahan dalam frekuensi, misalnya: jika vr = 0(pemain tidak bergerak) dan emitor memiliki kecepatan konstan, maka vrdan vstidak akan berubah (sementara mereka harus).

mungkin saya harus menghitung kecepatan pemain relatif terhadap kecepatan emitor?

seperti ini :

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

lalu bagaimana vrdan vsharus diberi makan?


2) wikipedia juga memberikan formula lain untuk mensimulasikan efek kendaraan yang dilewati oleh pengamat:

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

Namun, rumus ini menganggap penerima tidak bergerak, yang tidak terjadi di sini. jika pemain dan emitor bergerak dengan kecepatan yang sama (atau perbedaan kecil), seharusnya tidak ada efek doppler. fungsi ini juga spesifik untuk satu kasus, saya kira rumus terakhir harus sama tanpa situasi.


EDIT: Saya mencoba mencari formula yang benar, menggunakan pos 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:

Bagi yang berminat, berikut adalah rumus terakhir:

vec2 dist = vs.pos - vr.pos;

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

CATATAN: ini menggunakan proyeksi vektor, dijelaskan di sini :

rumus proyeksi

lalu vr,sdan vs,rharus disuntikkan dalam rumus wikipedia pertama:

masukkan deskripsi gambar di sini

Saya mengujinya dan berhasil, memberikan hasil yang bagus.

tigrou
sumber
3
Anda dapat menyesuaikan rumus yang mengasumsikan bahwa penerima tidak bergerak dengan mengganti gerakan sumber yang sebenarnya dengan gerakannya yang relatif terhadap penerima.
yoozer8

Jawaban:

9

1) Diasumsikan bahwa kedua objek bergerak pada baris yang sama - (ini dijelaskan di halaman wikipedia yang Anda tautkan) kesimpulan Anda benar, dalam situasi ini, dengan kecepatan konstan, pergeseran frekuensi konstan. Untuk perubahan frekuensi untuk berubah, kecepatan relatif perlu diubah, maka rumus 2), untuk situasi di mana Vskonstan tetapi tidak colinear dengan sumbu SR.

Formula 2) menyesatkan: Vrharus dibaca sebagai Vs,rkomponen radial / relatif dari kecepatan sumber.

Harap dicatat bahwa efek Doppler hanya bergantung pada kecepatan, Anda hanya perlu posisi untuk menemukan sumbu SR.

Sunting : ini akan membantu Anda mengetahui kecepatan, Anda perlu menggunakan Vs,rdan Vr,rjumlah dengan rumus 1:

Kecepatan relatif untuk perubahan Doppler

SkimFlux
sumber
ok terima kasih atas jawaban Anda (dan gambar), itu sangat membantu. sekarang semuanya jelas, saya harus menggabungkan formula 1 dan 2 bersama-sama. seperti yang Anda jelaskan, formula2 akan berguna ketika objek tidak berada di baris yang sama. bagian terakhir adalah untuk mengetahui vr, r dan 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)); // apakah ada cara yang lebih mudah / lebih cepat untuk mengetahuinya? // note vr.vel dan vs.vel adalah vektor, panah hijau dan merah pada gambar SkimFlux.
tigrou
Saya mengedit posting pertama dan menambahkan formula dengan format yang benar. Bisakah Anda memeriksanya? (pertama kali saya menggunakan gamedev stackexchange. Saya tidak tahu itu tidak akan mengembalikan garis, dan komentar itu dikunci setelah 5 menit ...)
tigrou
@ user1083855 Ya, itu terlihat benar. Salah satu cara untuk membuatnya lebih sederhana / lebih cepat adalah dengan mengikuti saran Jim dan menggunakan rumus 2) dengan gerakan relatif di antara keduanya. Saya tidak berpikir itu benar-benar sama karena efek Doppler yang sebenarnya tergantung pada kecepatan kedua entitas relatif terhadap media suara (udara), tetapi dalam situasi permainan mungkin akan cukup dekat dan menghemat operasi cos mahal.
SkimFlux
baik, sebenarnya saya menemukan banyak cara yang lebih mudah untuk menemukan vr, r vs, r: en.wikipedia.org/wiki/Vector_projection
tigrou
0

Untuk XACT, ada variabel skalar pitch doppler yang harus ditentukan, yaitu kecepatan relatif, di mana 1,0 adalah kecepatan yang sama, tetapi <1,0 lebih lambat dan> 1,0 lebih cepat

Terima kasih kawan atas kodenya, yang telah saya transfer ke bagian C # ini, di mana suara dihitung antara posisi layar dan isyarat. Bekerja dengan tepat

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
sumber