一、渲染到 RT
渲染纹理(Render Texture)
在 Unity 中最简单的操作(三步实现渲染到 RT,无需写代码):
这样这台摄像机的渲染结果就会存入你设置的 RT 中,而不再显示在屏幕上
这个设置转成代码就是:
_renderTexture = new RenderTexture(width, height, 8);
_camera.targetTexture = _renderTexture;
二、FBO 与渲染到 RT 的原理
1):纹理从 CPU 到 GPU:
所有的贴图资源最开始都是存在 CPU 的内存中的,在渲染的过程中它会被送到 GPU 的内存里,在这之后 GPU 才能使用它进行渲染
2):再提 FBO
关于 FBO 本质上就是 GPU 渲染结果的目的地,我们绘制的所有结果:包括颜色、深度等信息都最终存在这个这里
现在的 GPU 当然可以创建多个 FBO,其中有个默认的 FBO 直接连着我们的显示器窗口区域,而其它的 FBO 存在的目的就是允许我们将渲染结果保存在 GPU 的一块存储区域以待之后使用
3):FBO 里面的内容,怎么用?
- 通过 ReadPixels() 方法将 FBO 里面的内容拷回 CPU,这可以用来实现经典的截屏操作
- 将 FBO 里面的内容拷贝到一张 GPU 上的 Texture 中
- 省去拷贝流程,直接将这个 FBO 关联到对应的 Texture,这样就等于在绘制时就直接绘制到这个 Texture 上,这即是 Unity RT 的实现原理
一个很经典的函数是 Graphics.Blit(src, target, mat, pass):其内部使用 mat 材质用 src 做 mainTex,clear 为 black 后渲染到 target 上,mat 留空可以理解为直接拷贝纹理,一般都是使用这种方法渲染到 RT,在做一些屏幕后处理时这个方法几乎是必用到的
三、实现截屏功能
按下 S 键就可以将当前的游戏画面保存到 D 盘的根目录下:
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class ScreenShot : MonoBehaviour
{
private void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
Debug.Log("Save");
Save();
}
}
private RenderTexture TargetTexture;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
TargetTexture = source;
Graphics.Blit(source, destination);
}
private void Save()
{
RenderTexture.active = TargetTexture;
int width = RenderTexture.active.width;
int height = RenderTexture.active.height;
//↑ 把当前的fbo设为可读的对象 ↓ 调用ReadPixels操作将其读回内存
Texture2D png = new Texture2D(width, height, TextureFormat.ARGB32, true);
png.ReadPixels(new Rect(0, 0, width, height), 0, 0);
byte[] bytes = png.EncodeToPNG();
string file = string.Format(@"D:\TestPic.png");
System.IO.File.WriteAllBytes(file, bytes);
Debug.Log("Save Down");
}
}
关于 Unity 内置的 OnRenderImage() 函数:
该脚本必须挂载在有相机组件的游戏对象上,该函数在所有的渲染完成后由 monobehavior 自动调用,其允许我们使用着色器滤波操作来修改最终的图像,第一个参数 source 为输入原图像,第二个参数 desitination 为输出的图象
还需要注意的点:
若要将 RenderTexture 拷贝回 CPU 内存,那么拷贝前后的 Tex 的格式必须匹配,且必须是 RGBA32 这种基本类型,要注意的是很多机器支持 ARRBHALF 或者 ARGBFLOAT 这样的浮点格式来存储其它非颜色信息(例如 uv 坐标),这种不能直接拿来当 256 位颜色信息使用
参考文章: