Unity: ScriptableRenderPass ile Özel Kamera Efektleri

Unity'de ScriptableRenderPass ile özel kamera efektleri yazarak oyunlarınıza benzersiz bir görsel imza katın. Performanslı ve esnek efektler geliştirme rehberi.

Modern oyun geliştirme dünyasında görsel çekicilik, oyuncu deneyiminin ayrılmaz bir parçasıdır. Unity’nin Scriptable Render Pipeline (SRP) ve özelde Universal Render Pipeline (URP) ile sunduğu esneklik sayesinde, geliştiriciler oyunlarına sadece performanslı değil, aynı zamanda görsel olarak çarpıcı ve benzersiz kamera efektleri ekleyebilirler. Bu makale, Unity’nin ScriptableRenderPass özelliğini kullanarak nasıl özel kamera efektleri yazabileceğinizi adım adım açıklayacak ve projelerinize sanatsal bir dokunuş katmanız için size rehberlik edecektir.

Geleneksel Unity renderlama yöntemleri, post-processing efektleri üzerinde sınırlı kontrol sunarken, SRP ve ScriptableRenderPass, renderlama döngüsünün her aşamasına müdahale etme imkanı tanır. Bu sayede, sadece hazır efektleri kullanmakla kalmaz, kendi vizyonunuza uygun, tamamen özelleştirilmiş efektler yaratabilirsiniz. İster retro bir piksel sanatı filtresi, ister fütüristik bir siberpunk ışıltısı olsun, ScriptableRenderPass size bu gücü verir.

ScriptableRenderPass Nedir?

ScriptableRenderPass, Unity’nin Scriptable Render Pipeline (SRP) mimarisinin temel yapı taşlarından biridir. Adından da anlaşılacağı gibi, renderlama adımlarını (pass) programatik olarak tanımlamanıza olanak tanır. Her bir ScriptableRenderPass, belirli bir görevi yerine getirmek üzere tasarlanmıştır; örneğin, sahneyi bir dokuya renderlamak, gölgeleri çizmek veya bir post-processing efekti uygulamak gibi. Bu sistem, geliştiricilere renderlama sürecini tam anlamıyla kontrol etme ve optimize etme yeteneği sunar.

URP gibi bir SRP uygulamasında, renderlama işlemi bir dizi RenderPass’in sırayla yürütülmesiyle gerçekleşir. Siz de kendi özel RenderPass’inizi oluşturarak bu zincire dahil olabilir ve istediğiniz noktada renderlama mantığınızı enjekte edebilirsiniz. Bu, kamera efektleri gibi post-processing işlemleri için ideal bir yapıdır, çünkü sahne renderlandıktan sonra, son görüntüyü işlemek üzere kendi pass’inizi ekleyebilirsiniz.

Neden ScriptableRenderPass ile Özel Efektler Yazmalısınız?

Özel kamera efektleri için ScriptableRenderPass kullanmanın birçok avantajı bulunmaktadır:

  • Tam Kontrol ve Esneklik: Renderlama döngüsünün her aşamasına müdahale edebilir, istediğiniz render hedeflerini kullanabilir ve kendi shader’larınızı tamamen entegre edebilirsiniz. Bu, hazır efekt paketlerinin ötesinde, benzersiz görsel stiller yaratmanızı sağlar.
  • Performans Optimizasyonu: Sadece ihtiyacınız olan işlemleri gerçekleştirerek gereksiz render çağrılarından ve GPU işlemlerinden kaçınabilirsiniz. Örneğin, belirli bir pass’i sadece belirli koşullarda (düşük grafik ayarlarında kapalı tutmak gibi) çalıştırabilirsiniz.
  • Modülerlik ve Yeniden Kullanılabilirlik: Her bir efekti ayrı bir RenderPass olarak tasarlamak, kodunuzu daha düzenli hale getirir ve farklı projelerde veya farklı efekt kombinasyonlarında kolayca yeniden kullanmanızı sağlar.
  • URP/HDRP Entegrasyonu: Mevcut render pipeline’ınızla sorunsuz bir şekilde entegre olur. Unity’nin güncel renderlama mimarisine uygun çözümler geliştirmenizi sağlar.
  • Daha İyi Hata Ayıklama: Renderlama sürecini daha şeffaf hale getirdiği için, görsel hataları veya performans sorunlarını tespit etmek ve gidermek daha kolaydır.

Özel Kamera Efekti Adımları: Temel Yapı

ScriptableRenderPass ile özel bir kamera efekti yazmak için izlemeniz gereken temel adımlar şunlardır:

1. Yeni Bir ScriptableRendererFeature Oluşturma

Her özel RenderPass, bir ScriptableRendererFeature sınıfı içinde tanımlanır. Bu özellik, Unity editöründe bir varlık olarak görünür ve URP Renderer ayarlarınıza eklenebilir. Project penceresinde sağ tıklayın: Create > Rendering > URP > Renderer Feature.

2. ScriptableRenderPass Sınıfını Tanımlama

