[Unity] 使用Render Texture截图并消除描边

关于使用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;
    }
}

猜你喜欢

转载自blog.csdn.net/rickshaozhiheng/article/details/78659984