NEDEN MATRİSLER?
Uzun bir aradan sonra tutoriala kaldığımız yerden devam ediyoruz. Bu noktadan önce genel olarak uzay, vektör, pozisyon, yön kavramlarını inceledik. Sonra bir uzaydaki bir vektörün diğer bir uzayda nasıl gösterildiğini hesaplamayı öğrendik. Ardından 3d uzayda işimize yarayacak bir kaç vektör işlemi gördük.
Aslında tutorialın başlığı olan 'Temel Geometri' denen hadisenin tamamını gördük. Yukardaki formülleri ezberleyerek yahut çıkarım yollarını hatırlayarak bir 3D motorunun uzay ve vektörlerle olan kısmını yazabilirsiniz. Ama bu bölümden sonra göreceğimiz şeyleri kullanmadan işin içinden biraz zor çıkılır.
İşin temelinde uzaylar arası vektör çevrim formülleri yer alıyor. Formüllere bir göz atarak neden bu işin sizi zorlayacağını anlayalım.
A uzayında v yön vektörü [x,y,z] şeklinde tanımlı olsun. A uzayının kendisi ise kendi içinde şu vektörlerle tanımlı:
[0,0,0] pozisyon vektörü: merkez
[1,0,0] yön vektörü: x vektörü (i)
[0,1,0] yön vektörü: y vektörü (j)
[0,0,1] yön vektörü: z vektörü (k)
Bütün uzaylar kendi içlerinde böyle tanımlıydı zaten. Mesela bu A uzayı B uzayında şu durumda bulunsun (A uzayında tanımlı vektörleri [a,b,c], B uzayında tanımlı vektörleri [d,e,f]' şeklinde göstereceğiz) :
[px,py,pz]' pozisyon vektörü: merkez
[xx,xy,xz]' yön vektörü: x vektörü (i'😉
[yx,yy,yz]' yön vektörü: y vektörü (j'😉
[zx,zy,zz]' yön vektörü: z vektörü (k'😉
Böyle bir durumda v vektörünün B uzayındaki tanımı olan v' vektörü şu formüllerle çıkıyordu:
v' = x[xx,xy,xz] + y[yx,yy,yz] + z[zx,zy,zz]
Eğer v' bir pozisyon vektörü olsaydı bu toplama [px,py,pz] vektörünü de ekleyecektik, bir başka deyişle A'nın merkezi kadar ilerleyecektik. Eğer v' vektörünün tamamını değil de bir elemanını (x, y ya da z) istersek, formül her bir eleman için şöyle olacaktı:
v' . i = x * xx + y * yx + z * zx (+ px)
v' . j = x * xy + y * yy + z * zy (+ py)
v' . k = x * xz + y * yz + z * zz (+ pz)
Not: v' . i, v' vektörünün ilk elemanı anlamına gelir. ([x, y, z] . [1, 0, 0] = x) Diğerleri de benzer ifadelerdir.
Bunlar A uzayının B uzayındaki tanımını biliyorsak kullanabileceğimiz formüllerdi. Eğer tersi bir durum söz konusu ise, yani sadece B uzayını A uzayında biliyorsak ve A uzayında tanımlı bir vektörü B uzayına taşıyacaksak, ters çevrim formülleri gerekli idi. 1D ve 2D'de bu formüllerin ne kadar karışık olduğunu gördük; 3D de ise çıkarmaya bile çalışmadık. Halbuki bu ters çevirim formülleri bize kamera yapımında gerekli olacak. Bu yüzden ters çevrim formüllerini de çevrim formülleri gibi bir sistematiğe bağlamamız gerekiyor.
Bu önümüzdeki zorluklardan sadece biri. Bir başka zorluk ise art arda yapılan çevirimler. Mesela A uzayının B uzayındaki tanımını ve B uzayının C uzayındaki tanımını biliyor olalım. A uzayında bildiğimiz bir vektörün C uzayındaki tanımını istiyorsak, önce o vektörü B uzayına geçirip yeni bir vektör elde edeceğiz. Sonra da bu yeni vektörü C uzayına geçirip aradığımız vektörü elde edeceğiz. Bu kadar basit. Ama asıl zorluk art arda 10, 100 belki 1000 tane uzay olduğunda kendini gösterecek.
Bunun çözümünü olaya pratik açıdan bakarak bulabiliriz. Ekrana sürekli çevrimlerden geçen bir 3D cismi çizdiğimizde, o cismin tüm vektörleri aynı çevirime maruz kalacak. Mesela, hepsini A uzayından C uzayına geçirmemiz gerekecek. Eğer arada B uzayına çevirmek yerine, A uzayının C uzayındaki tanımını bulursak bütün vektörleri tek bir çevrime sokma şansımız olur. Yani:
A -> B -> C (yanlış)
A -> C (doğru)
Bu sayede işlemlerimiz iki kat hızlanacak. Peki A uzayının C uzayındaki tanımını nasıl bulacağız? Çok basit. A uzayının B uzayındaki tanımındaki tüm vektörlere (3 yön, 1 pozisyon) B -> C çevrimi uygularsak elde ettiğimiz vektörler A -> C çevrimi için kullanılabilir hale gelecek.
Karıştıysa baştan alayım: A uzayının B uzayında tanımı, B uzayında 4 tane vektör (A'nın B uzayındaki basisi) demek. Bu vektörleri C uzayına geçirirsem A uzayının C uzayında tanımını elde ederim.
Peki bunun neresi zorluk diye sorarsanız, o kadar çevirim formülünü tek tek işlemek, formül içine formül sokmak, bu formüllerde vektörler yön vektörü müydü, pozisyon vektörü müydü diye kontrol etmek bir yerden sonra işleri arap saçına çevirecektir.
Peki bu işlemleri kolaylaştırmanın bir yolu var mı?
Tabi ki.
Bizim uğraştığımız denklemlerin, yani çevrim formüllerinin güzel bir özelliği var, o da lineer olmaları. Yani formüllerin hiç birinde vektörlerin ya da elemanların karesini küpünü filan kullanmıyoruz. Yaptığımız işlemler tamamen lineer işlemler.
Tabi bazı bilgili okurlar burada pozisyon vektörlerinin çevrimlerinin sonundaki toplama işleminden dem vurup lineerliğin bozulduğunu söyleyebilir. Daha sonra o kısmı da kılıfına uyduracağımızı belirtelim.
Ve de atalarımız bizden önce lineer denklemler üzerindeki işlemleri kolaylaştıran, onların daha kolay görünmesini, düzenlenmesini, çözülmesini vs. sağlayan bir takım sistemler geliştirmiş ve bunların hepsini 'Lineer Cebir' adı altında toplamışlar. Lineer Cebir'de; denklemleri, denklem kümelerini, katsayıları, bilinenleri, bilinmeyenleri, kısaca her şeyi 'matris' adı verilen elemanlar yapmışlar ve denklem çözümlerini, lineer işlemleri, formülleri, içiçe formülleri her bi şeyi matrisler üzerinden özel işlemlerle tanımlamışlar.
Neden matrisler sorusu burada iki önemli yanıt buluyor. Birincisi, geometride sürekli uğraşacağımız lineer denklemlerin, yılların birikimi sonucu oluşan Lineer Cebir sayesinde artık matris notasyonu ile gösterilmeleri; ikincisi ise, birazdan hepimizin göreceği üzere matrislerin programlamaya oldukça elverişli olmaları, hatta bildiğimiz arraylerden farksız olmaları.
Çevirimlerde matrisleri kullanmak bize şu katkıları da sağlayacak:
1) Sadece vektörlerin uzaylar arası çevrimi değil, uzayların da uzaylar arası çevrimi kolaylaşacak.
2) Yön vektörü, pozisyon vektörü ayrımından nihayet kurtulacağız ve çevrimler için tek bir formülümüz olacak. Hatta konuya yeniyseniz biraz şaşırtıcı olacak ama bu formül yukarıdaki kadar karışık olmayacak, sadece tek bir çarpma işleminden oluşacak.
3) Ve nihayetinde ters çevrim formülleri çok basit bir hale gelecek. (Basit olacak ama kolay olmayacak.)
O zaman bu matrislere ve nasıl kullanıldıklarına bir giriş yapalım.
Matrisler
Matris denen şey iki boyutlu sınırlı bir seridir. Yani içindeki elemanların matristeki pozisyonu iki sayı ile gösterilir. Bu elemanlar her şey olabilir ama biz kolaylık olsun diye onları sayı kabul edeceğiz. Mesela aşağıdaki şey bir matristir.
[4 5 7]
A = [0 2 1]
Bu matrise A ismini verdik. A matrisinin 6 elemanı vardır. Bu elemanlar üç sütun ve iki satır içerisinde kendilerine yer bulmuşlardır. Bir başka değişle iki satır ve üç sütuna sahip bir tablodur. Örneğin '5' elemanı ikinci sütun, birinci satırdadır.
Bir matristeki sütun ve satır sayıları herhangi bir pozitif tamsayı olabilir. Sıfır satır ya da sütunlu matrisler olabilir mi bilmiyorum ama pratik açıdan bir önemi yok zaten.
Bi de bu matrisleri gösterirken genelde iki tane köşeli parantez kullanılır ama burada iki köşeli parantezi üstüste koydum, kafalar karışmasın.
Neyse, bu matrislerin satır ve sütun sayısı istediğimiz gibi olabiliyorsa, şunların her biri ayrı ayrı birer matristir.
[4] [5] [7]
k = [0], l = [2], m = [1]
Demek ki yukarıda gösterdiğim 3x2'lik (genelde sütun önce gelir) bir sayı matrisi olan A'yı, 3x1'lik matris matrisi olarak kabul edebilirim. Bu, tercihi bir bakış açısı olmakla birlikte, geometri konusunda işimize yarayacak bir ayrıntıdır. Bu bakış açısıyla aynı A'yı şu şekilde de gösterebilirim:
A = [k l m]
Aynı bakış açısıyla her matrisin kendisini içeren bir 1x1'lik matris olduğu söylenebilir. (A = [A] gibi) Bu biraz mantıksız gelse de (ve belki yanlış olsa da) işleri epeyce kolaylaştıran bir bakış açısıdır. Bunun örneklerini birazdan göreceğiz.
Matrisler üzerindeki tanımlı en basit işlemler toplama ve çıkarmadır. İki matrisin toplamı ya da farkı demek, bütün elemanlarının tek tek toplam ya da farkından oluşan başka bir matris demektir. Örneğin:
[4 5] [0 2] [4 3]
[2 0] - [1 0] = [1 0]
[7 1] [4 0] [3 1]
Kolayca görüleceği üzere iki matris üzerinde toplama ya da çıkarma yapmak için hem satır hem de sütun sayılarının bire bir aynı olması gerekiyor.
Toplama işlemi için bir adet de etkisiz elemanımız var. O da tüm elemanları sıfır olan matris, yani sıfır matrisi.
[0 0]
[0 0]
[0 0]
Ama sadece bir tane değil bir sürü sıfır matrisi var. Özel birini belirtirken şu sütun ve şu satır sayısına sahip sıfır matrisi deriz. Tabi her biri o sütun ve satır sayısına sahip matrisler üzerindeki toplama işleminin etkisiz elemanı.
Şimdi matris matrisleri bakış açısının toplama ya da çıkarmada sorun çıkarmadığını görelim.
k = [2 9], l = [4 2], m = [2 0], n = [1 1]
[2 9] [2 0] [k] [m] [k+m] [4 9]
[4 2] + [1 1] = [l] + [n] = [l+n] = [5 3]
Hmm biraz fazla bariz. Neyse yine beklemeye devam edin siz.
Matrisler üzerindeki bir diğer işlem (ve de en önemlisi) çarpma işlemi. Ben olsam çarpma işlemini tıpkı toplama ve çıkarma gibi elemanların tek tek çarpımından ibaret yapardım. Ama adamlar düşünmüş ve daha komplike ama çok daha fazla işe yarayan bir çarpma işlemi düşünmüşler. Bu işlemi de bir örnekle görüp sonra açıklamasını verelim.
[0 2]
[3 4 1] [1 9] = [8 44]
[4 2]
Biliyorum sonuç çoktan gökten düşmüş gibi duruyor. Ama bi de tek tek inceleyelim. Öncelikle sonucun ilk satır ve ilk sütununu, yani 8'i nasıl bulduk? Soldaki matrisin ilk satırı ve sağdaki matrisin ilk sütunu dışındaki elemanları görmezden gelirsek şöyle bir görüntü oluşur.
[0 _]
[3 4 1] [1 _] = [8 _]
[4 _]
Burda soldaki satır ile sağdaki sütun arasında şunu yaparsak:
3*0 + 4*1 + 1*4 = 0 + 4 + 4 = 8
Aynı işlemi 44'ü bulmak için yapalım:
[_ 2]
[3 4 1] [_ 9] = [_ 44]
[_ 2]
3*2 + 4*9 + 1*2 = 6 + 36 + 2 = 44
Peki ne yaptık hemen söyleyeyim.
Öncelikle sonucun boylarını nasıl bulduk? Soldaki matrisin satır sayısını (1) ve sağdaki matrisin sütun sayısını (2) kullandık. Bu her zaman böyledir. Tersi şekilde olsaydı da bi şey farketmezdi, o zaman lineer cebirin diğer kısımları ona göre şekillenirdi. 3x1 bir matrisi, 2x3 bir matrisle çarparsam sonuç 2x1 bir matris olur. Genelleme yaparsam NxM bir matrisi KxL bir matrisle çarparsam, sonuç MxK boyunda bir matris olur.
Peki boyda karar kıldık, peki matrisin içini nasıl dolduracağım? Yukarıda dediğim gibi, sonucun i. sütununda ve j. satırında bulunan elemanı bulmak için; sol matrisin j. satırını ve sağ matrisin i. sütununu kullanacağım. Sonra bu satır ve sütundaki her karşılıklı elemanı çarpıp tüm çarpımları toplayacağım. Sol matrisin satırları ve sağ matrisin sütunları önemli benim için. Sonucun boyuna da etki eden bu ikisinin sayıları zaten.
Amaaaa!
Burda bir kısıtlama var karşımızda, Örneği sol matris NxM boyunda olsun, sağ matris de KxL boyunda. Sol matristeki her satırda N tane (sütun sayısı kadar) eleman var. Sağ matristeki her sütunda da L tane (satır sayısı kadar) eleman var. Benim bunları karşılıklı çarpıp toplamı alabilmem için N=L olmalı. Yani:
"Sol matrisin sütun sayısı sağ matrisin satır sayısına eşit olmalı!"
<--3--> [0 2] ^
[3 4 1] [1 9] 3 = [8 44]
[4 2] v
Ama bu sayı sonucun boyuna etki etmiyor.
Bu arada çarpma işleminde genelde işaret kullanılmıyor. Yani A ve B matrislerinin çarpımı AB şeklinde gösteriliyor. Bir de AB çarpımı boy kurallarına uyarken BA çarpımı uymayabilir. (NxM)(KxL) çarpımı N=L iken anlamlıyken, (KxL)(NxM) çarpımı ise M=K iken anlamlıdır.
Çarpma işleminde kafası karışanlar için yavaş yavaş bir daha gidelim. Örneğin iki matrisimiz şöyle olsun:
[a b c] [j k]
A = [d e f], B = [l m]
[g h i] [n o]
Öncelikle nasıl çarpma yapacağımıza bakalım. A'nın sütun sayısı (3), B'nin satır sayısına (3) eşit olduğu için AB işlemi mümkündür. Halbuki B'nin sütun sayısı (2), A'nın satır sayısına (3) eşit olmadığından BA işlemi mümkün değildir.
O zaman AB işlemi de şöyle olsun:
[a b c] [j k] [p q]
AB = [d e f] [l m] = [r s]
[g h i] [n o] [t u]
Bu çarpımda:
p = aj + bl + cn
q = ak + bm + co
r = dj + el + fn
s = dk + em + fo
t = gj + hl + in
u = gk + hm + io
Bu çarpımları elinizle (ya da tercihen mousela) yaparsanız olayın özünü şap diye kavrarsınız sanıyorum ki.
Şimdi bir adet gereksiz atraksiyon yapacağım ama gerekli olduğu yerleri anlamak için bunu inceleyin derim.
Şimdi bir kaç tane daha matris tanımlayayım:
[a b] [c]
C = [d e], D = [f], E = [g h], F = [i]
[j k]
G = [l m], H = [n o]
Bu durumda şöyle bir ilginçlik elde ettik:
[C D] [G] [i]
A = [E F], B = [H], AB = [J]
Yani:
I = CG + DH
J = EG + FH
İlk ifadeyi açması biraz uzun olacak o yüzden ikinciyi açalım:
[j k]
J = [g h] [l m] + [i] [n o] =
J = [gj+hl gk+hm] + [in io] = [gj+hl+in gk+hm+io]
J = [t u]
Yani AB çarpımının alt kısmı. Benim açmadığım ifadeyi de açarsanız şu çıkacak:
[p q]
I = [r s]
Demek ki matris matrisleri görüşü bazen hayatımızı kolaylaştırabilirmiş. Tabi burda çok abuk boyutlarda matrisler seçtim ve çarpılabilmeleri için biraz dikkatli davrandım. Ama 4x4 iki matrisi çarpmak yerine, bu matrislerin her birini 2x2'lik matrisler matrisi haline getirip kendi aralarında bu küçük matrisleri çarpabilirsiniz. Ve daha da güzeli kare matrislerden soldakini satır matrisler matrisi, sağdakini sütun matrisler matrisi yaparsanız bu tutorialın ana konuları çerçevesinde işleriniz çok kolaylaşabilir. Bundan ileride bahsedeceğiz.
Matris mevzusunu burada kesiyorum ama matrislerle ilgili başka olayları konunun içerisinde vereceğim. (Evet bunlar sadece temeldi.) Eğer matris nedir ne değildir tam anlamıyla kavradıysanız, üstüne matris işlemlerini (özellikle çarpmayı) bu şekilde anlatmama rağmen kaptıysanız sizi bir sonraki bölüm olan 'Vektör Matrisleri'ne bekliyorum.