Unity OnRenderImage

关于这个话题,网上的资料真的是太少了,主要都是关于在OnRenderImage实现后处理的一些基本的操作。
但是对于其原理方面讲解的很少,可能外网我还没搜到,后面如果找到的话,会再补充更新。

网上的资料:https://gameinstitute.qq.com/community/detail/112744
可以参考,但主要还是看我这篇的实验分析即可。

本文可能有点啰嗦,主要还是多种情况考虑的结果分析。

第一部分:

1、准备一个测试脚本:TestRenderTexture.cs
将其挂在主摄像机上即可,此时脚本里面的内容为空,后面会测试多种情况。
此时在什么都没做的情况下,只是在主摄像机上挂了一个TestRenderTexture脚本,此时我们让摄像机看到一个球体,如下:
在这里插入图片描述
此时打开帧调试器:
在这里插入图片描述
此时,一切都和我们之前一样,没有任何的变化。这里我们不做任何的总结,因为实验还未开始展开,总结是没有任何根据的。ok,我们继续:

2、在TestRenderTexture.cs中,做如下的操作:
2.1)在脚本中重写OnRenderImage方法,看其变化

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        // 不做任何操作
    }
}

此时屏幕黑了:
在这里插入图片描述
帧调试器:
在这里插入图片描述
此时有点变化,屏幕黑色这是其一,第二个,摄像机的画面,画到一个临时TempBuffer中去了。
断点情况:
在这里插入图片描述
source为那个临时缓冲区,destination为null。
我们继续。

3、在OnRenderImage方法中,使用Blit方法:
代码变为:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }
}

此时结果:
在这里插入图片描述
帧调试器:
在这里插入图片描述
断点情况:
在这里插入图片描述
此时屏幕能看到东西了,原因是我们在OnRenderImage方法中执行了Blit操作。
这blit我们看看,source为临时缓冲,destination为null,也就是将临时缓冲,拷贝到null,我们知道destination为空,则是直接拷贝到屏幕。所以我们看到了屏幕有东西。同时主要到帧调试器中,多了一个ImageEffects的绘制调用,使用内置的BlitCopy着色器,将临时缓冲拷贝到了屏幕上去。
在这里插入图片描述

4、创建要给rt,在start方法中赋值给摄像机

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
        m_camera.targetTexture = rt;
    }
}

此时屏幕:
在这里插入图片描述
屏幕为黑色,并且摄像机的targetTexture为我们创建的rt了。
帧调试器:
在这里插入图片描述
此时,我们将东西绘制到了我们创建的rt上去了,所以屏幕为黑色。

5、在脚本中,添加OnRenderImage方法,但是只是空方法。
在这里插入图片描述
此时,屏幕依然为黑色,输出一个提示,说目标未画任何东西。
帧调试器:
在这里插入图片描述
此时多了后处理,ImageEffects调用。将ImageEffects Temp拷贝到了我们的RT。
此时我们看rt:
在这里插入图片描述
rt为黑色,没有任何的东西。
此时我们看断点:
在这里插入图片描述
source变了,变成我们rt,目标也变了,变成了ImageEffects Temp了。
我们回忆下,我们设置了摄像机的rt,我们重写OnRenderImage方法,但是在方法中没有做任何的操作。
此时,帧调试器有ImageEffects调用了,并且执行了BlitCopy,是将ImageEffects Temp拷贝到我们的RT。
这一步是unity自己帮我们做的。为什么拷贝到了我们自己RT呢,因为我们设置了Camera的targetTexture。

而为什么我们支持的rt上为黑色呢?可能有点绕,但是要屏住呼吸,听我说:
我们设置了rt给摄像机,我们重写了OnRenderImage方法,但是里面未做任何事情,此时unity为我们做了一个操作(帧调试器可以看到做了一个Dynamic Draw的操作,执行BlitCopy),就是将ImageEffects Temp里面的内容,拷贝到了我们的rt,ok,那么我们就问了,ImageEffects Temp里面内容是啥,也许你知道了答案。
对的,此时Image Effects Temp里面的内容为空,没画任何的东西,所以最终的rt也为黑色。u

6、接着5,我们在OnRenderImage方法中,添加blit操作:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
        m_camera.targetTexture = rt;
    }

    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }
}

此时屏幕:
在这里插入图片描述
屏幕依然为黑色,这是应当的,因为我们把东西画到rt,camera的targetTexture设置成了rt。

