Làm cách nào để mô phỏng hiệu ứng doppler trong trò chơi?


14

Tôi đang cố gắng mô phỏng hiệu ứng doppler trong một trò chơi (một trò chơi đua xe). Tôi không sử dụng một thư viện âm thanh cụ thể mô phỏng hiệu ứng, tôi chỉ có chức năng gọi lại nơi tôi trộn dữ liệu.

Tôi đã tìm ra cách thay đổi tần số của một mẫu trong chức năng trộn.

Điều tôi không biết là tần số sẽ thay đổi bao nhiêu tùy thuộc vào vị trí và tốc độ phát của người chơi và người phát.

Đây là những gì tôi có trong trò chơi:

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

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

1) Theo wikipedia , mối quan hệ giữa tần số phát ra và tần số quan sát được đưa ra bởi:

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

nơi c là một hằng số, vận tốc trong môi trường (thường là một số lượng lớn) vsvr là nguồn và máy thu vận tốc tương đối so với trung bình.

vì vậy tôi đoán:

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

nhưng tôi nghĩ sai của nó, nó sẽ không tạo ra bất kỳ thay đổi trong tần số, cho ví dụ: nếu vr = 0(máy nghe nhạc dont di chuyển) và emitter có tốc độ không đổi, sau đó vrvssự thay đổi sẽ không (trong khi họ cần).

Có lẽ tôi nên tính vận tốc của người chơi tương đối với vận tốc của bộ phát?

như thế này :

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

Sau đó nên làm thế nào vrvsnên cho ăn?


2) wikipedia cũng đưa ra một công thức khác để mô phỏng hiệu ứng của một phương tiện mà phương tiện đi qua người quan sát:

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

tuy nhiên, công thức này giả sử người nhận không di chuyển, đây không phải là trường hợp ở đây. nếu người chơi và bộ phát di chuyển cùng tốc độ (hoặc chênh lệch nhỏ), sẽ không có hiệu ứng doppler. Hàm này cũng đặc trưng cho một trường hợp, tôi cho rằng công thức cuối cùng sẽ giống như không có tình huống nào.


EDIT: Tôi đang cố gắng tìm công thức chính xác, sử dụng bài đăng 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:

Đối với những người quan tâm, đây là công thức cuối cùng:

vec2 dist = vs.pos - vr.pos;

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

LƯU Ý: nó sử dụng phép chiếu vector, được mô tả ở đây :

công thức chiếu

sau đó vr,svs,rnên được tiêm trong công thức wikipedia đầu tiên:

nhập mô tả hình ảnh ở đây

Tôi đã thử nghiệm nó và nó hoạt động thành công, cung cấp kết quả tuyệt vời.


3
Bạn có thể điều chỉnh công thức giả định rằng máy thu không di chuyển bằng cách thay thế chuyển động thực tế của nguồn bằng chuyển động của nó so với máy thu.
yoozer8

Câu trả lời:


9

1) Giả sử rằng cả hai đối tượng đang di chuyển trên cùng một đường - (điều này được giải thích trong trang wikipedia mà bạn đã liên kết) kết luận của bạn là chính xác, trong tình huống này, với vận tốc không đổi, sự thay đổi tần số là không đổi. Để thay đổi tần số thay đổi, vận tốc tương đối cần thay đổi, do đó, công thức 2), trong trường hợp Vskhông đổi nhưng không phải là colinear với trục SR.

Công thức 2) tuy nhiên gây hiểu nhầm: Vrnên được đọc là Vs,r, thành phần xuyên tâm / tương đối của vận tốc nguồn.

Xin lưu ý rằng hiệu ứng Doppler chỉ phụ thuộc vào vận tốc, bạn chỉ cần các vị trí để tìm trục SR.

Chỉnh sửa : điều này sẽ giúp bạn tìm ra vận tốc, bạn cần sử dụng Vs,rVr,rsố lượng với công thức 1:

Vận tốc tương đối cho sự thay đổi Doppler


ok cảm ơn bạn đã trả lời (và hình ảnh), nó giúp rất nhiều. Bây giờ mọi thứ đã rõ ràng, tôi nên kết hợp công thức 1 và 2 lại với nhau. như bạn đã giải thích, công thức 2 sẽ hữu ích khi các đối tượng không cùng dòng. phần cuối cùng là tìm ra vr, r và vs, r. vr, r = vr.vel * cos (shortest_angle_b between (vr.vel, vs.pose - vr.pose)); vs, r = vs.vel * cos (shortest_angle_b between (vs.vel, vr.pose - vs.pose)); // có cách nào dễ dàng hơn / nhanh hơn để tìm ra chúng không? // lưu ý vr.vel và vs.vel là các vectơ, mũi tên màu xanh lá cây và màu đỏ trên hình ảnh SkimFlux.
tigrou

Tôi chỉnh sửa bài đầu tiên và thêm công thức với định dạng chính xác. Bạn có thể kiểm tra chúng? (lần đầu tiên tôi sử dụng gamedev stackexchange. Tôi không biết nó sẽ không trả lời dòng và nhận xét đó bị khóa sau 5 phút ...)
tigrou

@ user1083855 Vâng, những cái đó nhìn đúng. Một cách để làm cho nó đơn giản / nhanh hơn là làm theo gợi ý của Jim và sử dụng công thức 2) với chuyển động tương đối giữa cả hai. Tôi không nghĩ nó thực sự giống nhau bởi vì hiệu ứng Doppler thực sự phụ thuộc vào vận tốc của cả hai thực thể so với môi trường âm thanh (không khí), nhưng trong tình huống trò chơi, nó có thể sẽ đủ gần và tiết kiệm cho bạn một hoạt động cos đắt tiền.
SkimFlux

tốt, thực sự tôi đã tìm thấy một cách dễ dàng hơn nhiều để tìm vr, r vs, r: en.wikipedia.org/wiki/Vector_projection
tigrou

0

Đối với XACT, cần chỉ định biến vô hướng độ dốc doppler, tức là tốc độ tương đối, trong đó 1.0 là cùng tốc độ, nhưng <1.0 chậm hơn và> 1.0 nhanh hơn

Cảm ơn các bạn về mã mà tôi đã chuyển đến đoạn C # này, trong đó âm thanh được tính giữa vị trí màn hình và tín hiệu. Hoạt động chính xá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.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.