Compute Shader

JeasonBoy發表於2024-06-28

Compute Shader 是一種高階功能,用於在 GPU 上執行平行計算任務。它非常適合處理大量資料,執行復雜的數學計算,或在高效能圖形處理中使用。

常用於,需要密集的並行多執行緒計算,CPU不擅長平行計算,丟給GPU去算,然後把結果返回給CPU,或者直接渲染到螢幕上。

需要圖形API,Vulkan 或 DX11以上 或 OpenglES3以上 才支援

Compute Shader是獨立於渲染管線的,不屬於管線流水線過程,屬於額外的功能

注:但是在安卓下,Vulkan 不知道為啥不支援,OpenglES3才行

Unity設定預設是開啟auto api的,安卓實測在手機上會自動選OpenglES3,

但若取消勾選,可以看到有2個api,預設Vulkan在上面,在手機上透過SystemInfo.graphicsDeviceType獲取到的當前api是OpenGLES3,但是也載入不出影像,把OpenGLES3挪到上面變成預設的就正常了

Unity可以透過 SystemInfo.graphicsDeviceType 獲取裝置的圖形API版本

透過 SystemInfo.supportsComputeShaders 判斷裝置是否支援Compute Shader

示例:

MyComputeShader.compute

// 這行程式碼告訴編譯器將 CSMain 函式編譯為一個 Compute Shader 核心。你可以在一個著色器檔案中定義多個核心,但每個核心都會有自己的 #pragma kernel 指令。例如,一個核心用於影像處理,另一個核心用於物理模擬。
// 核心(Kernel) 是指一個具體的計算任務單元。每個核心都可以看作一個獨立的函式,它們在 GPU 上並行執行計算操作,
#pragma kernel CSMain

// 計算著色器的輸入和輸出緩衝區,一般是從C#裡往這裡傳入一張圖片,在這裡計算完後修改這張圖片
RWTexture2D<float4> Result;

// 定義一個執行緒組的大小,xyz分別表示三個維度上執行緒的數量,這裡是一個組有8*8*1=64個執行緒
// 為什麼要這樣分成3個維度,是跟GPU的硬體設計相關,GPU 硬體在排程和訪問記憶體時通常對某些執行緒組大小和形狀有最佳化。例如,許多 GPU 硬體對 2D 執行緒組(如 8x8)進行最佳化,使得記憶體訪問和執行緒排程更加高效。
[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
   // uint3 id : SV_DispatchThreadID 表示每個執行緒在執行時都會獲得一個唯一的 3D 執行緒 ID (id),這個 ID 是由 Dispatch 函式確定的排程執行緒組的索引。xy實際上相當於圖片的uv座標
    // 在這裡編寫你的計算程式碼
    Result[id.xy] = float4(id.x, id.y, 0.0, 1.0);
}

ComputeShaderExample.cs

// 建立一張RT,並傳給CS去計算,然後將結果直接畫在相機上
using UnityEngine;

[RequireComponent (typeof(Camera))]
public class ComputeShaderExample : MonoBehaviour
{
    public ComputeShader computeShader;
    public RenderTexture outputTexture;

    void Start()
    {
        // 初始化 RenderTexture
        int textureSize = 256;
        outputTexture = new RenderTexture(textureSize, textureSize, 0);
        outputTexture.enableRandomWrite = true;
        outputTexture.Create();

        // 設定要使用的核心,設定計算著色器的引數
        int kernelHandle = computeShader.FindKernel("CSMain");
        computeShader.SetTexture(kernelHandle, "Result", outputTexture);

        // 執行計算著色器
     // 影像解析度是128x128,X維度分成16個組,每個組有8個執行緒,剛好128個執行緒可以處理128個畫素,Y維也同理,因為是處理二維影像,所以Z維可以只為1
        computeShader.Dispatch(kernelHandle, textureSize / 8, textureSize / 8, 1);
    }

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        // 將計算結果渲染到螢幕上
        Graphics.Blit(outputTexture, destination);
    }
}

相關文章