在这里插入图片描述
我们rt上,惊奇的出现了东西,不再是黑色。
我们再看帧调试器:
在这里插入图片描述
在这里插入图片描述
我们可以看到镇调试,多了一个blit操作,就是我们自己写的那个blit操作:

  public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }

我们再看下,断点情况:
在这里插入图片描述
这个就是对应了,我们的帧调试器中的:
在这里插入图片描述
我们再来回顾下:
我们设置了camera的rt,我们重写OnRenderImage方法,我们在此方法中,执行一个Blit(source, destination)的操作。
此时source为我们rt,destination为ImageEffets Temp。然后unity又默认的为我们将ImageEffects Temp拷贝到我们设置的rt上去。

其实5中的操作,rt上是有东西,但是unity最终将ImageEffets Temp拷贝到了rt,而我们没有对ImageEffets Temp做任何内容的填充。对比6,6多了一个将rt拷贝到ImageEffets Temp的操作。所以在6中,我们看到rt上有东西。

以上是关于在设置的rt的全部内容。并且是在start方法中设置rt的流程。

==========================================================================================

第二部分:

下面做的是,将rt移动到PreRender方法中去,看有什么变化:
1、代码如下:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
    }

    private void OnPreRender()
    {
        m_camera.targetTexture = rt;
    }

    private void OnPostRender()
    {
        m_camera.targetTexture = rt;
    }
}

此时:
在这里插入图片描述此时屏幕为黑色,因为我们将内容绘制到rt上去。rt的内容:
在这里插入图片描述
rt上有值。此时帧调试器:
在这里插入图片描述
目标是我们rt。

2、我们重写OnRenderImage方法
在这里插入图片描述
画面为黑色,帧调试器,从ImageEffects Temp拷贝到我们rt:
在这里插入图片描述
source为我们的rt,目标是ImageEffects Temp。
3、我们在OnRenderImage方法中执行blit:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
    }

    private void OnPreRender()
    {
        m_camera.targetTexture = rt;
    }

    private void OnPostRender()
    {
        m_camera.targetTexture = rt;
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }
}

在这里插入图片描述
此时断点:
在这里插入图片描述
可以看到这个和上面案例6是一样的。参考案例5和6。

=======================================================================

第三部分:

1、我们使用buffer方式,设置rt:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
        m_camera.SetTargetBuffers(rt.colorBuffer, rt.depthBuffer);
    }
}

在这里插入图片描述
屏幕黑色,rt有东西。

2、重写OnRenderImage方法:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
        m_camera.SetTargetBuffers(rt.colorBuffer, rt.depthBuffer);
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        
    }
}

在这里插入图片描述
屏幕黑色,rt有内容:
在这里插入图片描述
断点情况:
在这里插入图片描述
此时source为null,为什么是屏幕呢?因为我们设置了colorbuffer和depthbuffer,但未设置camera的targetTexture,所以是源头是null,也就是屏幕。但是此时colorbuffer中是有值的,正如上面看到的。那为什么不是黑色的呢?和第一部分的,设置了targetTexture,有什么不同呢?不同的地方看帧调试器,帧调试器,最后执行的是ImageEffects Temp拷贝的操作,拷贝到了哪里?拷贝到了屏幕,那为什么不是拷贝到了我们的colorbuffer呢?因为我们没有设置camera的targetTexture。所以最后是拷贝到了屏幕。

 m_camera.SetTargetBuffers(rt.colorBuffer, rt.depthBuffer);

设置,也就是改变了绘制目标。等同于设置camera的targetTexture。但是不用buffer等同于rt,所以是null,这点解释我都看不下去了,不知道怎么解释。以后再悟。

3、在OnRenderIamge方法中做blit:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
        m_camera.SetTargetBuffers(rt.colorBuffer, rt.depthBuffer);
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }
}

在这里插入图片描述
断点情况:
在这里插入图片描述
source为null,就是我们colorbuffer,又colorbufer拷贝到ImageEffects Temp。最后unity默认执行一个ImageEffects拷贝到目标,而目标此时没有设置camera的targetTexture所以是拷贝到了屏幕。所以这种做法,其实就是相当于做了以抓屏幕,抓到了colorbuffer中去,如帧调试器中看到的:
在这里插入图片描述
ok,到此第三部分结束。

有人还会问,如果将设置colorbuffer代码,移动到PreRender中呢?然后在OnPostRender中设置null,这个读者自行尝试下,不再给出具体分析。

