关于使用Render Texture保存png的资料很多,Unity所提供的文档中也有提及。思路是将将摄像机渲染的纹理渲染到Render Texture上去,然后生成texture2D,获取texture2D的字节信息,将字节流保存到文件中。看着似乎没什么大问题,不过需要注意的是,渲染到Render Texture中到图像有一层明显到黑边:(需附图),原因自然是在渲染我们需要到图像到时候,我们并不关心到背景也被渲染进去了。
为了方便起见,我们将Camera对背景设为黑色对Solid Color。
了解渲染过程到话,应该知道背景是在最后被渲染进去到,图像外层的颜色没有被保存进文件里,这是因为对颜色为黑色对像素进行了剔除,而图像对边缘因为图像本身有透明度对缘故,进行了混合。在这里设图像渲染后的RGBA值为(r1, g1, b1, a1),背景渲染在最后,其颜色为(0, 0, 0, 0),那么最终混合得到对RGBA值为(r1 * a1, g1 * a1, b1 * a1, a1),也就是我们在场景中看到的颜色,即Render Texture中的颜色。拿到这个(r1 * a1, g1 * a1, b1 * a1, a1),我们就可以根据RGB值和alpha值,还原出刚渲染完目标图像的RGBA值。
下面的代码将Render Texture中的颜色还原,保存为png,并设置保存的png图片的TextureImportSetting:
//Author: Shaozhiheng
//EmaiL: [email protected]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System;
public class RenderTextureToPNG : EditorWindow
{
private int currentWaitFrames = int.MaxValue;
private const int WAIT_FRAMES = 10;
private RenderTexture rt;
private bool canSave = true;
private string fileName = null;
//将RenderTexture保存成一张png图片
[MenuItem("Custom/RenerTextureToPNG")]
public static void Execute()
{
Rect wr = new Rect(0, 0, 500, 500);
RenderTextureToPNG window = (RenderTextureToPNG)EditorWindow.GetWindowWithRect(typeof(RenderTextureToPNG), wr, true, "RenderTexure to PNG");
window.Show();
}
void Update() {
if (++currentWaitFrames == WAIT_FRAMES) {
canSave = true;
SetImportSettings();
this.ShowNotification(new GUIContent("Save Complete"));
}
}
void OnGUI()
{
EditorGUILayout.LabelField("RenderTexure");
rt = EditorGUILayout.ObjectField(rt, typeof(RenderTexture), true) as RenderTexture;
if (canSave && rt != null)
{
if (GUILayout.Button("SaveToPNG", GUILayout.Width(200)))
{
canSave = false;
currentWaitFrames = 0;
SaveRenderTextureToPNG(rt);
EditorApplication.update += Update;
}
}
}
void SetImportSettings() {
if (fileName != null)
{
AssetDatabase.Refresh();
string filePath = "Assets/ExportPNG/" + fileName;
Debug.Log(filePath);
TextureImporter textureImporter = AssetImporter.GetAtPath(filePath) as TextureImporter;
textureImporter.textureType = TextureImporterType.Sprite;
AssetDatabase.ImportAsset(filePath);
}
EditorApplication.update -= Update;
}
void SaveRenderTextureToPNG(RenderTexture rt)
{
RenderTexture prev = RenderTexture.active;
RenderTexture.active = rt;
//Shader shader = Shader.Find("Custom/Anti Premultiply Alpha");
//Material material = new Material(shader);
//Graphics.Blit(rt, rt, material);
Texture2D png = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false);
png.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
Color[] colors = png.GetPixels();
List<Color> newColors = new List<Color>();
foreach (Color color in colors)
{
newColors.Add(new Color(color.r / color.a, color.g / color.a, color.b / color.a, color.a));
}
png.SetPixels(newColors.ToArray());
byte[] bytes = png.EncodeToPNG();
string directoryPath = Path.Combine(Application.dataPath, "ExportPNG");
if (!Directory.Exists(directoryPath))
Directory.CreateDirectory(directoryPath);
fileName = GetTimeStamp().ToString() + ".png";
string filePath = directoryPath + "/" + fileName;
FileStream file = File.Open(filePath, FileMode.Create);
BinaryWriter writer = new BinaryWriter(file);
writer.Write(bytes);
file.Close();
Texture2D.DestroyImmediate(png);
png = null;
RenderTexture.active = prev;
}
private static long GetTimeStamp(bool bflag = true)
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
long ret;
if (bflag)
ret = Convert.ToInt64(ts.TotalSeconds);
else
ret = Convert.ToInt64(ts.TotalMilliseconds);
return ret;
}
}