Bu sayfadaki yazılar
Sayfanın metni
Sayfa 50
2) PBuffer (Pixel Buffer) yöntemi kullanılabilir. Bu yöntemde pencerenin kullandığının dışında farklı bir render context yaratılır ve sahne buraya çizdirilir. Sonra wglARBRenderTexture extension'ı kullanılarak bu render context'in içeriği texture olarak kullanılabilir. Pbuffer'ları kurmanın karmaşıklığı yüzünden ve de render context switching hakkındaki trickleri bilmiyorsanız ( her frame'de render context switching var : pbuffer --> window --> pbuffer... GeForce 3 , Radeon 8500 altı kartlarda sürünürsünüz) PBuffer ile render to texture'dan uzak durun. Pbuffer'lar başlı başına bir tutorial konusu olabilir. Bu yüzden burda kesiyorum.
3) EXT_framebuffer_object extension'ını kullanabilirsiniz. Gerek kullanım kolaylığı ve hız açısından en uygun yöntem bu bence. Fakat detaya girmiyorum. Bu da kendi içinde bir tutorial olabilir.
3) Pixel Shader Yöntemi :
Huh , şimdiden bayağı uzun bir tutorial oldu. Bu kısmı okuyacak arkadaşların direct3d ile tecrübesi olduğunu varsayıyorum. Yani en azından burada api detaylarına girmicem. Mesela texture nasıl yüklenir ya da .fx dosyaları nasıl hazırlanır gibi.. çünkü bunlar başka bir tutorial konusu olabilir. Shader'lar ile fazla haşır neşir olmamış scener'lar için bu shader konusuna çok kısa bir giriş yapalım. Demolar (ve bilgisayar oyunları) gerçek zamanlı bilgisayar grafikleri ile ilgili olduğundan , bizim shader diyince anlamamız gereken şey şu olmalı : 3D işhattını (pipeline) modifiye eden programlar. Bu programlar GPU üzerinde (vertex ve pixel processor'ler) çalışır. Günümüz GPU teknolojisinde pipeline'ın belli kısımları programlanabilir haldedir ve pipeline'ın kabaca iki ana hattan oluştuğunu söyleyebiliriz : Vertex hattı ve pixel hattı.
Yukarıda yazılanlara göre bazı tanımlar yapabiliriz:
Vertex Shader : Pipeline'a giren her vertex üzerinde çalışan programlardır. Vertex shader ile GPU'nun vertex processor denen donanım kısmı programlanır. Vertex shader'lar girdi olarak (dolayısıyla vertex processor) herhangi bir geometrik dönüşüme uğramamış vertex'ler (object space'de tanımlı) , bu vertex'lerle ilişkilendirilmiş renk , doku koordinatı , normal vektörleri vs.. gibi verileri alırlar. Çıktı olarak ise clip space'de vertex koordinatları (rasterizer'a gitmeden önce, vertex shader'dan sonra arada başka pipeline seviyeleri var , mesela backface culling ya da homojen divide gibi.. bunlar programlanamaz) ve rasterizer'ı besleyecek her türlü veriyi (modifiye ettikleri doku koordinatları , normaller vs..) verirler. Vertex shader'lar yeni vertex'ler üretemezler , var olan vertex'leri modifiye ederler.
Pixel Shader : Rasterizasyon sonucu oluşan her pixel üzerinde çalışan programlardır. Pixel shader ile pixel processor programlanır. Input olarak rasterizer'dan gelen verileri alırlar ve çıktı olarak da color ve depth buffer'a hesapladıkları piksel değerlerini yazabilirler. Piksel shader'dan sonra son pipeline seviyesi olan per fragment operations (yeni gelen piksel değerleri ile frame buffer'da yazılı olan piksel değerleri ile ilgili her türlü işlemin yapıldığı kısım.. depth test , alpha test , alpha blending fog vs..) gelir.
Esas konumuza geri dönersek. Fade In/out işlemi bir post processing işlemi. Yine sahnemizi bir texture'a çizdireceğiz ve bu texture'u alıp bir quad üzerine map edeceğiz. Pixel shader piksel değerlerine doğrudan erişemediği için texture lookup yapacağız. Bu yüzden her frame texture'a çizdiriliyor(Render to Texture). Tutorial yeterince uzadığından nasıl screen aligned quad çizilir ya da render to texture nasıl yapılır o konulara girmicem , sadece shader'lardan bahsedeceğim.
Fade In/Out için pixel shader'da lineer interpolasyon yapacağız. İlk anlattığım yönteme (Frame Buffer ile blending) dikkatle bakarsanız lineer interpolasyon formunda olduğunu görürsünüz. Bildiğimiz iki değer arasındaki değerleri bulmak için interpolasyon kullanırız. Şimdi lineer interpolasyonu bir anlayalım. Mesela x ekseninde, x1 koordinatından x2 koordinatına gideceğiz :
Klavyeyle gezinmek için ← ve → tuşları.
Sayfa 51
LERP = x1 + (x2 - x1) * t dediğimiz zaman x1 ve x2'nin arasındaki her değeri t'yi kullanarak bulabiliriz. t'nin izin verilen değer aralığı [0,1]'dir. t = 0 için x1 , t = 1 için x2'deyiz. Interpolasyon bilgisayar grafiklerinde, özellikle animasyonda çok kullanılan matematiksel bir araçtır. Lineer'in dışında çeşitli spline eğrileri kullanan interpolatörler de vardır.
Bizde pixel shader'da texture ile fade rengi arasında lerp işlemi uygulayacağız.
Not: Lineer interpolasyon lerp olarak da bilinir.
Daha önceden yazdığım .fx dosyasına bakalım ve en azından shader'lara bir parça kendimizi alıştıralım.
İlk olarak uniform değişkenler karşımıza çıkacak. Uniform değişkenler shader'ın etkilediği her piksel ya da vertex için aynı kalan değişkenlerdir. Mesela burada mWorldViewProjection, transformasyon matrisidir. (object space ----> clip space'e) Shader'ın etkilediği geometrideki her vertex için aynıdır. t ise lineer interpolasyonda kullanılan parametredir ve her piksel için aynıdır. width ve height vertex shader'da doku koordinatları için offset değerleri hesaplanmasında kullanılır. Çünkü Direct3D sampling kurallarına göre ekrandaki pikseller ile texture'daki texellerin 1:1 eşleşmesi için doku koordinatları (u,v) yerine: u+ = offset_u, ve v+ = offset_v olmalıdır.
offset_u = (1.0/2.0) * (1.0/width) // width : screen width offset_v = (1.0/2.0) * (1.0/height) // height : screen height
Esasında HLSL'de uniform keyword'ü uniform değişkenleri belirtmekte kullanılır. Fakat global değişkenlerde (shader kodları gövdelerinde olmayan değişkenler) buna gerek yok, global değişkenler uniformdur. Genelde uniform değişkenler uygulamadan shader'a gönderilen değişkenlerdir. Her vertex ya da her piksel için ayrıca hesaplanmazlar.
float4x4 mWorldViewProj; float t; float width, height;
.fx dosyalarının bir güzelliği de texture address ve filtreleme modlarının bu dosyalar içerisinde tanımlanabilmesidir. t0 pixel shader'ın kullanacağı texture'ı belirtir. s0 ise pixel shader'ın bizim texture'ımıza erişirken (look up için) kullandığı sampler'dır.
texture t0; sampler2D s0 = sampler_state { Texture = (t0); AddressU = CLAMP; AddressV = CLAMP; MagFilter = LINEAR; MinFilter = LINEAR; MipFilter = NONE; };
Bu kısımda ise piksel ve vertex shader'ların kullandığı veri yapılarını görüyoruz. C struct'larından pek bir farkı yok. Göze çarpan tek fark değişken isimleri yanındaki kolonlar ve bazı keyword'ler. Bu keyword'lere semantics denir ve görevleri ise pipeline'dan gelen verilerle ilgili GPU register'larını birbirlerine eşlemektir. VS_IN struct'ına bakarsak pos değişkeni homojen koordinatlarda (x,y,z,w) bir noktayı temsil eder ve vertex processor'un input register'larına (position register'ı, v0) alınacağı POSITION0 semantic'i ile belirtilir. VS_OUT'a da baktığınızda pos değişkeni, vertex shader'da transform edilmiş bir noktayı temsil eder ve vertex processor'un output register'ına (output position register'ı, oPos) alınacağı yine POSITION0 semantic'i ile belirtilir. HLSL'de input ve output register'larının aynı semantic'lerle belirtildiğini görebilirsiniz.