【Unity】使用RenderTexture为物体生成快照

版权声明:本文为博主原创文章,未经博主允许不得转载。

作者:Jimm          邮箱:[email protected]



RenderTexture的定义和作用

RenderTexture are textures that can be rendered to.

RenderTexture(下文简称RTT)是可以被渲染的纹理,简称渲染纹理。一般来说,RTT可以应用在制作动态阴影,反射以及监视摄像机(车辆后视镜)等,另一方面可以应用到游戏截图,背景模糊等方面,用途十分广泛。以后这些技术都会慢慢分享到博客上,敬请期待!



RTT的用法

Camera(摄像机)是Unity中非常重要的一个组件,其中有一个属性叫做targetTexture,在设置了targetTexture后,Camera会在渲染时将其屏幕上的图像渲染到targetTexture上。在相机渲染完成后可以读取屏幕像素内的缓存来使用。其中,相机渲染完成有三种调用方式:

1.OnPostRender()

OnPostRender is called after a camera finished rendering the scene.

OnPostRender在相机完成渲染场景时调用。这次遇到的需求是需要为物体生成快照,做法是另外创建一个相机,在另一个位置完成渲染工作,代码如下:

//快照相机
public Camera shotCam;
public UITexture texture;
void OnPostRender()
{
    //设定当前RenderTexture为快照相机的targetTexture
    RenderTexture rt = shotCam.targetTexture;
    RenderTexture.active = rt;
    Texture2D tex = new Texture2D(rt.width, rt.height);
    //读取缓冲区像素信息
    tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
    tex.Apply();
    texture.mainTexture = tex;
Texture2D.Destroy(tex);    tex = null;
}

场景中的效果如下:

2.使用协程(yield return new WaitForEndOfFrame())

yield return new WaitForEndOfFrame()

等待当前帧结束。类似于OnPostRender(),可以使用for循环来依次对多个物体进行快照,代码如下:

public GameObject[] gos;
void Start()
{
    StartCoroutine(RenderGoTexCR());
}
IEnumerator RenderGoTexCR()
{
    int length = textures.Length;
    for (int i = 0; i < length; i++)
    {
        GameObject go = Instantiate(gos[i]);
        go.SetActive(true);
        yield return new WaitForEndOfFrame();
        RenderTexture rt = shotCam.targetTexture;
        RenderTexture.active = rt;
        Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
        tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        tex.Apply();
        textures[i].mainTexture = tex;
        GameObject.Destroy(go);
    }
}

PS:yield语句要放在设置RenderTexture.active之前,因为只有在帧结束时shotCam的targetTexture才被正确渲染,才可以通过ReadPixels取得正确的图像。tex在使用过后最好使用Destroy()销毁,或者在设置mainTexture之前销毁之前的Texture,否则就会像博主一样写着写着博客发现Unity运行了一段时间就把内存吃光了

场景效果如下:

3.使用Camera.Render()

我们发现当要对多个物体进行快照时OnPostRender()就没那么好用了,因为我们需要在相机渲染前将物体展示出来(OnPreRender()),在相机渲染后取得像素信息,对于两个函数分别处理GameObject我们是拒绝的(程序员就喜欢简单粗暴!)。那么使用协程又有什么问题呢?细心的同学会发现,每次渲染一个物体都需要等到帧结束,那么当需要渲染的物体较多时渲染时间会明显变长,这显然也不是我们想要的,那么有没有能在很短时间内完成大量物体的渲染呢?Camera.Render()给了我们福音。

Camera.Render()

手动渲染相机。废话不多说,贴代码:

public GameObject[] gos;
void Start()
{
    for (int i = 0; i < textures.Length; i++)
    {
        GameObject go = Instantiate(gos[i]);
        go.SetActive(true);
        textures[i].mainTexture = RenderGoTex();
        GameObject.Destroy(go);
    }
}
Texture2D RenderGoTex()
{
    RenderTexture rt = shotCam.targetTexture;
    shotCam.Render();
    RenderTexture.active = rt;
    Debug.Log(RenderTexture.active);
    Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
    tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
    tex.Apply();
    return tex;
}

Camera.Render()无需等待帧结束,它在调用时强制渲染相机,通过返回的tex进行操作即可,同样不要忘了Texture的内存问题,场景中效果如下:

咦,怎么会发生重叠现象呢,明明在取得tex之后调用了Destroy()函数呀!这个也困扰了我一段时间,后来发现是Destroy函数的延迟问题。Destroy()函数对实际物体的销毁会延迟到当前循环更新后,在渲染前完成的,所以我们在这一帧执行for循环时,虽然每次循环都调用了Destroy()来销毁物体,但是实际上它们是在for循环结束后才一起销毁的,所以为了避免此问题,我们改用DestroyImmediate(),或者先调用gameObject.SetActive(false)后再Destroy(),后者是比较推荐的做法,原因请看官方文档

https://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html



结尾语

博主只是初入江湖的小菜,最近萌生写博客的想法,希望能将自己在学习和工作中遇到的问题以及所感所想与大家分享,同时也是对自我的总结。最后感谢大家的支持,你们的支持就是我的动力!