Oluşturduğunuz Renderer Feature’ın içinde, ScriptableRenderPass sınıfından türeyen kendi pass’inizi yazmanız gerekir. Bu sınıfın temel metotları şunlardır:

  • Configure(CommandBuffer cmd, RenderTargetIdentifier cameraColorTarget, RenderTargetIdentifier cameraDepthTarget): Pass’in render hedeflerini ve temizleme ayarlarını yapılandırmak için kullanılır.
  • Execute(ScriptableRenderContext context, ref RenderingData renderingData): Asıl renderlama mantığının yazıldığı yerdir. Burada CommandBuffer kullanarak GPU komutları gönderirsiniz. Örneğin, sahnenin bir dokuya kopyalanması (Blit) veya bir shader’ın uygulanması gibi.
  • FrameCleanup(CommandBuffer cmd): Pass bittikten sonra geçici render hedeflerini temizlemek için kullanılır.

3. Giriş/Çıkış Dokularını Yönetme

Post-processing efektleri genellikle kameranın o anki görüntüsünü alır, üzerinde işlem yapar ve sonucu tekrar ekrana yazar. Bu işlem için RenderTargetIdentifier yapıları kullanılır. Blit metodu, bir render hedefinden diğerine çizim yapmanızı sağlar ve bu sırada bir materyal (shader) uygulayabilir. Genellikle kameranın ana renk dokusu _CameraColorTexture olarak adlandırılır.

4. Efekt Shader’ı Yazma

Efektin görsel mantığı bir shader (genellikle HLSL) içinde tanımlanır. Bu shader, kameranın mevcut görüntüsünü giriş olarak alır, her piksel üzerinde hesaplamalar yapar ve yeni bir renk değeri döndürür. Basit bir post-processing shader’ı genellikle bir vertex shader (ekranı kaplayan bir dörtgen çizmek için) ve bir fragment shader (piksel başına rengi hesaplamak için) içerir. Örneğin, gri tonlama efekti için her pikselin RGB değerlerinin ortalaması alınabilir.

5. Materyal Oluşturma ve Entegrasyon

Yazdığınız shader’ı kullanmak için bir materyal oluşturmanız gerekir. Bu materyal, RenderPass içinde Blit işlemi sırasında kullanılır. RenderFeature’ınızda, bu materyali bir değişken olarak tanımlayabilir ve Inspector’dan atamasını yapabilirsiniz. Böylece, shader’ınızdaki parametreleri (örneğin, parlaklık, kontrast) kolayca ayarlayabilirsiniz.

6. Injection Point Ayarlama

Renderer Feature’ınızın Inspector penceresinde, Event (Olay) ayarı bulunur. Bu, RenderPass’inizin renderlama döngüsünün hangi aşamasında çalıştırılacağını belirler. Kamera efektleri için genellikle AfterRenderingPostProcessing veya AfterRenderingOpaques gibi değerler kullanılır. Bu, sahnedeki nesneler renderlandıktan sonra, ancak Unity’nin kendi post-processing efektlerinden önce veya sonra efekti uygulamanıza olanak tanır.

Pratik Bir Örnek: Basit Bir Gri Tonlama Efekti

Şimdi bu adımları, sahneye gri tonlama efekti uygulayan basit bir örnekle somutlaştıralım:

Adım 1: Shader Oluşturma (HLSL)

Projenizde yeni bir Shader Graph veya HLSL Shader oluşturun (örn: GrayscaleShader.shader).


Shader "Unıtyegitim/GrayscaleEffect"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _GrayscaleAmount ("Grayscale Amount", Range(0,1)) = 1
    }
    SubShader
    {
        Tags { "RenderPipeline"="UniversalPipeline" "Queue"="Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off
        Cull Off

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);

            half _GrayscaleAmount;

            Varyings vert(Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.uv = input.uv;
                return output;
            }

            half4 frag(Varyings input) : SV_Target
            {
                half4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
                half grayscale = dot(color.rgb, half3(0.299, 0.587, 0.114)); // Luminosity method
                color.rgb = lerp(color.rgb, grayscale.xxx, _GrayscaleAmount);
                return color;
            }
            ENDHLSL
        }
    }
}

Adım 2: Materyal Oluşturma

Yukarıdaki shader’ı kullanan bir materyal oluşturun (örn: GrayscaleMaterial). Project penceresinde sağ tıklayın: Create > Material ve shader olarak Unıtyegitim/GrayscaleEffect seçin.

