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

本文介绍了一种在Unity中将RenderTexture转换为PNG的方法,解决了图像边缘出现黑色的问题。通过调整透明度并还原RGBA值,确保了图像质量。代码示例展示了如何实现这一过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于使用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: zhiheng.rick@gmail.com
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;
    }
}
Unity 中实现描边(Outline)效果,Blit 是一个常用的手段,通常通过后期处理(Post-Processing)技术来完成。以下是使用 Blit 实现描边效果的详细步骤和方法。 ### 基本原理 描边效果通常是通过检测对象边缘将其渲染为特定颜色来实现的。常见的做法是利用深度缓冲或法线信息,在屏幕空间中进行边缘检测[^1]。这种方法可以有效地突出物体轮廓,同时避免对场景中的每个对象进行额外计算。 ### 实现步骤 #### 1. 创建 Render Texture 首先需要创建一个 `RenderTexture` 来存储对象的边缘信息。可以通过 Unity 的 `RenderTexture` 类动态创建,也可以在 Inspector 中手动配置。 ```csharp public class OutlineEffect : MonoBehaviour { private RenderTexture outlineRT; void Start() { outlineRT = new RenderTexture(Screen.width, Screen.height, 24); GetComponent<Camera>().targetTexture = outlineRT; } } ``` #### 2. 编写 Shader 进行边缘检测 接下来编写一个简单的边缘检测 Shader,用于提取对象的轮廓。以下是一个基于 Sobel 算子的边缘检测示例: ```hlsl Shader "Custom/EdgeDetection" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Pass { CGPROGRAM #pragma vertex vert_img #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; float4 _MainTex_TexelSize; fixed4 frag (v2f_img i) : SV_Target { float2 uv = i.uv; float3 dx = tex2D(_MainTex, uv + float2(_MainTex_TexelSize.x, 0)).rgb - tex2D(_MainTex, uv - float2(_MainTex_TexelSize.x, 0)).rgb; float3 dy = tex2D(_MainTex, uv + float2(0, _MainTex_TexelSize.y)).rgb - tex2D(_MainTex, uv - float2(0, _MainTex_TexelSize.y)).rgb; float edge = length(dx) + length(dy); return fixed4(edge, edge, edge, 1); } ENDCG } } } ``` #### 3. 使用 Blit 渲染到屏幕 最后通过 `Graphics.Blit` 方法将边缘检测结果渲染到屏幕上。可以在 C# 脚本中调用 `Graphics.Blit`,将上一步生成的 `RenderTexture` 作为输入源。 ```csharp public Material outlineMaterial; void OnRenderImage(RenderTexture source, RenderTexture destination) { // 应用边缘检测材质 Graphics.Blit(source, destination, outlineMaterial); } ``` ### 性能优化建议 由于 `OnRenderImage` 和 `Blit` 操作可能会导致性能问题,特别是在移动设备上开启 MSAA 时,因此需要注意以下几点: - 尽量减少后处理效果的数量,避免多个 `OnRenderImage` 调用叠加。 - 可以尝试使用 ComputeShader 或其他更高效的 GPU 计算方式替代传统的 `Blit` 操作。 - 如果目标平台支持,可以考虑使用 Unity 的 Universal Render Pipeline (URP) 或 High Definition Render Pipeline (HDRP),它们提供了更高效的后处理系统[^3]。 ### 总结 通过上述步骤,可以在 Unity使用 Blit 技术实现基本的描边效果。这种技术不仅适用于 Left 4 Dead 风格的轮廓高亮,还可以扩展到更多基于屏幕空间的后处理效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值