【Unity】使用RenderTexture为物体生成快照

时间: 2024-10-08 09:47:01

【Unity】使用RenderTexture为物体生成快照的相关文章

unity 鼠标拖拽物体实现任意角度自旋转

主要涉及函数 Input.GetAxis(“Mouse x”) 可取得鼠标横向(x轴)移动增量 Input.GetAxis(“Mouse y”) 可取得鼠标竖向(y轴)移动增量 通过勾股定理获取拖拽长度,长度越长旋转越快 在project setting--Input 可以设置 直接上代码,看了就明白了 1 using UnityEngine; 2 using System.Collections; 3 4 public class startRoate : MonoBehaviour 5 {

从ISO创建实例、实例生成快照,创建镜像(基于openstack kilo平台、KVM虚拟化)

ISO创建实例从ISO创建实例有两个关键配置:云主机类型flavor和ISO镜像参数.云主机类型:openstack使用ISO镜像创建虚拟机实例时,会将选择的云主机类型flavor中的根磁盘(Disk)设置为cdrom,作为ISO的启动的光驱设备,用于OS的安装源:临时磁盘(OS-FLV-EXT-DATA:ephemeral)作为虚拟机实例的系统盘,作为安装目标.因此在配置用于ISO启动的云主机类型flavor时,需要设置根磁盘(disk)值,可以略大约ISO镜像文件,5G/10G均可,可根据现

【Unity笔记】UGUI物体的渲染顺序

①不同Camera的Depth.(大在前,小在后)②同Camera的SortingLayer.(下在前,上在后)③同SortingLayer下的Order in Layer.(大在前,小在后)④同Order in Layer下的Z轴.(小在前,大在后) 注意: 如果是多个Canvas的渲染先后顺序 http://blog.csdn.net/huutu/article/details/43636241调Canvas下面有一个Sort Order值,默认为0,越大越在后面. 创建任意UGUI元素时自

unity如何在安卓端生成日志信息

游戏发布到安卓端后会崩溃,在unity编译器运行是正常的.可以通过这个类进行捕获log. 在你想打log的地方进行appdebug.logError然后在生成的日志文件内就可以看到对应的log信息了.

[Unity Shader]光照模型对物体的假设

什么是光照模型 光照模型就是模拟光在物体间的传递过程,以确保物体可见表面每一点的亮度和颜色. 当光照射到一个物体表面时,光可能被吸收.反射或折射.反射和折射的光使物体可见.如果入射光全部被吸收,物体将不可见,称物体为黑体. 一个物体表面呈现的颜色是有物体表面向视线方向辐射的光能中各种波长的分布所确定的. 如果物体是不透明的,则物体表面呈现的颜色仅有其反射光决定,通常把反射光考虑成环境反射光.漫反射光和镜面反射光三个分量的组合. 环境反射光(Ambient Light) 环境反射光是由于邻近物体所

Unity利用AnimationCurve做物体的各种运动

?之前一直都是自己学习Unity各种做Demo,最近开始正式使用Unity来做一个款2d的游戏. 其中在做一个类似小球弹跳运动的时候遇到了点问题,查找了很多资料,无意间发现AnimationCurve,顿时那种心情啊! 然后苦心钻研了一翻 抛砖引玉 的写了个Move2D的类主要是个大家一个思路. 不多说上正菜: 直线平移运动效果: 曲线上升运动效果: 曲线上升然后下降的弧线运动效果: 小球弹跳运动效果: 下面是C#代码,由于之前一直用Cocos2d-x所以有点cocos的风格: using Un

[Unity基础]RenderTexture

参考链接: https://www.cnblogs.com/Jimm/p/5951362.html 一.相关API 1.Texture2D.ReadPixels 从RenderTexture.active中复制像素,以左下角为原点. 2.MonoBehaviour.OnPostRender 当相机渲染完所有物体就会调用该方法,并且只有当这个脚本挂在相机时才会调用. 二.测试 新建一个场景,新建一个camera和一个go,把go设置为一个单独的层,让camera单独照这个层,主camera不要照这

Unity 5.3 将物体转向鼠标所在位置

一.需求描述: 初始情况—— 目标需求: 二.代码 1 void Update () { 2 // 获取鼠标位置相对移动向量 3 Vector2 translation = new Vector2(Input.GetAxis("Horizontal"),Input.GetAxis("Vertical")); 4 // 根据鼠标位置相对移动向量移动物体 5 transform.Translate(translation * speed * Time.deltaTime

关于Unity中鼠标选取物体的解决方案

今天修改了之前写的飞机大战的代码,原来的不足之处是点击屏幕的任意一点都可以移动飞机,也就是没有检测鼠标到底有没有点到飞机上. 我先是用之前的3D拾取技术,发现没有反应,才意识到我这个plane飞机节点挂载的是Box Collier2D的碰撞器组件,不是Box Collier,3D射线拾取技术在2D游戏里面还用不了. 后来我百度了一下,才知道用UGUI写的2D游戏用鼠标选取物体不用射线检测,要用事件系统.但是我不会用OnMouseEnter()和OnPointerEnter(),所以还是想用射线来