前言 :
也許你想先看 CPU 版,任意門 :
https://blog.csdn.net/weixin_38884324/article/details/80458545
另外這篇的新版本在這
https://blog.csdn.net/weixin_38884324/article/details/80673220
這裡是記錄我之前多蠢的樣子,請不要參考也不要亂學,不過是可以當負面教材就是哈。
C # :
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class GPU_RAW_To_RGB : MonoBehaviour {
public RawImage outputImage;
public RawImage outputImage2;
public ComputeShader shader;
int[] a;
void Start()
{
print("是否支持 GPU 運算 ? "+SystemInfo.supportsComputeShaders);
System.Diagnostics.Stopwatch time = new System.Diagnostics.Stopwatch();
time.Start();
a = ByteArray_To_IntArray(File.ReadAllBytes("C:/FFMPEG/Hello.raw"));
time.Stop();
print("檔案讀取 " + time.Elapsed.TotalSeconds + " 秒");
Run();
}
// 之後這裡寫成多線程 (多執行序) 速度也許會快點
int[] ByteArray_To_IntArray(byte [] b) {
int[] i = new int[b.Length];
for (int x = 0; x < b.Length; x++)
{
i[x] = b[x];
}
return i;
}
public void Run()
{
System.Diagnostics.Stopwatch time = new System.Diagnostics.Stopwatch();
time.Start();
// 預先必須要先知道圖片寬高,因為 RAW 檔案沒有寬高的資訊
int width = 4208;
int height = 3120;
// ------------------------------------------------
//初始化 RAW 圖片
RenderTexture t = new RenderTexture(width, height, 24);
t.enableRandomWrite = true;
t.Create();
outputImage.texture = t;
// ------------------------------------------------
//初始化 RGB 圖片
RenderTexture t2 = new RenderTexture(width, height, 24);
t2.enableRandomWrite = true;
t2.Create();
outputImage2.texture = t2;
// ------------------------------------------------
// CSMain
int k = shader.FindKernel("CSMain");
ComputeBuffer buffer = new ComputeBuffer(a.Length, 4);
buffer.SetData(a);
shader.SetBuffer(k, "a", buffer);
shader.SetInt("width", width);
shader.SetInt("height", height);
shader.SetTexture(k, "outputTexture", t);
shader.Dispatch(k, width, height, 1);
// ------------------------------------------------
// ToRGB
int k2 = shader.FindKernel("ToRGB");
shader.SetInt("width", width);
shader.SetInt("height", height);
shader.SetTexture(k2, "outputTexture", t);
shader.SetTexture(k2, "outputTexture2", t2);
shader.Dispatch(k2, width, height, 1);
// 釋放
buffer.Dispose();
time.Stop();
print("GPU 執行 " + time.Elapsed.TotalSeconds + " 秒");
}
}
ComputeShader :
#pragma kernel CSMain
int width;
int height;
// ( CPU -> GPU )
StructuredBuffer<int> a;
// ( GPU -> CPU )
RWTexture2D <float4> outputTexture;
RWTexture2D <float4> outputTexture2;
[numthreads(1, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID, uint3 _group_thread_id : SV_GroupThreadID)
{
int i = ((width * id.y) + id.x) * 2; // 因為 RAW 10bit 佔用 2 個 byte 所以要乘二
int L = a[i];
int H = a[i + 1];
int D = (H << 8) + L;
float C = D / 1024.0f;
float4 color;
if (id.y % 2 == 0)
{
if (id.x % 2 == 0)
{
color = float4(0, C, 0, 1);
}
else
{
color = float4(C, 0, 0, 1);
}
}
else
{
if (id.x % 2 == 0)
{
color = float4(0, 0, C, 1);
}
else
{
color = float4(0, C, 0, 1);
}
}
int y_mirror = height - 1 - id.y; // 上下反轉
outputTexture[float2(id.x, y_mirror)] = color; // 丟回 CPU
}
#pragma kernel ToRGB
[numthreads(1, 1, 1)]
void ToRGB(uint3 id : SV_DispatchThreadID, uint3 _group_thread_id : SV_GroupThreadID) {
// 注意這是左下角
if (id.y % 2 == 0) {
if (id.x % 2 == 0) {
int X = id.x;
int Y = id.y;
// 不採用 Y 軸反轉了,Debug 累了,就直接從左下角算吧
float4 _B = outputTexture[float2(X, Y)];
float4 _Gr = outputTexture[float2(X, Y + 1)];
float4 _Gb = outputTexture[float2(X + 1, Y)];
float4 _R = outputTexture[float2(X + 1, Y + 1)];
float Gr = _Gr[1];
float B = _B[2];
float R = _R[0];
float Gb = _Gb[1];
float G = (Gr + Gb) / 2;
float4 color = float4(R, G, B, 1);
// 丟回 CPU :
//
// 理論上 RAW 轉 RGB 解析度會是原來的四分之一
// 但是這裡我的 RAW 與 RGB 是一樣大小,所以要補足另外三個空缺顏色
outputTexture2[float2(X, Y)] = color;
outputTexture2[float2(X, Y + 1)] = color;
outputTexture2[float2(X + 1, Y)] = color;
outputTexture2[float2(X + 1, Y + 1)] = color;
}
}
}