Ein Weitwinkelobjektiv sollte sich nicht anders verhalten als andere normale Objektivmodelle. Sie haben nur ein größeres FOV (in dem D3DXMatrixPerspectiveFovLH
Sinne - ich gehe davon aus, dass Sie DirectX verwenden) oder größere Werte für links / rechts und unten / oben (im OpenGL- glFrustum
Sinne).
Ich glaube, der wirklich interessante Teil liegt in der Modellierung des Fischaugenobjektivs. Es gibt Fisheye Quake , das Sie studieren können, es kommt mit Quelle.
Die wahre Fischaugenprojektion
Die Projektion eines Fischaugenobjektivs ist jedoch stark nichtlinear. In der allgemeineren (meines Wissens auf Überwachungskameras beschränkten) Art von Linse wird ein Punkt M
im Raum auf die Oberfläche einer Einheitshalbkugel projiziert, dann wird diese Oberfläche parallel auf die Einheitsscheibe projiziert:
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
Es gibt andere Fisheye-Mappings , die interessantere Effekte erzielen können. Es liegt an dir.
Ich sehe zwei Möglichkeiten, um den Fisheye-Effekt in HLSL zu implementieren.
Methode 1: Führen Sie die Projektion auf dem Vertex-Shader durch
Vorteil : Im Code muss fast nichts geändert werden. Der Fragment Shader ist sehr einfach. Eher, als:
...
float4 screenPoint = mul(worldPoint, worldViewProjMatrix);
...
Du machst so etwas (kann wahrscheinlich sehr vereinfacht werden):
...
// 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);
...
Nachteile : Da die gesamte Rendering-Pipeline für lineare Transformationen gedacht war, ist die resultierende Projektion für Scheitelpunkte genau, aber alle Abweichungen sowie Texturkoordinaten sind falsch, und Dreiecke werden weiterhin als Dreiecke angezeigt, obwohl sie verzerrt erscheinen sollten.
Problemumgehungen : Es kann akzeptabel sein, eine bessere Annäherung zu erhalten, indem eine verfeinerte Geometrie mit mehr Dreiecksunterteilungen an die GPU gesendet wird . Dies kann auch in einem Geometrie-Shader durchgeführt werden. Da dieser Schritt jedoch nach dem Vertex-Shader ausgeführt wird, ist der Geometrie-Shader recht komplex, da er seine eigenen zusätzlichen Projektionen ausführen muss.
Methode 2: Führen Sie die Projektion auf dem Fragment-Shader durch
Eine andere Methode wäre, die Szene mit einer Weitwinkelprojektion zu rendern und dann das Bild zu verzerren, um einen Fisheye-Effekt mit einem Vollbild-Fragment-Shader zu erzielen.
Wenn der Punkt M
Koordinaten (x,y)
auf dem Fischaugenbildschirm hat, bedeutet dies, dass er Koordinaten (x,y,z)
auf der Hemisphäre hat, mit z = sqrt(1-x*x-y*y)
. Das heißt, es wurden Koordinaten (ax,ay)
in unserer Szene mit einem theta
solchen FOV gerendert a = 1/(z*tan(theta/2))
. (Nicht 100% sicher über meine Mathematik hier, ich werde heute Abend noch einmal überprüfen).
Der Fragment-Shader wäre also etwa so:
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);
}
Vorteil : Sie erhalten eine perfekte Projektion ohne Verzerrungen, abgesehen von denen aufgrund der Pixelgenauigkeit.
Nachteil : Sie können nicht die gesamte Szene physisch betrachten, da das Sichtfeld nicht 180 Grad erreichen kann. Je größer das FOV, desto schlechter die Präzision in der Bildmitte ... genau dort, wo Sie maximale Präzision wünschen.
Problemumgehungen : Der Genauigkeitsverlust kann durch Ausführen mehrerer Rendering-Durchläufe (z. B. 5) und Ausführen der Projektion nach Art einer Cube-Map verbessert werden. Eine weitere sehr einfache Lösung besteht darin, das endgültige Bild einfach auf das gewünschte Sichtfeld zuzuschneiden. Auch wenn das Objektiv selbst ein 180-Grad-Sichtfeld hat, möchten Sie möglicherweise nur einen Teil davon rendern. Dies wird als "Vollbild" -Fisheye bezeichnet (was etwas ironisch ist, da es den Eindruck erweckt, dass Sie das "Vollbild" erhalten, während es das Bild tatsächlich beschneidet).
(Hinweis: Wenn Sie dies nützlich, aber nicht klar genug fanden, teilen Sie mir dies bitte mit. Ich würde gerne einen ausführlicheren Artikel darüber schreiben.)
Es sollte sein
z = sqrt(1.0 - in_Point.x * in_Point.x - in_Point.y * in_Point.y)
, richtig?Meine GLSL-Implementierung ist:
quelle