我做这个的目的是截取canvas上某元素的照片,不过普通的截图也能用,把renderTexture长宽设成屏幕长宽就行了

思路:把相机的画面渲染到一个指定纹理上,将纹理转码为图片格式存储在文件中。

代码如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;

public class SaveCanvasElementAsImage : MonoBehaviour
{
    Camera recordCamera;//一般不使用主相机
    string ImageSave;
    public RectTransform elementToCapture;
    
    // Start is called before the first frame update
    void Start()
    {
        recordCamera = GetComponent<Camera>();//我这里是直接挂摄像机所以这样写
        ImageSave = Path.Combine(Application.dataPath, "example.png");
        //recordCamera.enabled = false;//如果负责截图的不是主相机最好随用随开避免性能耗费
    }
    public void Capture()
    {
        //recordCamera.enabled = true;
        RenderTexture renderTexture = new RenderTexture((int)elementToCapture.rect.width, (int)elementToCapture.rect.height,24);
        recordCamera.targetTexture = renderTexture;//把相机画面渲染到指定纹理上
        recordCamera.Render();//手动让相机再渲染一次
        recordCamera.targetTexture = null;//恢复正常
        //recordCamera.enabled = false;

        SaveRenderTextureAsImage(renderTexture);
    }

    private void SaveRenderTextureAsImage(RenderTexture renderTexture)
    {
        Texture2D texture = new Texture2D(renderTexture.width, renderTexture.height);
        RenderTexture.active = renderTexture;//下一帧会自动切回默认值(屏幕)
        texture.ReadPixels(new Rect(0,0,renderTexture.width,renderTexture.height),0,0);//这里会直接读取RenderTexture.active
        texture.Apply();//立刻保存

        byte[] pngData = texture.EncodeToPNG();//转码
        File.WriteAllBytes(ImageSave, pngData);//写入文件
    }
}

写完了把它挂到Camera上,把想截的元素尺寸挂上去。

unity UI mask 截图_游戏引擎

在按钮上使用capture方法

unity UI mask 截图_unity_02

运行后点击按钮就可以截到指定ui啦

unity UI mask 截图_学习_03


注意事项:(截整张图的不用看)

1、这段代码的本质是从相机渲染好的画面里截出一个符合ui元素尺寸大小(这就是传入RectTransform的作用)的中心矩形,而不会去自动寻找ui元素位置对它截图,因此为了便于控制精确截图,被截图的ui元素需要放在canvas中心,并将canvas的RenderMode设置为Screen space-camera,canvas scaler设为Constant Pixel Size

unity UI mask 截图_unity UI mask 截图_04

2、代码中这一行可以理解为是设置截图尺寸的

RenderTexture renderTexture = new RenderTexture((int)elementToCapture.rect.width, (int)elementToCapture.rect.height,24);

要截ui元素的朋友们注意,rect类的width和height是不会自动乘以scale的。如果在设置ui元素时进行了缩放,需要手动乘上缩放值,不然就会是这样的效果

unity UI mask 截图_unity_05

在代码中手动乘上0.5除以2后(renderTexture只能是整数长宽不能有浮点数)

unity UI mask 截图_unity_06


做到这已经满足我项目的功能需求了,但是我还想做得更精确一些,比如能自动找到非居中的ui位置并把它存下来,记一下优化思路有机会扩充

思路:renderTexture获取整块屏幕的纹理,通过texture读取像素时的起始点及长宽来精准截取。

texture.ReadPixels(new Rect(0,0,renderTexture.width,renderTexture.height),0,0);//这里会直接读取RenderTexture.active
  • new Rect(0, 0, renderTexture.width, renderTexture.height): 这个参数定义了一个矩形区域,指定了要从渲染纹理中读取像素的区域。这个矩形的左下角坐标是 (0, 0),宽度和高度分别为渲染纹理的宽度和高度,这样就覆盖了整个渲染纹理的区域。
  • (0, 0): 这是指定了要将读取到的像素数据存储到目标 Texture2D 中的起始坐标。这表示像素数据将从目标纹理的左下角开始存储。

所以只需将elementToCapture的width和height以及左下角坐标弄出来就可以了。

Ps.UI元素的rect设定很复杂,具体可以查阅这篇

贴一些有助于理解代码的来源GPT的(可能有它胡说八道的部分)

纹理部分:

在计算机图形学中,纹理(Texture)是一种图像或图案,用于覆盖(或映射)在三维模型的表面上,以模拟物体的外观和材质。纹理可以为物体的表面增加细节、颜色、图案、反射、折射等效果,从而使物体在渲染时更加逼真。

