最近处理大量的图片缩小放大处理,之前用的是.net的Bitmap,加载到unity用c#加指针来缩放图像,发现效率不高,流程复杂,产生GC严重。然而想到了computeShader并发计算的强大力量,用来处理图像有天然优势,遂用computeShader来搞,开始埋头苦干。果然效果惊人。目前缺陷是只适合Win平台,移动平台没测试过。理论上讲只要支持ComputeShader就没问题
2021-7-10无意间发现了网友的一个新的缩放贴图的办法unity 生成缩略图 , 图片缩放 - 伊景轩 - 博客园
角度有点刁钻,但肯定的是比我的好用,简单且移动平台也可以用,性能还高,主要还是利用了GPU来处理。下面的内容就不要看了,强烈推荐这个
图像缩放我参考的是这篇文章的第一个方法:c#数字图像处理(十)图像缩放 - 微光-倾城 - 博客园
上代码:
首先是c#:
using UnityEngine;
using UnityEngine.UI;
public class ScaleImage : MonoBehaviour
{
public RawImage SrcRawImage;
public RawImage DstRawImage;
/// <summary>
/// 处理前的图像
/// </summary>
public Texture2D SourceTexture2D;
public ComputeShader ScaleImageComputeShader;
/// <summary>
/// 源图缩放至目标图的宽度倍数
/// </summary>
public float WidthScale = 0.5f;
/// <summary>
/// 源图缩放至目标图的高度倍数
/// </summary>
public float HeightScale = 0.5f;
// Use this for initialization
void Start ()
{
Standardization();
}
private void Standardization()
{
bool isStand = !(WidthScale <= 0f || HeightScale <= 0f);
if (ScaleImageComputeShader == null || SourceTexture2D == null || DstRawImage == null || SrcRawImage == null)
isStand = false;
if (!isStand)
{
throw new UnityException("变量数据不符合规范");
}
}
// Update is called once per frame
void Update () {
}
public void ScaleImageUserRt()
{
RenderTexture rtDes = new RenderTexture((int)(SourceTexture2D.width * WidthScale), (int)(SourceTexture2D.height * HeightScale), 24);
rtDes.enableRandomWrite = true;
rtDes.Create();
// Compute Shader
//1 找到compute shader中所要使用的KernelID
int k = ScaleImageComputeShader.FindKernel("CSMain");
ScaleImageComputeShader.SetTexture(k, "Source", SourceTexture2D);
ScaleImageComputeShader.SetTexture(k, "Dst", rtDes);
ScaleImageComputeShader.SetFloat( "widthScale", WidthScale);
ScaleImageComputeShader.SetFloat( "heightScale", HeightScale);
//3 运行shader 参数1=kid 参数2=线程组在x维度的数量 参数3=线程组在y维度的数量 参数4=线程组在z维度的数量
ScaleImageComputeShader.Dispatch(k, (int)(SourceTexture2D.width * WidthScale), (int)(SourceTexture2D.height * HeightScale), 1);
//cumputeShader gpu那边已经计算完毕。rtDes是gpu计算后的结果
SrcRawImage.texture = SourceTexture2D;
DstRawImage.texture = rtDes;
DstRawImage.SetNativeSize();
//后续操作,把reDes转为Texture2D
//删掉rtDes,SourceTexture2D,我们就得到了所要的目标,并且不产生内存垃圾
}
private void OnGUI()
{
if (GUI.Button(new Rect(0f, 0f, 100f, 100f), "test"))
{
ScaleImageUserRt();
}
}
}
CompteuShader代码:
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain
// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> Dst;
Texture2D Source;
float widthScale;
float heightScale;
//numthreads(1,1,1)里面的三个参数和 c#那边 ComputeShader.Dispatch(int kernelIndex, int threadGroupsX, int threadGroupsY, int threadGroupsZ);理解透了,就明白其中的运作机制了
//三句代码搞定缩放,简直优雅漂亮
[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
uint x = ceil(id.x/widthScale);
uint y = ceil(id.y/heightScale);
Dst[id.xy]= Source[uint2(x,y)];
}