Yüzey Ayırma

Bir 3B küpü zihinsel olarak görselleştirmeyi deneyin ve herhangi bir yönden görebileceğiniz maksimum yüz sayısını sayın. Hayal gücünüz çok yaratıcı değilse, muhtemelen maksimum 3 sayısı ile sonuçlanır. Bir küpü herhangi bir konumdan ve / veya yönden görüntüleyebilirsiniz, ancak asla 3'ten fazla yüzü göremezsiniz. Öyleyse neden göremediğimiz diğer 3 yüzü çizmek için boşa çaba harcayalım? Bunları bir şekilde atabilirsek, bu küpün toplam parça gölgelendirici çalışmalarının % 50'sinden fazlasını kurtarırdık!

% 50 yerine; % 50'den fazlası diyoruz, çünkü belirli açılardan sadece 2 hatta 1 yüz görülebilir. Bu durumda % 50'den fazla tasarruf etmiş oluruz.

Bu gerçekten harika bir fikir, ancak çözmemiz gereken bir sorun var: Bir nesnenin bir yüzünün izleyicinin bakış açısından görünmediğini nasıl anlarız? Herhangi bir kapalı şekli hayal edersek, yüzlerinin her birinin iki kenarı vardır. Her iki taraf da ya kullanıcıya bakacak ya da arkasını kullanıcıya gösterecektir. Ya sadece izleyicinin karşısına çıkan yüzleri işleyebilseydik?

Bu tam olarak yüzey ayırmanın (ing. face culling) yaptığı şeydir. OpenGL, ön yüzleri (ing. front facing) kontrol eder ve arka yüzleri (ing. back facing) atıp bunları işleyerek bize çok sayıda parça gölgelendirici çağrısı kazandırır. OpenGL' e kullandığımız yüzlerden hangisinin gerçekte ön yüzler ve hangi yüzlerin arka yüzler olduğunu söylememiz gerekiyor. OpenGL, köşe verilerinin dönme sırasını (ing. winding order) analiz ederek bunun için akıllıca bir numara kullanır.

Dönme sırası

Bir dizi üçgen köşesini tanımladığımızda, onları saat yönünde veya saat yönünün tersine belirli bir dönme sırasına göre tanımlıyoruz. Her üçgen 3 köşeden oluşur ve bu 3 köşeyi, üçgenin merkezinden bakıldığında sarma sırasına göre belirtiriz.

Görselde görebileceğiniz gibi, ilk önce köşe 1'i tanımlıyoruz ve buradan bir sonraki tepe noktasının 2 mi yoksa 3 mü olduğunu seçebiliyoruz. Bu seçim, bu üçgenin dönme sırasını tanımlar. Aşağıdaki kod bunu göstermektedir:

float vertices[] = {
    // clockwise
    vertices[0], // vertex 1
    vertices[1], // vertex 2
    vertices[2], // vertex 3
    // counter-clockwise
    vertices[0], // vertex 1
    vertices[2], // vertex 3
    vertices[1]  // vertex 2  
};

Üçgen ilkel oluşturan her 3 köşe kümesi bu nedenle bir sarma sırası içerir. OpenGL, üçgenin öne bakan veya arkaya bakan bir üçgen olup olmadığını belirlemek için ilkellerinizi oluştururken bu bilgileri kullanır. Varsayılan olarak, saat yönünün tersine köşelerle tanımlanan üçgenler, öne bakan üçgenler olarak işlenir.

Köşe sıranızı tanımlarken, karşılık gelen üçgeni sanki size bakıyormuş gibi görselleştirirsiniz, böylece belirttiğiniz her üçgen, sanki doğrudan o üçgene bakıyormuşsunuz gibi saat yönünün tersine olmalıdır. Tüm köşelerinizi bu şekilde belirtmenin en güzel yanı, gerçek sarma sırasının rasterleştirme aşamasında hesaplanmasıdır, yani köşe gölgelendiricisi zaten çalıştığında. Köşeler daha sonra izleyicinin bakış açısından görülüyor.

İzleyicinin daha sonra baktığı tüm üçgen köşeler, aslında belirttiğimiz gibi doğru sarma sırasındadır, ancak küpün diğer tarafındaki üçgenlerin köşeleri artık sarma sıraları tersine dönecek şekilde oluşturulur. Sonuç olarak, karşı karşıya olduğumuz üçgenler öne bakan üçgenler olarak görülüyor ve arkadaki üçgenler arkaya bakan üçgenler olarak görülüyor. Aşağıdaki görüntü bu etkiyi göstermektedir:

