Как мне симулировать эффект Доплера в игре?

14

Я пытаюсь смоделировать эффект Доплера в игре (игра в автомобильные гонки). Я не использую определенную звуковую библиотеку, которая имитирует эффект, у меня есть только функция обратного вызова, где я смешиваю данные.

Я уже разобрался, как изменить частоту сэмпла в функции микшера.

То, что я не знаю, это то, насколько частота должна меняться в зависимости от положения игрока и эмиттера и его скорости.

Вот что у меня есть в игре:

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

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

1) Согласно википедии , соотношение между излучаемой частотой и наблюдаемой частотой определяется как:

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

где c - постоянная, скорость в среде (обычно большое число) vs и vr - скорости источника и приемника относительно среды.

так что я думаю :

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

но я думаю, что это неправильно, это не приведет к какому-либо изменению частоты, например: если vr = 0(игрок не двигается), а излучатель имеет постоянную скорость, то vrи не vsизменится (пока они должны).

Может быть, я должен рассчитать скорость игрока относительно скорости излучателя?

как это :

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

тогда как vrи vsнадо кормить?


2) В википедии также приведена другая формула для имитации воздействия транспортного средства, которое транспортное средство проезжает мимо наблюдателя:

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

однако эта формула предполагает, что приемник не двигается, что здесь не так. если игрок и эмиттер движутся с одинаковой скоростью (или небольшой разницей), эффект допплера не должен быть. эта функция также характерна для одного случая, я полагаю, что окончательная формула должна быть такой же, независимо от ситуации.


РЕДАКТИРОВАТЬ: я пытаюсь найти правильную формулу, используя сообщение 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:

Для тех, кто заинтересован, вот окончательная формула:

vec2 dist = vs.pos - vr.pos;

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

ПРИМЕЧАНИЕ: здесь используется векторная проекция, описанная здесь :

формула проекции

тогда vr,sи vs,rнужно вводить в первую формулу википедии:

введите описание изображения здесь

Я протестировал его, и он успешно работает, обеспечивая отличные результаты.

tigrou
источник
3
Вы можете адаптировать формулу, которая предполагает, что приемник не движется, заменив фактическое движение источника его движением относительно приемника.
yoozer8

Ответы:

9

1) Предполагается, что оба объекта движутся по одной и той же линии - (это объясняется на странице википедии, на которую вы ссылались). Ваш вывод верен, в этой ситуации с постоянными скоростями сдвиг частоты является постоянным. Для изменения сдвига частоты необходимо изменить относительные скорости, следовательно, формулу 2), для ситуации, когда Vsона постоянна, но не является коллинеарной с осью SR.

Однако формула 2) вводит в заблуждение: ее Vrследует понимать как Vs,rрадиальную / относительную составляющую скорости источника.

Обратите внимание, что эффект Доплера зависит только от скоростей, вам нужны только позиции, чтобы найти ось SR.

Изменить : это должно помочь вам выяснить скорости, вам нужно использовать Vs,rи Vr,rвеличины с формулой 1:

Относительные скорости для доплеровского сдвига

SkimFlux
источник
Хорошо, спасибо за ваш ответ (и изображение), это очень помогает. теперь все ясно, я должен объединить формулы 1 и 2 вместе. как вы объясняли, формула2 будет полезна, когда объекты находятся не в одной строке. последняя часть, чтобы узнать vr, r и vs, r. vr, r = vr.vel * cos (shorttest_angle_between (vr.vel, vs.pos - vr.pos)); vs, r = vs.vel * cos (shorttest_angle_between (vs.vel, vr.pos - vs.pos)); // Есть ли более простой / быстрый способ их выяснить? // примечание vr.vel и vs.vel - векторы, зеленые и красные стрелки на картинке SkimFlux.
tigrou
Я отредактировал первый пост и добавил формулу с правильным форматированием. Вы можете их проверить? (впервые я использую gamedev stackexchange. Я не знал, что он не будет хранить возврат строк в ответе, и этот комментарий заблокирован через 5 минут ...)
tigrou
@ user1083855 Да, все выглядит правильно. Один из способов сделать это проще / быстрее - это следовать предложению Джима и использовать формулу 2) с относительным движением между ними. Я не думаю, что это действительно то же самое, потому что настоящий эффект Доплера зависит от скоростей обоих объектов по отношению к звуковой среде (воздуху), но в игровой ситуации он, вероятно, будет достаточно близок и спасет вас от дорогостоящей операции cos.
SkimFlux
ну, на самом деле я нашел гораздо более простой способ найти vr, r vs, r: en.wikipedia.org/wiki/Vector_projection
tigrou
0

Для XACT существует скалярная переменная доплеровского шага, то есть относительная скорость, где 1,0 - это та же скорость, но <1,0 медленнее, а> 1,0 быстрее.

Спасибо вам, ребята, за код, который я перенес на этот кусок C #, где звук рассчитывается между положением экрана и сигналом. Работает точно

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.

Олег Скрипняк
источник