本文的第四部分,将是讨论targetTexture和SetTargetBuffers两种方式能不能同时存在的问题。

第四部分:

1、创建两个rt,一个是以targetTexture方式设置到主相机,一个是以SetTargetBuffers方式设置主相机,看两个rt上有没有值:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt;
    public RenderTexture rt2;
    public Camera m_camera;

    public void Start()
    {
        rt = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt.name = "xxx";
        rt2 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt2.name = "yyy";
        m_camera.targetTexture = rt;
        m_camera.SetTargetBuffers(rt2.colorBuffer, rt2.depthBuffer);
    }
}

在这里插入图片描述
rt1是设置成摄像机的targetTexture,而rt2,设置成了buffer。并且rt1先。
在这里插入图片描述
可以看到rt2上有值,但是rt1上黑色。

2、我们把顺序调换下:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt1;
    public RenderTexture rt2;
    public Camera m_camera;

    public void Start()
    {
        rt1 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt1.name = "xxx";
        rt2 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt2.name = "yyy";
        m_camera.SetTargetBuffers(rt2.colorBuffer, rt2.depthBuffer);
        m_camera.targetTexture = rt1;
    }
}

在这里插入图片描述
我们看到rt1上有值。
也就是谁靠后,谁有值。

3、重写OnRenderImage方法:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt1;
    public RenderTexture rt2;
    public Camera m_camera;

    public void Start()
    {
        rt1 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt1.name = "xxx";
        rt2 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt2.name = "yyy";
        m_camera.SetTargetBuffers(rt2.colorBuffer, rt2.depthBuffer);
        m_camera.targetTexture = rt1;
    }

    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
    }
}

此时rt1,靠后,是有效值。
在这里插入图片描述
此时,没有做任何的blit,所以:rt1和rt2都是黑色。

在这里插入图片描述

4、在OnRenderIamge方法中,添加blit:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt1;
    public RenderTexture rt2;
    public Camera m_camera;

    public void Start()
    {
        rt1 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt1.name = "xxx";
        rt2 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt2.name = "yyy";
        m_camera.SetTargetBuffers(rt2.colorBuffer, rt2.depthBuffer);
        m_camera.targetTexture = rt1;
    }

    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }
}

在这里插入图片描述
rt1靠后,rt1是camera的targetTexture,并且经过blit操作,所以rt1上有值。断点情况:
在这里插入图片描述

5、调换rt1和rt2的顺序:

using UnityEngine;

public class TestRenderTexture : MonoBehaviour
{
    public RenderTexture rt1;
    public RenderTexture rt2;
    public Camera m_camera;

    public void Start()
    {
        rt1 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt1.name = "xxx";
        rt2 = new RenderTexture(m_camera.pixelWidth, m_camera.pixelHeight, 16); //16位的深度
        rt2.name = "yyy";
        m_camera.targetTexture = rt1;
        m_camera.SetTargetBuffers(rt2.colorBuffer, rt2.depthBuffer);
    }

    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }
}

在这里插入图片描述
在这里插入图片描述
rt2有值:
在这里插入图片描述

也即是说,targetTexture和SetTargetBuffers两者不能共存,谁靠后,就按照谁的规则。

并且这句代码:
在这里插入图片描述
没有什么用,Inspector面板上也没有指定的targetTexture,因为rt1的指定在SetTargetBuffers之前,被冲掉了。

那么问题来了,能不能用同一个rt的颜色缓冲和深度缓冲呢?我暂时认为是不行的,因为unity怎么知道,将深度写在那个图的哪个通道呢?所以一般做法是,开一个rt用于颜色,再开一个rt用作深度,这样生成两个图,传递给shader使用即可。

以上是我关于OnRenderImage函数的全部总结,希望对读者有点帮助。
当然这里没有涉及到任何的后处理的效果,后面会根据这个原理,再写几个实际例子,再次印证本文总结的规律。

总结规律如下:
1、rt和buffer不能同时存在,谁靠后设置,谁有效。
2、设置rt,重写了OnRenderImage,则source为rt,destination为ImageEffects Temp,unity在默认增加一个blit,ImageEffects Temp到我们的rt。rt上有值。
3、设置buffer,重写了OnRenderImage,则source为null(即屏幕),destination为ImageEffects Temp,unity在默认增加一个blit,ImageEffects Temp到屏幕。此时buffer中是有值的,这是抓屏操作。

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值