在游戏开发和计算机图形领域中,纹理通常用于以下几个方面:

  1. 颜色纹理: 这是最常见的类型,用于给模型的表面上色。可以包含物体的外观、颜色、图案等信息。
  2. 法线纹理: 法线贴图(Normal Map)是一种特殊的纹理,用于模拟物体表面的凹凸细节,以增加光照效果。
  3. 位移纹理: 位移贴图(Displacement Map)也用于模拟物体表面的凹凸,但会直接影响几何形状,而不仅仅是表面细节。
  4. 高光纹理: 高光贴图(Specular Map)用于控制物体的高光区域,影响光照反射的强度和位置。
  5. 环境贴图: 环境贴图(Environment Map)用于模拟物体周围环境的反射和折射,用于增加物体的真实感。
  6. 遮罩纹理: 遮罩贴图(Mask Map)用于在一个纹理中储存多个信息,例如颜色、金属度、粗糙度等。

纹理可以由图像编辑软件创建,如Photoshop、GIMP等,也可以通过摄影或扫描等方式获得。在游戏引擎中,纹理被应用到模型的表面上,以展现出模型更丰富的外观和质感。

同时可以将纹理理解为一种特殊的图像格式,但它们在计算机图形学中的作用更加广泛。它们不仅仅包含颜色信息,还可以包括其他有关材质、表面属性和效果的信息。纹理与普通图像在格式上没有本质上的不同,但它们在用途上有很大的差异。纹理是计算机图形学中的一个重要概念,用于增强模型的外观、质感和真实感。

RenderTexture(渲染纹理)是Unity中的一个概念,它是一种特殊类型的纹理,用于将场景或相机的渲染输出捕捉到纹理中,而不是直接显示在屏幕上。RenderTexture 可以用于各种目的,例如实现屏幕后处理效果、渲染到纹理以便后续使用等。

RenderTexture 允许你在渲染过程中将图像数据写入到一个纹理中,然后你可以将这个纹理用于各种用途,比如将一个相机的渲染结果用作另一个相机的纹理,或者将渲染结果保存为图片文件。

在Unity中,相机(Camera)的 "Target Texture" 是一个用于指定相机渲染输出的纹理。当你设置相机的 Target Texture 属性时,相机将会将其渲染结果绘制到这个指定的纹理上,而不是直接显示在屏幕上。

这个功能的主要用途之一是实现屏幕后处理效果。例如,你可以创建一个 RenderTexture(渲染纹理),将相机的渲染结果渲染到这个纹理上,然后再将这个纹理应用于一个全屏的 Quad(平面)上,通过自定义的着色器来实现各种后处理效果,如模糊、扭曲、颜色滤镜等。

使用 Target Texture 还可以实现其他一些功能,比如在运行时捕获相机的渲染结果,将渲染结果用于实时的反射或纹理投影等。

要注意的是,设置了 Target Texture 后,相机将不会在屏幕上直接显示渲染结果,而是将渲染结果写入到指定的纹理中。这通常在需要对渲染结果进行后处理或者捕获时非常有用。

 RenderTexture.active 是一个静态属性,它在Unity中用于设置当前的活动渲染纹理(Active RenderTexture)。这个属性允许你指定将渲染输出绘制到哪个渲染纹理上。

当你在代码中设置了 RenderTexture.active 之后,后续的渲染操作会将渲染结果绘制到你指定的渲染纹理上。然而,在渲染管线继续进行下一帧的渲染时,Unity会自动将RenderTexture.active 重置回默认值,即屏幕的渲染目标。

这是因为在每一帧的渲染过程中,Unity会依次处理各个相机的渲染操作,绘制场景的各个部分。在处理完一个相机的渲染后,Unity会将渲染目标重置回屏幕,以准备处理下一个相机的渲染。

因此,即使你在一帧中设置了 RenderTexture.active,在下一帧的渲染过程中,它会被重置回默认值,以确保正常的渲染流程。

如果你希望在连续的帧中都使用相同的渲染目标,需要在每一帧中都重新设置 RenderTexture.active,以便告诉Unity将渲染结果绘制到你指定的渲染纹理上。

相机部分: 

默认情况下,Unity的相机会在每一帧中自动进行渲染,以生成场景的画面。这是自动进行的,无需手动调用 Render 方法。然而,当需要在不同的上下文中进行渲染,比如将渲染结果绘制到渲染纹理中,就需要手动触发相机的渲染。

 在Unity中,相机的渲染结果通常需要先渲染到一个渲染纹理(RenderTexture),然后再将渲染纹理的内容复制到一个普通的纹理(Texture2D)中。这是因为渲染结果通常在显存中,并不直接可用于CPU操作,所以需要通过渲染纹理来进行转换和处理。

直接将相机的渲染结果输出到一个普通的纹理(Texture2D)是不太常见的做法,因为这涉及到将显存中的图像数据复制到CPU内存中,而这样的操作会带来性能开销。