> Ana Sayfa > Yazılar

Işın izleme Algoritmaları (Raycasting)

Giriş

Bir nesnenin çevresini algılayıp anlamlandırması, oyun geliştirme ve simülasyon dünyasında kritik bir konudur. Bu çevresel veriler kullanılarak dinamik ışıklandırma, gölge efektleri, yapay zeka karar mekanizmaları ve daha birçok oyun mekaniği inşa edilebilir. Örneğin, bir odadaki ışığın yan odaya sızıp sızmadığını belirlemek için ışık kaynağı ile hedef oda arasında herhangi bir engelin bulunup bulunmadığını kontrol etmemiz gerekir. Benzer şekilde, bir yapay zeka ajanının hedefine doğru ilerlerken rotası üzerinde bir engel olup olmadığını da bu algoritmalar sayesinde tespit edebiliriz.
Çevre modelleme yöntemlerini temel olarak ikiye ayırabiliriz: Izgara (grid) tabanlı ve nesne (vektör/poligon) tabanlı yapılar. Bu yazıda, özellikle performans avantajlarıyla öne çıkan grid tabanlı ışın izleme algoritmalarına odaklanacağız.

Digital Differential Analyzer (DDA) Algoritması

Kare hücrelerden (grid) oluşan büyük bir labirentin içinde olduğumuzu düşünelim. Bu hücrelerin bazıları boş geçitlerden, bazıları ise geçilemez duvarlardan oluşsun. Elimizde de bir lazer var ve bu lazerin duvara ilk çarptığı noktayı hassas bir şekilde bulmak istiyoruz. Bu problemi çözmek için DDA (Digital Differential Analyzer) algoritmasını kullanabiliriz. DDA, bir ışının ızgara tabanlı bir harita üzerinde nasıl ilerlediğini hesaplayan oldukça verimli bir yöntemdir.
Lazer ışınının katettiği her milimetreyi veya pikseli tek tek kontrol ederek hangi hücreye girdiğini bulabilirdik; ancak bu yaklaşım son derece yavaş ve işlemciyi yoran bir yöntem olurdu. Üstelik tam çarpma noktasını bulabilmek için çok sayıda ek hesaplama yapmamız gerekirdi. DDA algoritması ise bu problemi matematiksel olarak çok daha hızlı ve zarif bir şekilde çözer. Işını milim milim ilerletmek yerine, her adımda bir sonraki dikey veya yatay grid çizgisine doğrudan atlamamızı sağlar.
SembolAçıklamasıTipi
rayPosX,rayPosYrayPosX,rayPosYIşının başlangıç koordinatıGirdi
rayDirX,rayDirYrayDirX,rayDirYIşının yön vektörüGirdi
cellWidth/cellHeightcellWidth/cellHeightHücrelerin genişliği ve yüksekliğiGirdi
rayDirLenrayDirLenIşının yön vektörünün uzunluğuAra Değişken
uRayDirX,uRayDirYuRayDirX,uRayDirYIşının yön vektörünün normalleştirilmiş haliAra Değişken
sideDistX,sideDistYsideDistX,sideDistYİlk hücre sınırına olan uzaklık (sideDist)Ara Değişken
deltaDistX,deltaDistYdeltaDistX,deltaDistYHücreler arası adım boyutu (deltaDist)Ara Değişken
stepX,stepYstepX,stepYEksenlerdeki adım yönü (+1 / -1)Ara Değişken
intrsX,intrsYintrsX,intrsYIşın ile duvarın kesişme noktasıÇıktı

