Unity 使用Blit生成图片踩的坑

Unity 使用Blit生成图片踩的坑

引言

  在项目中经常需要把shader处理过的纹理保存成图片,如果直接获取材质的源纹理并保存,保存的是未经shader处理的原始纹理。有一种方法是使用Unity中的Blit函数读出经shader处理后的纹理,再保存成图片。但在Blit的使用中,保存的图片常常是黑色(像素是(0,0,0,255)),做了很多尝试,最后成功保存了图片,为此将Blit踩的坑记录在此。

内容

Blit函数的基本逻辑

  Blit函数的一般用法是Blit(src, dst, mat),给定原纹理、目标纹理和材质,将结果渲染到目标纹理上,然后将RenderTexture类型的目标纹理保存成图片(如png格式,本文最后保存成png图片)。

Blit函数用法实例

  • 错误1

  问题:材质使用的shader中使用了矩形裁剪,会导致保存图片是黑色。Blit不会设置顶点坐标,因此会导致片元全部被裁剪掉。手动给shader中的_ClipRect传递参数new Vector4(0f, 0f, 1f, 1f),不会影响原本的UI矩形遮罩效果,保存图片正常。

public RenderTexture GetColorImgTex(int size)
{RenderTexture tempRT = new RenderTexture(size, size, 0, RenderTextureFormat.ARGB32);tempRT.Create();this.colorImage.material.SetTexture("_MainTex", MaskGenerator.Instance.maskExpandTex); Vector4 uvRect = new Vector4(0f, 0f, 1f, 1f);           // 防止裁剪问题this.colorImage.material.SetVector("_ClipRect", uvRect);//Graphics.Blit(MaskGenerator.Instance.maskExpandTex, tempRT, this.colorImage.material);      Graphics.Blit(null, tempRT, this.colorImage.material);return tempRT;
}
  • 错误2

  this.colorImage.material材质原本有我需要的源纹理,按照一般的逻辑,Blit中的src可以置为null,但这样也会生成黑色的图片,解决方式是需要设置原纹理,两种方式都有效:给材质设置源纹理,或者在Blit中设置源纹理。

this.colorImage.material.SetTexture("_MainTex", MaskGenerator.Instance.maskExpandTex); 
//或者
Graphics.Blit(MaskGenerator.Instance.maskExpandTex, tempRT, this.colorImage.material);  
  • 错误3

  定义的RenderTextureARGB格式需要和保存图片定义的纹理定义的格式一致,不一致也会导致生成图片是黑色。这里全部设置成ARGB32就能正确保存图片了。

public RenderTexture BlendTexture(RenderTexture background, int size)
{Texture2D foreground = MaskGenerator.Instance.previewTex;RenderTexture rt = new RenderTexture(size, size, 0, RenderTextureFormat.ARGB32);rt.Create();Material blendMat = Resources.Load<Material>("Material/Blend");         // 透明度混合blendMat.SetTexture("_MainTex", foreground);blendMat.SetTexture("_BgTex", background);Graphics.Blit(null, rt, blendMat);return rt;
}
  • 其他无效尝试

  网上有说法是在Blit时,设置RenderTexture.active,试过无效,应该是Blit时就已经将dst参数纹理设置成目标了,无需使用RenderTexture.active = rt;
RenderTexture保存成png格式图片的代码,需要在下面代码中的texture定义时,设置纹理编码格式与上文目标纹理一致。

public void SaveRenderTexToPNG(RenderTexture renderTexture, string filePath)
{if (renderTexture == null){Debug.LogError("RenderTexture为空,无法保存");return;}//注意纹理编码格式Texture2D texture = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);      RenderTexture previousActiveRT = RenderTexture.active;          // 保存当前活动的RenderTextureRenderTexture.active = renderTexture;                           // 设置传入的RenderTexture为活动状态,以便读取其内容texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);texture.Apply();RenderTexture.active = previousActiveRT;                        // 恢复之前的活动RenderTexture// 编码为PNG并保存byte[] bytes = texture.EncodeToPNG();//testRawImage2.texture = texture;System.IO.File.WriteAllBytes(filePath, bytes);Debug.Log($"RenderTexture已保存至: {filePath}");// 清理临时Texture2DGameObject.Destroy(texture);
}

小结

  在使用Blit函数中不会报错,但也得不到最终想要的结果,常常输出黑色图片。在使用Blit时需要注意的细节还很多。