เลนส์มุมกว้างไม่ควรทำงานต่างจากเลนส์รุ่นอื่นทั่วไป พวกเขามี FOV ที่มากขึ้น (ตามD3DXMatrixPerspectiveFovLH
ความหมาย - ฉันสมมติว่าคุณใช้ DirectX) หรือใหญ่กว่าค่าซ้าย / ขวาและล่าง / บนสุด (ในglFrustum
ความหมายOpenGL )
ฉันเชื่อว่าส่วนที่น่าสนใจคือการสร้างแบบจำลองเลนส์ฟิชอาย มีFisheye Quakeที่คุณสามารถเรียนได้มันมาพร้อมกับแหล่งที่มา
การฉายฟิชอายที่แท้จริง
อย่างไรก็ตามการฉายภาพของเลนส์ Fisheye นั้นไม่ใช่แบบเส้นตรงสูง จากความรู้ทั่วไปของฉันซึ่ง จำกัด อยู่ที่กล้องตรวจการณ์) M
มีการฉายจุดในอวกาศบนพื้นผิวของยูนิตซีกโลกจากนั้นพื้นผิวนั้นจะฉายภาพแบบขนานบนดิสก์ยูนิต:
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
มีการแมปฟิชอายอื่น ๆที่อาจให้ผลที่น่าสนใจมากขึ้น มันขึ้นอยู่กับคุณ.
ฉันเห็นสองวิธีในการใช้เอฟเฟ็กต์ฟิชอายใน HLSL
วิธีที่ 1: ทำการฉายภาพบน Shader ที่
ข้อได้เปรียบ : เกือบไม่มีอะไรจำเป็นต้องเปลี่ยนแปลงในรหัส ชิ้นส่วน shader ง่ายมาก ค่อนข้างมากกว่า:
...
float4 screenPoint = mul(worldPoint, worldViewProjMatrix);
...
คุณทำอะไรเช่นนี้ (อาจเป็นเรื่องง่ายมาก):
...
// 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);
...
ข้อเสีย : เนื่องจากการเรนเดอร์ไปป์ไลน์ทั้งหมดถูกคิดว่าเป็นการแปลงเชิงเส้นดังนั้นการฉายภาพที่เกิดขึ้นจึงเป็นสิ่งที่แน่นอนสำหรับจุดยอด แต่การเปลี่ยนแปลงทั้งหมดจะผิดรวมไปถึงพิกัดพื้นผิวและสามเหลี่ยมจะยังคงเป็น
วิธีแก้ปัญหา : สามารถยอมรับการประมาณที่ดีกว่าโดยการส่งรูปเรขาคณิตที่ละเอียดอ่อนไปยัง GPU พร้อมกับส่วนย่อยที่เป็นรูปสามเหลี่ยม สิ่งนี้อาจดำเนินการในรูปทรงเรขาคณิต แต่เนื่องจากขั้นตอนนี้เกิดขึ้นหลังจากจุดยอดรูปทรงเรขาคณิตรูปทรงจะค่อนข้างซับซ้อนเนื่องจากจะต้องดำเนินการคาดการณ์เพิ่มเติมของตัวเอง
วิธีที่ 2: ทำการฉายบน shader ส่วน
อีกวิธีหนึ่งก็คือการทำให้ฉากโดยใช้การฉายภาพมุมกว้างจากนั้นบิดเบือนภาพเพื่อให้ได้เอฟเฟ็กต์ฟิชอายโดยใช้ตัวแบ่งส่วนแบบเต็มหน้าจอ
หากจุดM
ที่มีพิกัด(x,y)
ในหน้าจอฟิชอายก็หมายความว่ามันมีพิกัดบนพื้นผิวซีกโลกด้วย(x,y,z)
z = sqrt(1-x*x-y*y)
ซึ่งหมายความว่ามันมีพิกัด(ax,ay)
ในฉากของเราแสดงผลด้วย FOV ของดังกล่าวว่าtheta
a = 1/(z*tan(theta/2))
(ไม่แน่ใจ 100% เกี่ยวกับคณิตศาสตร์ของฉันที่นี่ฉันจะตรวจสอบอีกครั้งในคืนนี้)
ชิ้นส่วน shader จะเป็นดังนี้:
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);
}
ข้อได้เปรียบ : คุณจะได้ภาพที่สมบูรณ์แบบโดยไม่มีการบิดเบือนนอกเหนือจากความถูกต้องของพิกเซล
ข้อเสียเปรียบ : คุณไม่สามารถดูทั้งฉากได้เนื่องจาก FOV ไม่สามารถไปถึง 180 องศาได้ ยิ่งค่า FOV ยิ่งมากเท่าไหร่ความแม่นยำก็จะยิ่งแย่ลงที่จุดศูนย์กลางของภาพ ... ซึ่งเป็นจุดที่คุณต้องการความแม่นยำสูงสุดอย่างแม่นยำ
วิธีแก้ปัญหา : การสูญเสียความแม่นยำสามารถปรับปรุงได้โดยดำเนินการเรนเดอร์หลายตัวอย่างเช่น 5 และทำการฉายภาพในลักษณะของแผนที่ลูกบาศก์ วิธีแก้ปัญหาที่ง่ายมากอีกวิธีหนึ่งคือการครอบตัดภาพสุดท้ายไปยัง FOV ที่ต้องการ - แม้ว่าตัวเลนส์เองจะมี FOV 180 องศาคุณอาจต้องการแสดงเพียงบางส่วนเท่านั้น สิ่งนี้เรียกว่าฟิชอาย "ฟูลเฟรม" (ซึ่งค่อนข้างน่าขันเพราะมันให้ความรู้สึกว่าคุณได้รับ "เต็ม" บางอย่างในขณะที่มันครอบตัดภาพ)
(หมายเหตุ: หากคุณพบว่ามีประโยชน์ แต่ไม่ชัดเจนพอโปรดบอกฉันฉันรู้สึกเหมือนเขียนบทความโดยละเอียดเกี่ยวกับเรื่องนี้)