Skip to main content

View frustum cullingbir önceki yazıda oluşturduğumuz modelleri çevreleyen küreleri kullanarak, “view frustum” adı verilen, sadece kameranın gördüğü bölgenin içinde kalan modellerin çizilmesi, bu bölgenin dışında kalan modellerin ise çizimden ayırt edilerek performans sağlanması işlemidir. 3D bir modelin çizdirilmesi sayfasında view frustum ile ilgili detayları görebilirsiniz. Bu işlem özellikle çok sayıda model içeren, kalabalık sahnelerde ciddi performans artışı sağlamaktadır. Ancak birçok küçük modelden oluşan bir sahnede her model kontrol edileceğinden verimi düşürebilir.

View frustum culling” işlemini yapabilmek için öncelikle view frustum denilen, kameranın gördüğü bölgenin hesaplanması gerekir. Kameralara özgü görünüm ve projeksiyon matrislerini bildiğimiz için, kameranın gördüğü alanı da kolaylıkla hesaplayabiliriz. Hesaplayacağımız bu değeri tutmak için kamera sınıfına (abstract Camera class) BoundingFrustum sınıfında yeni biz özellik ekleyelim. BoundingFrustum sınfı view frustum içerisinde BoundingSphere olup olmadığını kontrol eden bir methoda sahiptir.

public BoundingFrustum Frustum { get; private set; }

Görünüm ve projeksiyon matrislerinin sürekli değişeceğini düşünürsek, kameranın gördüğü alanı da sürekli güncellememiz gerekir. Bunu yapmak için generateFrustum() adında bir method yazarak, View ve Projection özellikleri içerisinde çağıralım.

Matrix view;
Matrix projection;

public Matrix Projection
{
    get { return projection; }
    protected set
    {
        projection = value;
        generateFrustum();
    }
}
public Matrix View
{
    get { return view; }
    protected set
    {
        view = value;
        generateFrustum();
    }
}

Kodlar eklendikten sonra kamera sınıfı şu şekildedir.

public abstract class Camera
{
    Matrix view;
    Matrix projection;
    public BoundingFrustum Frustum { get; private set; }
    protected GraphicsDevice GraphicsDevice { get; set; }

    public Matrix Projection
    {
        get { return projection; }
        protected set
        {
            projection = value;
            generateFrustum();
        }
    }
    public Matrix View
    {
        get { return view; }
        protected set
        {
            view = value;
            generateFrustum();
        }
    }

    public Camera(GraphicsDevice graphicsDevice)
    {
        this.GraphicsDevice = graphicsDevice;
        generatePerspectiveProjectionMatrix(MathHelper.PiOver4);
    }

    private void generateFrustum()
    {
        Matrix viewProjection = View * Projection;
        Frustum = new BoundingFrustum(viewProjection);
    }

    private void generatePerspectiveProjectionMatrix(float FieldOfView)
    {
        PresentationParameters pp = GraphicsDevice.PresentationParameters;
        float aspectRatio = (float)pp.BackBufferWidth / (float)pp.BackBufferHeight;
        this.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), aspectRatio, 0.1f, 1000000.0f);
    }

    public virtual void Update()
    {
    }

    public bool BoundingVolumeIsInView(BoundingSphere sphere)
    {
        return (Frustum.Contains(sphere) != ContainmentType.Disjoint);
    }
    public bool BoundingVolumeIsInView(BoundingBox box)
    {
        return (Frustum.Contains(box) != ContainmentType.Disjoint);
    }
}

Yaptığımız değişiklikleri görmek için Game1 sınıfındaki Draw() methodunu şu şekilde değiştirmek faydalı olacaktır. Burada dikkat edilirse çizilen hiçbir model olmadığı zaman ekran kırmızı renge boyanmaktadır. Kodun nasıl çalıştığını anladığınızda bu kısımları kaldırabilirsiniz.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    int nModelsDrawn = 0;
    foreach (CModel model in models)
        if (camera.BoundingVolumeIsInView(model.BoundingSphere))
        {
            nModelsDrawn++;
            model.Draw(camera.View, camera.Projection);
        }
    if (nModelsDrawn == 0)
        GraphicsDevice.Clear(Color.Red);
    base.Draw(gameTime);
}

Sade haldeki Draw() methodu şu şekildedir.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);
    foreach (CModel model in models)
        if (camera.BoundingVolumeIsInView(model.BoundingSphere))
            model.Draw(camera.View, camera.Projection);
    base.Draw(gameTime);
}