Adım 3: ScriptableRenderPass ve RendererFeature Oluşturma (C#)

Projenizde iki C# scripti oluşturun: GrayscaleRenderPass.cs ve GrayscaleFeature.cs.

GrayscaleRenderPass.cs:


using UnityEngine; 
using UnityEngine.Rendering; 
using UnityEngine.Rendering.Universal; 

public class GrayscaleRenderPass : ScriptableRenderPass 
{
    private Material grayscaleMaterial; 
    private RenderTargetIdentifier source; 
    private RenderTargetHandle tempTexture;

    public GrayscaleRenderPass(Material material) 
    {
        this.grayscaleMaterial = material; 
        renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; // Efektin ne zaman uygulanacağı
        tempTexture.Init("_TempGrayscaleTexture"); // Geçici doku için benzersiz isim
    }

    public void SetSource(RenderTargetIdentifier source) 
    {
        this.source = source; 
    }

    public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) 
    {
        // Geçici dokuyu oluştur
        RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
        descriptor.depthBufferBits = 0; // Derinlik tamponuna ihtiyacımız yok
        cmd.GetTemporaryRT(tempTexture.id, descriptor, FilterMode.Bilinear); 
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) 
    {
        if (grayscaleMaterial == null) 
        {
            Debug.LogError("Grayscale Material is null!"); 
            return; 
        }

        CommandBuffer cmd = CommandBufferPool.Get("Grayscale Effect"); 

        // Kaynak dokudan geçici dokuya shader ile çizim yap
        Blit(cmd, source, tempTexture.Identifier(), grayscaleMaterial, 0); 
        // Geçici dokudan kameranın hedef dokusuna çizim yap
        Blit(cmd, tempTexture.Identifier(), source); 

        context.ExecuteCommandBuffer(cmd); 
        CommandBufferPool.Release(cmd); 
    }

    public override void OnCameraCleanup(CommandBuffer cmd) 
    {
        // Geçici dokuyu temizle
        cmd.ReleaseTemporaryRT(tempTexture.id); 
    }
}

GrayscaleFeature.cs:


using UnityEngine; 
using UnityEngine.Rendering; 
using UnityEngine.Rendering.Universal; 

public class GrayscaleFeature : ScriptableRendererFeature 
{
    [SerializeField] 
    private Material grayscaleMaterial; 

    private GrayscaleRenderPass grayscalePass; 

    public override void Create() 
    {
        grayscalePass = new GrayscaleRenderPass(grayscaleMaterial); 
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) 
    {
        if (grayscaleMaterial == null) 
        {
            Debug.LogWarning($"Missing Grayscale Material. {GetType().Name} will not run."); 
            return; 
        }

        // Kameranın renk dokusunu pass'e ilet
        grayscalePass.SetSource(renderer.cameraColorTarget); 
        renderer.EnqueuePass(grayscalePass); 
    }
}

Adım 4: Unity Editöründe Kurulum

  1. Project penceresinde sağ tıklayın: Create > Rendering > URP > Renderer Feature. Bu, New Renderer Feature adında bir varlık oluşturacaktır. Bu varlığa GrayscaleFeature adını verin.
  2. Bu varlığı seçin ve Inspector’da Grayscale Material alanına, daha önce oluşturduğunuz GrayscaleMaterial materyalini sürükleyip bırakın.
  3. Projenizin UniversalRendererData varlığını bulun (genellikle Assets/Settings altında). Bu varlığı seçin ve Inspector’da Renderer Features listesine GrayscaleFeature varlığını ekleyin.

Artık oyununuz çalıştırıldığında, sahnede gri tonlama efekti uygulanmış olacaktır. GrayscaleMaterial üzerindeki Grayscale Amount değerini değiştirerek efektin yoğunluğunu ayarlayabilirsiniz.

Performans ve Optimizasyon İpuçları

Özel kamera efektleri geliştirirken performansı göz önünde bulundurmak kritik öneme sahiptir:

  • Gereksiz Pass’lerden Kaçının: Sadece ihtiyacınız olan efektleri ve pass’leri kullanın. Her pass’in bir maliyeti vardır.
  • Shader Optimizasyonu: Karmaşık shader’lar GPU üzerinde daha fazla yük oluşturur. Mümkün olduğunca basit, optimize edilmiş shader’lar yazın. Yüksek hassasiyetli (float) yerine düşük hassasiyetli (half) değişkenler kullanın.
  • RenderTarget Havuzlaması: Sürekli yeni RenderTarget oluşturmak yerine, Unity’nin RenderTexturePool veya benzeri mekanizmaları kullanarak dokuları yeniden kullanın. Bu, GPU belleği tahsisi ve serbest bırakma maliyetini azaltır.
  • Koşullu Yürütme: Efektleri sadece belirli koşullar altında çalıştırın (örn: uzak kameralar için daha basit efektler, düşük kaliteli ayarlarda efektleri kapatma).
  • CommandBuffer Kullanımı: CommandBuffer‘ları verimli bir şekilde kullanın. Her karede gereksiz yere yeni komut tamponları tahsis etmekten kaçının ve işiniz bittiğinde bunları havuza geri bırakın.

Sonuç

Unity’nin ScriptableRenderPass özelliği, geliştiricilere oyunlarının görsel kimliğini tamamen kendi kontrollerine alma gücü verir. Bu makalede ele aldığımız adımları ve ipuçlarını takip ederek, projelerinize sadece estetik değil, aynı zamanda performans açısından da optimize edilmiş, benzersiz kamera efektleri ekleyebilirsiniz. Bu güçlü araç, Unity’de görsel olarak çarpıcı ve unutulmaz oyun deneyimleri yaratmanız için kapıları aralar. Kendi özel efektlerinizi yaratmaya başlayın ve oyunlarınızı bir sonraki seviyeye taşıyın!