DDA Adımları

  1. Adım 1

    Işın vektörünün özelliklerini hesapla

    Işın yön vektörünün uzunluğunu hesaplayarak başlarız. Bu değer, ışının doğrultusunu ve büyüklüğünü belirlememizi sağlar.

    rayDirLen=rayDirX2+rayDirY2rayDirLen = \sqrt{rayDirX^2 + rayDirY^2}

    Işın yön vektörünü normalleştirerek birim yön vektörünü elde ederiz.

    uRayDirX=rayDirXrayDirLenuRayDirX = \frac{rayDirX}{rayDirLen}
    uRayDirY=rayDirYrayDirLenuRayDirY = \frac{rayDirY}{rayDirLen}

    Burada ise ışının eksenler üzerindeki ilerleme yönünü tespit ederiz. Daha basit bir ifadeyle: Işın X ekseninde pozitif yönde ilerliyorsa stepX değeri +1, negatif yönde ilerliyorsa -1 olur. Aynı tespit stepY ile Y ekseni için de gerçekleştirilir.

    stepX=uRayDirX<0ise1deg˘ilse1stepX = uRayDirX < 0 ise -1 değilse 1
    stepY=uRayDirY<0ise1deg˘ilse1stepY = uRayDirY < 0 ise -1 değilse 1
  2. Adım 2

    Adım boyutlarını hesapla

    Işının kendi doğrultusu üzerinde ilerlerken, tam olarak 1 hücre genişliği (X yönünde) veya 1 hücre yüksekliği (Y yönünde) katetmesi için gitmesi gereken toplam mesafeleri (deltaDistX, deltaDistY) hesaplarız.

    deltaDistX=abs(cellWidth/uRayDirX)deltaDistX = abs(cellWidth / uRayDirX)
    deltaDistY=abs(cellHeight/uRayDirY)deltaDistY = abs(cellHeight / uRayDirY)
  3. Adım 3

    Başlangıç farkını hesapla

    Işın genellikle bir hücrenin tam köşesinden değil, herhangi bir iç noktasından başlar. Bu adımda, ışının başlangıç noktasından X ve Y eksenlerindeki en yakın ilk grid çizgisine (hücre sınırına) ulaşması için gitmesi gereken ilk mesafeleri (sideDistX, sideDistY) hesaplarız.

    sideDistX={(mapX+1)cellWidthrayPosXuRayDirXstepX>0(mapXcellWidth)rayPosXuRayDirXdegilse;sideDistX = \begin{cases} \dfrac{(mapX + 1) * cellWidth - rayPosX}{uRayDirX} & stepX > 0\\ \\ \dfrac{(mapX * cellWidth)- rayPosX}{uRayDirX} & \text{degilse;} \end{cases}
    sideDistY={((mapY+1)cellHeightrayPosY)uRayDirYstepY>0(mapYcellHeight)rayPosYuRayDirYdegilse;sideDistY = \begin{cases} \dfrac{((mapY + 1) * cellHeight - rayPosY)}{uRayDirY} & stepY > 0\\ \\ \dfrac{(mapY * cellHeight)- rayPosY}{uRayDirY} & \text{degilse;} \end{cases}
  4. Adım 4

    Işını bir adım ilerlet

    Hangi eksendeki hücre sınırına olan uzaklık (sideDist) daha küçükse, ışın o eksen boyunca bir sonraki hücreye atlatılır. Seçilen eksendeki toplam kat edilen mesafe adım boyutu (deltaDist) kadar artırılır ve harita indeksi (mapX veya mapY) güncellenir.

    Eg˘er sideDistX<sideDistY ise:    sideDistX=sideDistX+deltaDistX    mapX=mapX+stepX\text{Eğer } sideDistX < sideDistY \text{ ise:} \\ \ \ \ \ sideDistX = sideDistX + deltaDistX \\ \ \ \ \ mapX = mapX + stepX

    Degilse:     sideDistY=sideDistY+deltaDistY    mapY=mapY+stepY\text{Degilse: } \\ \ \ \ \ sideDistY = sideDistY + deltaDistY \\ \ \ \ \ mapY = mapY + stepY
  5. Adım 5

    Işının katedilen mesafesi kontrolü

    Işının katettiği toplam mesafe kontrol edilir. Eğer ışın belirlenen maksimum menzili (maxRayLength) aşmışsa, menzil içinde bir engele çarpmadığı kabul edilerek tarama sonlandırılır.

    Eg˘er rayTravelDistancemaxRayLength ise:    Is¸ın engele c¸arpmamıs¸tır\text{Eğer } rayTravelDistance \ge maxRayLength \text{ ise:} \\ \ \ \ \ \text{Işın engele çarpmamıştır}
  6. Adım 6

    Çarpışma kontrolü

    Işının ulaştığı güncel hücrede (mapX, mapY) bir engel veya duvar olup olmadığı kontrol edilir. Eğer bu hücre doluysa, ışının duvara çarptığı tespit edilir.

    Eg˘er mapX,mapY hu¨cresinde duvar varsa:    Is¸ın duvara c¸arptı\text{Eğer } mapX, mapY \text{ hücresinde duvar varsa:} \\ \ \ \ \ \text{Işın duvara çarptı}
  7. Adım 7

    Kesişim noktasını döndür veya döngüye devam et

    Eğer 6. adımda bir çarpışma tespit edildiyse, çarpışmanın gerçekleştiği kesin koordinatlar (intrsX, intrsY) hesaplanır ve geri döndürülür. Eğer menzil aşımı veya çarpışma gerçekleşmediyse, ışın bir sonraki adım için Adım 4'e döner ve döngü devam eder.

    Eg˘er ıs¸ın duvara c¸arptıysa;return(    rayPosX+uRayDirXrayTravelDistance,    rayPosY+uRayDirYrayTravelDistance)\text{Eğer ışın duvara çarptıysa;} \\ \\ return (\\ \ \ \ \ rayPosX + uRayDirX * rayTravelDistance, \\ \ \ \ \ rayPosY + uRayDirY * rayTravelDistance \\ )
?
Algoritma Adımı

Hazırlık: Işını ayarlayın.

Lazer ucundan tutup yönünü ve boyunu ayarlayabilirsiniz.

Girdiler
Start:-32.0, 16.0
End:16.0, -16.0
Cell:0.0x0.0
Işın Vektörü
Pos:-32.0, 16.0
Dir:48.00, -32.00
Step:1, -1
DDA Değerleri
Delta:0.00, 0.00
Side:0.0, 0.0
Map:0, 0

DDA Algoritması Örnek Kodları

DDA (Digital Differential Analyzer) algoritmasının Python, Java ve C# dillerinde yazılmış örnek implementasyonlarını aşağıdan inceleyebilirsiniz:
            
        

Pratik Uygulama: Görüş Hattı

Izgara (grid) tabanlı bir dünyada ışın izleme, yalnızca teorik bir matematik konusu değil; modern oyun mekaniklerinin en temel yapı taşlarından biridir. DDA algoritması sayesinde, yapay zekanın oyuncuyu görüp görmediğini (görüş hattı) veya dinamik bir ışık kaynağının hangi hücreleri aydınlatacağını son derece yüksek bir performansla hesaplayabiliriz.
Hedef Görünmüyor
            
        

Pratik Uygulama: Dinamik Görünürlük ve Görüş Alanı

Bu bölümde, ilk örneğimizdeki dinamik görünürlük ve ışıklandırma sistemini farklı bir harita yapısı üzerinde deneyimleyebilirsiniz. Ajanın 360 derecelik görüş alanını, engellerin bu alanı nasıl gölgelediğini ve ışın izleme mantığının gerçek zamanlı görsel yansımasını burada inceleyebilirsiniz.
Işınlar
16
            
        

Pratik Uygulama: LIDAR Sensörü ve Nokta Bulutu Taraması

Gerçek dünyada LIDAR sensörleri, fırlatılan lazer ışınının geri dönme süresini (Time-of-Flight - ToF) ölçerek engellere olan doğrudan mesafeyi tespit eder. Bilgisayar simülasyonlarında ise sanal sensörün bu engelleri ve mesafeyi hesaplayabilmesi için altta DDA (Digital Differential Analyzer) algoritması çalıştırılır. Ajanın merkezinden çıkan tek bir lazer ışını 360° boyunca belirli bir açı adımıyla kesikli olarak döner. DDA ile hesaplanan kesişim noktaları haritada nokta bulutu (point cloud) halinde saklanır. Ajan hareket ettirildiğinde tespit edilen noktalar dünya koordinatlarında sabit kalır; dönen lazer ışını aynı açı adımına ulaştığında noktaları ajanın yeni konumuna göre kademeli olarak günceller.
Işın Adımı
72
            
        

Referanslar ve İleri Okuma

Işın izleme (Raycasting) ve DDA (Digital Differential Analyzer) algoritmalarının arkasındaki teoriyi, matematiği ve farklı programlama dillerindeki gelişmiş uygulamalarını daha derinlemesine incelemek için aşağıdaki klasik ve popüler makalelere göz atabilirsiniz: