Metin Sahneleme
Last updated
Last updated
Grafik maceralarınızın bir aşamasında, OpenGL' de metin çizmek isteyeceksiniz. Beklediğinizin aksine, ekranda işlenebilecek basit bir karakter dizesi elde etmek, OpenGL gibi düşük seviyeli bir API ile kolaydır. 128' den fazla farklı aynı boyutta karakter oluşturmayı umursamıyorsanız, muhtemelen çok zor değildir. Her karakterin farklı bir genişliği, yüksekliği ve kenar boşluğu olduğu anda işler zorlaşıyor. Yaşadığınız yere bağlı olarak, 128' den fazla karaktere de ihtiyacınız olabilir ve matematiksel ifadeler veya notalar gibi özel sembolleri ifade etmek istiyorsanız ve metni yukarıdan aşağıya doğru oluşturmaya ne dersiniz? Tüm bu karmaşık metin konularını düşündüğünüzde, bunun muhtemelen OpenGL gibi düşük seviyeli bir API' ye ait olmadığı sizi şaşırtmayacaktır.
OpenGL içinde metin yetenekleri için destek olmadığından, ekranda metin oluşturmak için bir sistem tanımlamak bize kalmış. Metin karakterleri için grafik ilkelleri yoktur, burada yaratıcı olmalıyız. Bazı örnek teknikler şunlardır: GL_LINES
ile harf şekilleri çizmek, harflerin 3B kafeslerini oluşturmak veya karakter dokularını 3B ortamda 2B dördeylere dönüştürmek.
Çoğu geliştirici, karakter dokularını dördeylere dönüştürmeyi tercih eder. Dokulu dördeyleri tek başına işlemek çok zor olmamalı, ancak ilgili karakter(ler)i bir dokuya almak zor olabilir. Bu bölümde, çeşitli yöntemleri inceleyeceğiz ve FreeType kütüphanesini kullanarak metin oluşturmak için daha gelişmiş ancak esnek bir teknik uygulayacağız.
İlk günlerde, metin oluşturma, uygulamanız için istediğiniz bir yazı tipini seçmeyi (veya kendiniz oluşturmayı) ve bunları tek bir büyük dokuya yerleştirmek için bu yazı tipinden ilgili tüm karakterleri çıkarmayı içeriyordu. Bitmap yazı tipi olarak adlandırdığımız böyle bir doku, dokunun önceden tanımlanmış bölgelerinde kullanmak istediğimiz tüm karakter sembollerini içerir. Yazı tipinin bu karakter sembolleri Glifler olarak bilinir. Her glif, onlarla ilişkili belirli bir doku koordinatları bölgesine sahiptir. Bir karakter oluşturmak istediğinizde, bitmap yazı tipinin bu bölümünü bir 2D dörtlüye dönüştürerek karşılık gelen glifi seçersiniz.
Burada, bir bitmap yazı tipi alarak ve birkaç dördey üzerinde oluşturduğumuz dokudan (doku koordinatlarını dikkatlice seçerek) karşılık gelen glifleri örnekleyerek 'OpenGL' metnini nasıl oluşturacağımızı görebilirsiniz. Harmanlamayı etkinleştirerek ve arka planı şeffaf tutarak, ekrana işlenen sadece bir karakter dizisiyle sonuçlanacağız. Bu özel bit eşlem yazı tipi Codehead'ın bit eşlem Yazı Tipi Üreteci kullanılarak oluşturuldu.
Bu yaklaşımın çeşitli avantajları ve dezavantajları vardır. Uygulanması nispeten kolaydır ve bitmap yazı tipleri önceden pikselleştirildikelrinde oldukça verimlidirler. Ancak, özellikle de esnek değildir. Farklı bir yazı tipi kullanmak istediğinizde, tam yeni bir bitmap yazı tipini yeniden derlemeniz gerekir ve sistem tek bir çözünürlükle sınırlıdır; yakınlaştırma hızlı bir şekilde pikselli kenarları gösterecektir. Ayrıca, küçük bir karakter kümesiyle sınırlıdır, bu nedenle Genişletilmiş veya Unicode karakterler genellikle söz konusu değildir.
Bu yaklaşım, hızlı olduğu ve herhangi bir platformda çalıştığı için gün içinde oldukça popülerdi (ve halen de öyle), ancak bugün itibariyle daha esnek yaklaşımlar var. Bu yaklaşımlardan biri, Freetype kütüphanesini kullanarak TrueType yazı tiplerini yüklemektir.
FreeType, yazı tiplerini yükleyebilen, bunları bit eşlemlere dönüştürebilen ve yazı tipiyle ilgili çeşitli işlemler için destek sağlayan bir yazılım geliştirme kütüphanesidir. Mac OS X, Java, PlayStation, Linux ve Android tarafından kullanılan popüler bir kütüphanedir. Freetype'ı özellikle çekici kılan şey, TrueType yazı tiplerini yükleyebilmesidir.
TrueType yazı tipi, pikseller veya ölçeklenemeyen başka bir çözüm tarafından tanımlanmayan, ancak matematiksel denklemler (Spline kombinasyonları) tarafından tanımlanan karakter glifleri topluluğudur. Vektör görüntülerine benzer şekilde, rasterleştirilmiş yazı tipi görüntüleri, bunları elde etmek istediğiniz tercih edilen yazı tipi yüksekliğine göre prosedürel olarak oluşturulabilir. TrueType yazı tiplerini kullanarak, herhangi bir kalite kaybı olmadan çeşitli boyutlardaki karakter gliflerini kolayca görselleştirebilirsiniz.
FreeType kendi web sitesinden indirilebilir. Hedef platformunuz listeleniyorsa, kitaplığı kendiniz derleyebilir veya önceden derlenmiş kitaplıklarından birini kullanabilirsiniz. Freetype ile bağlantı kurduğunuzdan emin olun.lib ve derleyicinizin başlık dosyalarını nerede bulacağını bildiğinden emin olun.
Ardından uygun başlık dosyalarını ekleyin:
Freetype'ın nasıl geliştirildiğinden dolayı (en azından bu yazı yazıldığı sırada), başlık dosyalarını yeni bir dizine koyamazsınız; bunlar include dizinlerinizin kökünde bulunmalıdır. #İnclude <FreeType/ft2build gibi FreeType dahil.H> muhtemelen birkaç başlık çakışmasına neden olur.
FreeType bu TrueType yazı tiplerini yükler ve her glif için bir bitmap görüntüsü oluşturur ve birkaç metrik hesaplar. Dokular oluşturmak için bu bitmap görüntüleri ayıklayabilir ve yüklenen metrikleri kullanarak her karakter glifini uygun şekilde konumlandırabiliriz.
Bir yazı tipi yüklemek için, tek yapmamız gereken FreeType kütüphanesini başlatmak ve yazı tipini FreeType olarak adlandırmayı sevdiği bir yüz olarak yüklemek. Burada arial'ı yüklüyoruz.Windows/Fonts dizininden kopyalanan ttf TrueType yazı tipi dosyası:
Bu FreeType işlevlerinin her biri, bir hata oluştuğunda sıfır olmayan bir tamsayı döndürür.
Yüzü yükledikten sonra, bu yüzden ayıklamak istediğimiz piksel yazı tipi boyutunu tanımlamalıyız:
İşlev, yazı tipinin genişlik ve yükseklik parametrelerini ayarlar. Genişliği 0 olarak ayarlamak, yüzün verilen yüksekliğe göre genişliği dinamik olarak hesaplamasını sağlar.
Bir FreeType yüzü bir glif koleksiyonuna ev sahipliği yapar. FT_Load_Char çağırarak bu gliflerden birini aktif glif olarak ayarlayabiliriz. Burada 'X'karakter glifini yüklemeyi seçiyoruz:
Ft_load_render'i yükleme bayraklarından biri olarak ayarlayarak, freetype'a face - > glyph - >bitmap aracılığıyla erişebileceğimiz 8 bitlik bir gri tonlamalı bitmap görüntüsü oluşturmasını söyleriz.
Bununla birlikte, FreeType ile yüklediğimiz gliflerin her biri aynı boyuta sahip değildir (bitmap yazı tipleriyle olduğu gibi). FreeType tarafından oluşturulan bitmap görüntüsü, bir karakterin görünür kısmını içerecek kadar büyüktür. Örneğin, nokta karakterinin bitmap görüntüsü '.'x' karakterinin bitmap görüntüsünden çok daha küçüktür. Bu nedenle, FreeType, her karakterin ne kadar büyük olması gerektiğini ve bunları nasıl düzgün bir şekilde konumlandıracağınızı belirten birkaç metriği de yükler. Sonraki, her karakter glifi için hesapladığı tüm metrikleri gösteren Freetype'tan bir görüntüdür:
Gliflerin her biri yatay bir taban çizgisinde (yatay okla gösterildiği gibi) bulunur; burada bazı Glifler tam olarak bu taban çizgisinin üstünde ('X' gibi) veya baz çizgisinin biraz altında ('g' veya 'p'gibi) bulunur. Bu metrikler, her bir glifi taban çizgisinde düzgün bir şekilde konumlandırmak için kesin ofsetleri, her bir glifin ne kadar büyük olması gerektiğini ve bir sonraki glifi oluşturmak için kaç piksel ilerlememiz gerektiğini tanımlar. Sırada ihtiyacımız olan mülklerin küçük bir listesi var:
width: the width (in pixels) of the bitmap accessed via face->glyph->bitmap.width
.
height: the height (in pixels) of the bitmap accessed via face->glyph->bitmap.rows
.
bearingX: the horizontal bearing e.g. the horizontal position (in pixels) of the bitmap relative to the origin accessed via face->glyph->bitmap_left
.
bearingY: the vertical bearing e.g. the vertical position (in pixels) of the bitmap relative to the baseline accessed via face->glyph->bitmap_top
.
advance: the horizontal advance e.g. the horizontal distance (in 1/64th pixels) from the origin to the origin of the next glyph. Accessed via face->glyph->advance.x
.
Bir karakter glifi yükleyebilir, metriklerini alabilir ve ekrana bir karakter oluşturmak istediğimizde bir doku oluşturabiliriz, ancak bunu her karede yapmak verimsiz olacaktır. Oluşturulan verileri uygulamada bir yerde saklamayı ve bir karakter oluşturmak istediğimizde sorgulamayı tercih ederiz. Bir haritada saklayacağımız uygun bir yapı tanımlayacağız:
Bu bölüm için, ASCII karakter kümesinin ilk 128 karakteriyle sınırlayarak işleri basit tutacağız. Her karakter için bir doku oluşturuyoruz ve ilgili verilerini karakter haritasına eklediğimiz bir karakter yapısına saklıyoruz. Bu şekilde, her karakteri işlemek için gereken tüm veriler daha sonra kullanılmak üzere saklanır.
For döngüsünde, ASCII kümesinin tüm 128 karakterini listeliyoruz ve karşılık gelen karakter gliflerini alıyoruz. Her karakter için: bir doku oluşturuyoruz, seçeneklerini belirliyoruz ve metriklerini saklıyoruz. Burada dikkat edilmesi gereken ilginç olan şey, gl_red'i doku iç biçimi ve biçim argümanları olarak kullanmamızdır. Gliften oluşturulan bit eşlem, her rengin tek bir baytla temsil edildiği gri tonlamalı 8 bitlik bir görüntüdür. Bu nedenle, bitmap arabelleğinin her baytını dokunun tek renk değeri olarak saklamak istiyoruz. Bunu, her baytın doku renginin kırmızı bileşenine (renk vektörünün ilk baytı) karşılık geldiği bir doku oluşturarak gerçekleştiririz. Bir dokunun renklerini temsil etmek için tek bir bayt kullanırsak, OpenGL kısıtlamasına dikkat etmemiz gerekir:
OpenGL, tüm dokuların 4 baytlık bir hizalamaya sahip olmasını gerektirir, örneğin boyutları her zaman 4 baytlık bir kattır. Genellikle bu bir sorun olmayacaktır, çünkü çoğu doku 4'ün katları olan bir genişliğe sahiptir ve / veya piksel başına 4 bayt kullanır, ancak şimdi piksel başına sadece bir bayt kullandığımızdan, doku herhangi bir olası genişliğe sahip olabilir. Açma hizalamasını 1 olarak ayarlayarak, hizalama sorunları olmadığından emin oluruz (segmentasyon hatalarına neden olabilir).
Glifleri işlemeyi bitirdikten sonra FreeType kaynaklarını temizlediğinizden emin olun:
Glifleri oluşturmak için aşağıdaki köşe gölgelendiricisini kullanacağız:
Hem konum hem de doku koordinat verilerini tek bir vec4'te birleştiriyoruz. Köşe gölgelendirici koordinatları bir projeksiyon matrisi ile çarpar ve doku koordinatlarını parça gölgelendiricisine iletir:
Parça gölgelendirici iki üniforma alır: biri glifin tek renkli bitmap görüntüsüdür ve diğeri metnin son rengini ayarlamak için bir renk üniformasıdır. Önce bitmap dokusunun renk değerini örnekliyoruz. Doku verileri sadece kırmızı bileşeninde saklandığından, doku r bileşenini örneklenmiş alfa değeri olarak örnekleriz. Çıktı renginin alfa değerini değiştirerek, ortaya çıkan piksel tüm glifin arka plan renkleri için saydam ve gerçek karakter pikselleri için saydam olmayacaktır. Ayrıca, metin rengini değiştirmek için RGB renklerini textColor uniform ile çarpıyoruz.
Bunun işe yaraması için harmanlamayı etkinleştirmemiz gerekiyor:
Projeksiyon matrisi için bir ortografik projeksiyon matrisi kullanacağız. Metni oluşturmak için (genellikle) perspektife ihtiyacımız yoktur ve ortografik bir projeksiyon matrisi kullanmak, aşağıdaki gibi yapılandırırsak, ekran koordinatlarındaki tüm köşe koordinatlarını belirtmemize izin verir:
Projeksiyon matrisinin alt parametresini 0.0 f'ye ve üst parametresini pencerenin yüksekliğine eşit olarak ayarladık. Sonuç, ekranın alt kısmından (0.0 f) ekranın üst kısmına (600.0 f) kadar değişen y değerleriyle koordinatları belirtmemizdir. Bu, noktanın (0.0, 0.0) şimdi sol alt köşeye karşılık geldiği anlamına gelir.
Son olarak, dörtlüleri oluşturmak için bir VBO ve VAO oluşturmaktır. Şimdilik, vbo'yu başlatırken yeterli bellek ayırıyoruz, böylece karakterleri oluştururken VBO'NUN belleğini daha sonra güncelleyebiliriz:
2D dörtlü, her biri 4 şamandıranın 6 köşesini gerektirir, bu yüzden 6 * 4 şamandıra belleği ayırırız. VBO'NUN belleğinin içeriğini oldukça sık güncelleyeceğimiz için belleği GL_DYNAMİC_DRAW ile ayıracağız.
Bir karakteri işlemek için, karakter haritasının karşılık gelen karakter yapısını çıkarırız ve karakterin metriklerini kullanarak dörtlü boyutlarını hesaplarız. Dörtlü hesaplanan boyutlarla, glBufferSubData kullanarak VBO tarafından yönetilen belleğin içeriğini güncellemek için kullandığımız 6 köşe kümesini dinamik olarak oluştururuz.
Bir karakter dizisi oluşturan RenderText adlı bir işlev oluşturuyoruz:
Fonksiyonun içeriğinin çoğu nispeten kendi kendini açıklayıcı olmalıdır: önce quad'in başlangıç konumunu (xpos ve ypos olarak) ve quad'in boyutunu (w ve h olarak) hesaplarız ve 2D quad'i oluşturmak için 6 köşe kümesi oluştururuz; her metriği ölçeğe göre ölçeklendirdiğimizi unutmayın. Daha sonra VBO'NUN içeriğini güncelliyoruz ve dörtlüyü oluşturuyoruz.
Bununla birlikte, aşağıdaki kod satırı biraz daha dikkat gerektirir:
Bazı karakterler ('p' veya 'g' gibi) taban çizgisinin biraz altında işlenir, bu nedenle dörtlü de Rendertext'in y değerinin biraz altına yerleştirilmelidir. Ypo'ları taban çizgisinin altında dengelemek için ihtiyacımız olan tam miktar, glif metriklerinden anlaşılabilir:
Bu mesafeyi hesaplamak için, örneğin ofset, bir glifin taban çizgisinin altına uzandığı mesafeyi bulmamız gerekir; bu mesafe kırmızı okla gösterilir. Glif metriklerinden de görebileceğiniz gibi, bu vektörün uzunluğunu, glifin (bitmap) yüksekliğinden ayı çıkararak hesaplayabiliriz. Bu değer daha sonra taban çizgisinde ('X' gibi) kalan karakterler için 0.0 ve taban çizgisinin biraz altında ('g' veya 'j'gibi) bulunan karakterler için pozitiftir.
Her şeyi doğru yaptıysanız, şimdi aşağıdaki ifadelerle metin dizelerini başarılı bir şekilde görselleştirebilmelisiniz:
Bu daha sonra aşağıdaki resme benzer görünmelidir:
Bu örneğin kodunu burada bulabilirsiniz.
Dörtgen köşelerini nasıl hesapladığımıza dair bir fikir vermek ve gerçek işlenmiş dörtgenlerin neye benzediğini görmek için harmanlamayı devre dışı bırakabiliriz:
Burada, 'P' veya ' ( ' ) gibi gliflere karşılık gelen dörtlülerin aşağı doğru kayarken, (hayali) taban çizgisine dayanan dörtlülerin çoğunu açıkça görebilirsiniz.
Bu bölümde Freetype kitaplığını kullanarak TrueType yazı tipleri ile bir metin işleme tekniği gösterilmiştir. Bu yaklaşım esnek, ölçeklenebilir ve birçok karakter kodlaması ile çalışır. Bununla birlikte, her glif için dokular oluşturduğumuzda ve oluşturduğumuzda, bu yaklaşım uygulamanız için aşırı olacaktır. Performans açısından, bitmap yazı tipleri tercih edilir, çünkü tüm gliflerimiz için sadece bir dokuya ihtiyacımız vardır. En iyi yaklaşım, freetype ile yüklenen tüm karakter gliflerini içeren bir bitmap yazı tipi dokusunu dinamik olarak oluşturarak iki yaklaşımı birleştirmek olacaktır. Bu, oluşturucuyu önemli miktarda doku anahtarından kurtarır ve her glifin ne kadar sıkı bir şekilde paketlendiğine bağlı olarak, bazı performanslardan tasarruf edebilir.
FreeType yazı tipi bit eşlemleri ile ilgili bir başka sorun, glif dokularının sabit bir yazı tipi boyutu ile depolanmasıdır, bu nedenle önemli miktarda Ölçekleme pürüzlü kenarlara neden olabilir. Ayrıca, gliflere uygulanan rotasyonlar bulanık görünmelerine neden olur. Bu, gerçek rasterleştirilmiş piksel renklerini depolamak yerine, piksel başına en yakın glif anahatlarına olan mesafeyi depolayarak hafifletilebilir. Bu tekniğe imzalı mesafe alanı yazı tipleri denir ve Valve birkaç yıl önce 3D render uygulamaları için şaşırtıcı derecede iyi çalışan bu tekniğin uygulanması hakkında bir makale yayınladı.
Tasarımcılar için 70'den fazla en iyi ücretsiz yazı tipi: kişisel veya ticari kullanım için projenizde kullanılacak büyük bir yazı tipi grubunun özetlenmiş listesi.
Orijinal Kaynak: Text Rendering
Çeviri: Nezihe Sözen