HLSL ile geniş açılı / balık gözü lensi nasıl oluştururum?


29

Değişken uçlardaki geniş açılı lenslerin etkisini elde etmek için uygulanması gereken kavramlar nelerdir?

İçerik boru hattının çeşitli aşamalarına atıfta bulunan sözde kod ve özel açıklama, ayrıca kaynak kodundan HLSL'ye hangi bilgilerin aktarılması gerektiğine çok yardımcı olacaktır.

Ayrıca, geniş açılı lens ile balık gözü uygulama arasındaki farklar nelerdir?

Yanıtlar:


37

Geniş açılı bir mercek diğer normal mercek modellerinden farklı davranmamalıdır. Onlar sadece daha büyük bir FOV (yani D3DXMatrixPerspectiveFovLH- DirectX kullandığınız varsayılmaktadır) veya daha büyük sol / sağ ve alt / üst değerler (OpenGL glFrustumanlamında) var.

Gerçekten ilginç olan kısmın balık gözü lensin modellenmesinde olduğuna inanıyorum. Orada Balık Gözü Deprem bu kaynağın birlikte geliyordu eğitim görebilirsiniz.

Gerçek balıkgözü izdüşümü

Bununla birlikte, bir balık gözü lensin çıkıntısı doğrusal değildir. Daha yaygın olarak (güvenlik kameraları ile sınırlı olan bilgilerime göre) bir çeşit mercek, Mbir birim yarımkürenin yüzeyine uzayda bir nokta yansıtılır, daha sonra bu yüzey birim disk üzerine paralel bir çıkıntıya uğrar:

           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

Daha ilginç efektler verebilecek başka balıkgözü haritaları vardır. Sana kalmış.

HLSL'de balık gözü etkisini uygulamak için iki yol görebiliyorum.

Yöntem 1: tepe noktası gölgelendiricisi üzerinde izdüşümü gerçekleştir

Avantaj : Neredeyse hiçbir şeyin kodda değiştirilmesi gerekmez. Parça gölgelendirici son derece basittir. Ziyade:

...
float4 screenPoint = mul(worldPoint, worldViewProjMatrix);
...

Böyle bir şey yaparsınız (muhtemelen çok basitleştirilebilir):

...
// 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);
...

Dezavantajları : Tüm oluşturma boru hattı doğrusal dönüşümler için düşünüldüğünden, sonuçta elde edilen projeksiyon köşeler için kesindir, ancak bütün farklılıklar yanlış olacaktır, ayrıca doku koordinatları ve üçgenler deforme olmuş gibi görünmelerine rağmen yine de üçgenler olarak görünecektir.

Çözümler : GPU'ya daha ince bir alt bölümle rafine bir geometri göndererek daha iyi bir yaklaşım elde etmek kabul edilebilir. Bu aynı zamanda bir geometri gölgelendiricisinde de gerçekleştirilebilir, ancak bu adım köşe gölgelendiriciden sonra gerçekleştiğinden, geometri gölgelendiricisi oldukça karmaşık olur çünkü kendi ek projeksiyonlarını gerçekleştirmek zorunda kalır.

Yöntem 2: projeksiyon parçasını gölgelendirici üzerinde gerçekleştir

Başka bir yöntem, sahneyi geniş açılı bir projeksiyon kullanarak oluşturmak ve ardından bir tam ekran fragman gölgelendirici kullanarak balık gözü efekti elde etmek için görüntüyü bozmaktır.

Nokta balıkgözü ekranında Mkoordinatlara sahipse , yarım küre yüzeyinde (x,y)koordinatlara sahip olduğu anlamına gelir . Bu , bizim sahnemizde böyle bir FOV ile oluşturulan koordinatlarına sahip olduğu anlamına gelir . (Buradaki matematiğimden% 100 emin değil, bu gece tekrar kontrol edeceğim).(x,y,z)z = sqrt(1-x*x-y*y)(ax,ay)thetaa = 1/(z*tan(theta/2))

Dolayısıyla, parça gölgelendirici şöyle bir şey olur:

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

Avantaj : piksel doğruluğu nedeniyle hiçbir bozulma olmadan mükemmel bir projeksiyon elde edersiniz.

Dezavantaj : FOV 180 dereceye ulaşamadığından tüm sahneyi fiziksel olarak görüntüleyemezsiniz. Ayrıca, FOV ne kadar büyük olursa, görüntünün ortasındaki hassasiyet de o kadar kötü olur ... bu da tam olarak maksimum hassasiyet istediğiniz yerdir.

Geçici Çözümler : Örneğin 5 olmak üzere birçok işleme geçişi gerçekleştirerek hassasiyet kaybı iyileştirilebilir ve projeksiyonu küp harita şeklinde yapabilirsiniz. Çok basit bir diğer geçici çözüm, son görüntüyü istenen FOV'ye basitçe kırpmaktır - lensin kendisi 180 derecelik bir FOV'a sahip olsa bile, bunun yalnızca bir bölümünü oluşturmak isteyebilirsiniz. Buna "full frame" balıkgözü denir (bu aslında biraz ironiktir, çünkü görüntüyü kırparken "tam" bir şey elde ettiğiniz izlenimini verir).

(Not: Eğer bunu yararlı bulduysanız ancak yeterince net görmediyseniz, lütfen bana söyleyin, bunun hakkında daha ayrıntılı bir yazı yazmak istiyorum).


çok yararlı ve gönülden yazmak istediğiniz daha ayrıntılı makaleyi memnuniyetle karşılıyoruz!
SirYakalot

Daha iyi sonuçlar elde etmek için her iki yaklaşımı da birleştirmek mümkün mü? Öncelikle her şeyi görebilmek için VS'de projeksiyon yapın, sonra PS'de projeksiyondan çekin ve doğru UV'leri ve her şeyi almak için tekrar projekte edin. Orijinaline doğru şekilde çıkarmak için PS'ye birkaç parametre daha göndermek gerekebilir.
Ondrej Petrzilka

3

Olmalı z = sqrt(1.0 - in_Point.x * in_Point.x - in_Point.y * in_Point.y), değil mi?

GLSL uygulamam:

#ifdef GL_ES
precision mediump float;
#endif

varying vec2 v_texCoord;
uniform sampler2D u_texture;
uniform float fovTheta; // FOV's theta

// fisheye
void main (void)
{   
    vec2 uv = v_texCoord - 0.5;
    float z = sqrt(1.0 - uv.x * uv.x - uv.y * uv.y);
    float a = 1.0 / (z * tan(fovTheta * 0.5));
//  float a = (z * tan(fovTheta * 0.5)) / 1.0; // reverse lens
    gl_FragColor = texture2D(u_texture, (uv* a) + 0.5);
}

hey @Josh, nasıl fovTheta hesaplandı?
tom

1
Biçimlendirmeyi ayarlamak için sadece bu cevabı değiştirdim, direkt olarak bman adresini almak istediğinizi düşünüyorum.
Josh,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.