Một ống kính góc rộng không nên hoạt động khác so với các mẫu ống kính thông thường khác. Họ chỉ có FOV lớn hơn (theo D3DXMatrixPerspectiveFovLH
nghĩa - tôi giả sử bạn sử dụng DirectX) hoặc các giá trị trái / phải và dưới / trên lớn hơn (theo glFrustum
nghĩa OpenGL ).
Tôi tin rằng phần thực sự thú vị nằm ở mô hình ống kính mắt cá. Có Fisheye Quake mà bạn có thể nghiên cứu, nó đi kèm với nguồn.
Phép chiếu mắt cá thật
Tuy nhiên, hình chiếu của ống kính mắt cá rất phi tuyến tính. Trong loại phổ biến hơn (theo hiểu biết của tôi, chỉ giới hạn ở các camera giám sát), một điểm M
trong không gian được chiếu lên bề mặt của một bán cầu đơn vị, sau đó bề mặt đó trải qua một phép chiếu song song lên đĩa đơn vị:
M
x M: world position
\ M': projection of M on the unit hemisphere
\ ______ M": projection of M' on the unit disc (= the screen)
M'_x' `-.
,' |\ `.
/ | \ \
/ | \ \
| | \ |
__________|_____|____\_________|_________
M" O 1
Có những ánh xạ mắt cá khác có thể cho hiệu ứng thú vị hơn. Tuỳ bạn.
Tôi có thể thấy hai cách để thực hiện hiệu ứng mắt cá trong HLSL.
Phương pháp 1: thực hiện phép chiếu trên shader đỉnh
Ưu điểm : hầu như không có gì cần phải thay đổi trong mã. Các shader mảnh là cực kỳ đơn giản. Thay vì:
...
float4 screenPoint = mul(worldPoint, worldViewProjMatrix);
...
Bạn làm một cái gì đó như thế này (có thể được đơn giản hóa rất nhiều):
...
// This is optional, but it computes the z coordinate we will
// need later for Z sorting.
float4 out_Point = mul(in_Point, worldViewProjMatrix);
// This retrieves the world position so that we can project on
// the hemisphere. Normalise this vector and you get M'
float4 tmpPoint = mul(in_Point, worldViewMatrix);
// This computes the xy coordinates of M", which happen to
// be the same as M'.
out_Point.xy = tmpPoint.xy / length(tmpPoint.xyz);
...
Nhược điểm : do toàn bộ đường ống kết xuất được cho là biến đổi tuyến tính, phép chiếu kết quả là chính xác cho các đỉnh, nhưng tất cả các thay đổi sẽ sai, cũng như tọa độ kết cấu và hình tam giác vẫn sẽ xuất hiện dưới dạng hình tam giác mặc dù chúng có vẻ bị biến dạng.
Giải pháp thay thế : có thể chấp nhận được để có được xấp xỉ tốt hơn bằng cách gửi một hình học tinh chế đến GPU, với nhiều phân vùng tam giác hơn. Điều này cũng có thể được thực hiện trong một shader hình học, nhưng vì bước này xảy ra sau shader đỉnh, nên shader hình học sẽ khá phức tạp vì nó sẽ phải thực hiện các phép chiếu bổ sung của riêng nó.
Phương pháp 2: thực hiện phép chiếu trên shader mảnh
Một phương pháp khác là kết xuất cảnh bằng cách sử dụng phép chiếu góc rộng, sau đó làm biến dạng hình ảnh để đạt được hiệu ứng mắt cá bằng cách sử dụng trình tạo bóng mảnh toàn màn hình.
Nếu điểm M
có tọa độ (x,y)
trong màn hình mắt cá, điều đó có nghĩa là nó có tọa độ (x,y,z)
trên bề mặt bán cầu, với z = sqrt(1-x*x-y*y)
. Điều đó có nghĩa là nó có tọa độ (ax,ay)
trong cảnh của chúng ta được hiển thị bằng FOV theta
như vậy a = 1/(z*tan(theta/2))
. (Không chắc chắn 100% về toán học của tôi ở đây, tôi sẽ kiểm tra lại vào tối nay).
Do đó, shader mảnh sẽ là một cái gì đó như thế này:
void main(in float4 in_Point : POSITION,
uniform float u_Theta,
uniform sampler2D u_RenderBuffer,
out float4 out_FragColor : COLOR)
{
z = sqrt(1.0 - in_Point.x * in_Point.x - in_Point.y * in_Point.y);
float a = 1.0 / (z * tan(u_Theta * 0.5));
out_FragColor = tex2D(u_RenderBuffer, (in_Point.xy - 0.5) * 2.0 * a);
}
Ưu điểm : bạn có được một hình chiếu hoàn hảo không có biến dạng ngoài những điều đó do độ chính xác của pixel.
Nhược điểm : bạn không thể xem toàn bộ cảnh, vì FOV không thể đạt tới 180 độ. Ngoài ra, FOV càng lớn, độ chính xác ở trung tâm của hình ảnh càng tệ ... đó chính xác là nơi bạn muốn độ chính xác tối đa.
Giải pháp thay thế : mất độ chính xác có thể được cải thiện bằng cách thực hiện một số đường chuyền kết xuất, ví dụ 5 và thực hiện phép chiếu theo cách của bản đồ khối. Một cách giải quyết rất đơn giản khác là chỉ cần cắt hình ảnh cuối cùng thành FOV mong muốn - ngay cả khi bản thân ống kính có FOV 180 độ, bạn có thể chỉ muốn hiển thị một phần của nó. Đây được gọi là fisheye "full-frame" (thật là mỉa mai, vì nó mang lại ấn tượng rằng bạn có được cái gì đó "đầy đủ" trong khi nó thực sự cắt ảnh).
(Lưu ý: nếu bạn thấy điều này hữu ích nhưng không đủ rõ ràng, xin vui lòng cho tôi biết, tôi cảm thấy như viết một bài viết chi tiết hơn về điều này).