Köşe verilerinde her iki üçgeni de saat yönünün tersine sırayla tanımladık (ön ve arka üçgen 1, 2, 3 olarak). Bununla birlikte, izleyicinin şu anki bakış açısından 1, 2 ve 3 sıralarında çizersek, izleyicinin yönünden arka üçgen saat yönünde oluşturulur. Arka üçgeni saat yönünün tersi sırada belirtmiş olsak da, artık saat yönünde işleniyor. Görünmeyen yüzleri ayırmak (atmak) istediğimiz şey tam olarak budur!

glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);  

Gördüğünüz gibi, yüzey ayırma, en az çabayla OpenGL uygulamalarınızın performansını artırmak için ve özellikle tüm 3B uygulamalar, modelleri tutarlı dönme sıraları ile dışa aktarırken (varsayılan olarak CCW) harika bir araçtır. Yüz ayıklamadan gerçekten fayda sağlayacak nesneleri ve hangi nesnelerin hiç itlaf edilmemesi gerektiğini takip etmelisiniz.

Face culling

Bölümün başında, OpenGL'nin arka tarafa bakan üçgenler olarak işlenirlerse üçgen temelleri atabileceğini söylemiştik. Artık köşelerin sarma sırasını nasıl ayarlayacağımızı bildiğimize göre, OpenGL'nin varsayılan olarak devre dışı bırakılan yüz ayırma seçeneğini kullanmaya başlayabiliriz.

Önceki bölümlerde kullandığımız küp tepe verileri, saat yönünün tersine sarma sırası göz önünde bulundurularak tanımlanmamıştı, bu yüzden köşe verilerini buradan kopyalayabileceğiniz saat yönünün tersine sarma sırasını yansıtacak şekilde güncelledim. Bu köşelerin gerçekten de her üçgen için saat yönünün tersine bir sırada tanımlandığını denemek ve görselleştirmek iyi bir uygulamadır.

Yüz ayırmayı etkinleştirmek için yalnızca OpenGL'nin GL_CULL_FACE seçeneğini etkinleştirmemiz gerekir:

glEnable(GL_CULL_FACE);  

Bu noktadan sonra, ön yüz olmayan tüm yüzler atılır (tüm iç yüzlerin gerçekten atıldığını görmek için küpün içinde uçmayı deneyin). Şu anda, OpenGL önce arka yüzleri oluşturmaya karar verirse parçaları işlerken% 50'den fazla performans tasarrufu sağlıyoruz (aksi takdirde derinlik testi bunları zaten iptal ederdi). Bunun yalnızca küp gibi kapalı şekillerde işe yaradığını unutmayın. Önceki bölümdeki çim yapraklarını çektiğimizde, ön ve arka yüzlerinin görünür olması gerektiğinden, yüz ayırmayı tekrar devre dışı bırakmalıyız.

OpenGL, ayırmak istediğimiz yüz tipini de değiştirmemize izin verir. Ya arka yüzleri değil de ön yüzleri ayırmak istiyorsak? Bu davranışı glCullFace ile tanımlayabiliriz:

glCullFace(GL_FRONT);  

TheglCullFace function has three possible options:

  • GL_BACK: Culls only the back faces.

  • GL_FRONT: Culls only the front faces.

  • GL_FRONT_AND_BACK: Culls both the front and back faces.

The initial value ofglCullFace is GL_BACK. We can also tell OpenGL we'd rather prefer clockwise faces as the front-faces instead of counter-clockwise faces viaglFrontFace:


glFrontFace(GL_CCW);  

The default value is GL_CCW that stands for counter-clockwise ordering with the other option being GL_CW which (obviously) stands for clockwise ordering.

As a simple test we could reverse the winding order by telling OpenGL that the front-faces are now determined by a clockwise ordering instead of a counter-clockwise ordering:


glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);  

The result is that only the back faces are rendered:

Note that you can create the same effect by culling front faces with the default counter-clockwise winding order:

glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);  

As you can see, face culling is a great tool for increasing performance of your OpenGL applications with minimal effort; especially as all 3D applications export models with consistent winding orders (CCW by default). You do have to keep track of the objects that will actually benefit from face culling and which objects shouldn't be culled at all.

Alıştırmalar

  • Her üçgeni saat yönünde belirleyerek köşe verilerini yeniden tanımlayabilir ve ardından sahneyi saat yönünde üçgenler ön yüzler olarak ayarlayarak oluşturabilir misiniz: çözüm

Orijinal Kaynak: Face culling

Last updated