Search Unity

  1. Looking for a job or to hire someone for a project? Check out the re-opened job forums.
    Dismiss Notice
  2. Unity 2020 LTS & Unity 2021.1 have been released.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Keep previous frame without turning it into a Texture2D

Discussion in 'Shaders' started by OCASM, Jun 15, 2016.

  1. OCASM

    OCASM

    Joined:
    Jan 12, 2011
    Posts:
    282
    In order to create a ghosting effect I need to keep the previously rendered frame and blend it with the current one. I keep the previous frame by turning it into a Texture2D but the performance of ReadPixels is terrible. I'm wondering if there's a better way to do so.

    Here's the code I have:

    Code (CSharp):
    1.  
    2. // Ghosting.
    3.  
    4. using UnityEngine;
    5. using System.Collections;
    6.  
    7. [AddComponentMenu ("OCASM/Image Effects/ImageEffect002")]
    8. public class ImageEffect_002 : MonoBehaviour {
    9.  
    10.    [Range (0,0.99f)]
    11.    public float intensity;
    12.    private Material ImageEffect_002_Mat;
    13.    private Texture2D pastFrame;
    14.  
    15.    void Awake(){
    16.      ImageEffect_002_Mat = new Material (Shader.Find("Hidden/OCASM/Image Effects/ImageEffect_002"));
    17.    }
    18.  
    19.    void OnRenderImage(RenderTexture src, RenderTexture dst){
    20.      if (pastFrame) {
    21.        ImageEffect_002_Mat.SetFloat ("_Intensity", intensity);
    22.        ImageEffect_002_Mat.SetTexture ("_BTex", pastFrame);  
    23.        Graphics.Blit (src, dst, ImageEffect_002_Mat);
    24.      }
    25.    }
    26.  
    27.    void OnPostRender () {
    28.      if (!pastFrame) {
    29.        pastFrame = new Texture2D (Screen.width, Screen.height);
    30.      }
    31.      RenderTexture.active = Camera.main.targetTexture;
    32.      pastFrame.ReadPixels (new Rect(0,0,Screen.width,Screen.height),0,0);
    33.      pastFrame.Apply ();
    34.    }
    35. }
    36.  
    37.  
    Code (CSharp):
    1. Shader "Hidden/OCASM/Image Effects/ImageEffect_002"
    2. {
    3.     Properties {
    4.         _MainTex ("Input Render Texture", 2D) = "black" {}
    5.         _BTex ("Output Render Texture", 2D) = "black" {}
    6.         _Intensity ("Intensity", Range (0,1)) = 0.5
    7.     }
    8.     SubShader {
    9.         Pass {
    10.             CGPROGRAM
    11.             #pragma vertex vert_img
    12.             #pragma fragment frag
    13.  
    14.             #include "UnityCG.cginc"
    15.  
    16.             uniform sampler2D _MainTex;
    17.             uniform sampler2D _BTex;
    18.             uniform float _Intensity;
    19.  
    20.             float4 frag(v2f_img i) : COLOR{
    21.                 float4 c1 = tex2D(_MainTex, i.uv);
    22.                 float4 c2 = tex2D(_BTex, i.uv);
    23.                 float4 c3 = lerp(c1, c2, _Intensity);
    24.                 return c3;
    25.             }
    26.             ENDCG
    27.         }
    28.     }
    29. }
    30.  
     
  2. OCASM

    OCASM

    Joined:
    Jan 12, 2011
    Posts:
    282
    OK, it turned out to be simpler than I thought BUT now the resulting image flips vertically every frame. Any idea on what's happening?

    Code (CSharp):
    1. // Ghosting.
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. [AddComponentMenu ("OCASM/Image Effects/ImageEffect002")]
    7. public class ImageEffect_002 : MonoBehaviour {
    8.  
    9.     // VARIABLES
    10.     [Range (0,0.99f)]
    11.     public float intensity;
    12.     private Material ImageEffect_002_Mat;
    13.     private RenderTexture pastFrame;
    14.  
    15.     void Awake(){
    16.         ImageEffect_002_Mat = new Material (Shader.Find("Hidden/OCASM/Image Effects/ImageEffect_002"));
    17.     }
    18.  
    19.     void OnRenderImage(RenderTexture src, RenderTexture dst){
    20.         if (!pastFrame) {
    21.             pastFrame = new RenderTexture (Screen.width, Screen.height, 16);
    22.         }
    23.  
    24.         ImageEffect_002_Mat.SetFloat ("_Intensity", intensity);
    25.         ImageEffect_002_Mat.SetTexture ("_BTex", pastFrame);  
    26.         Graphics.Blit (src, dst, ImageEffect_002_Mat);
    27.  
    28.         RenderTexture.active = Camera.main.targetTexture;
    29.         Graphics.Blit (RenderTexture.active, pastFrame);
    30.     }
    31. }
    32.  
     

    Attached Files:

  3. OCASM

    OCASM

    Joined:
    Jan 12, 2011
    Posts:
    282
    This was the problem:

    http://docs.unity3d.com/Manual/SL-PlatformDifferences.html

    Here's the final code, at least an order of magnitude faster than with the ReadPixels method:

    Code (CSharp):
    1. // Ghosting.
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. [AddComponentMenu ("OCASM/Image Effects/ImageEffect002")]
    7. public class ImageEffect_002 : MonoBehaviour {
    8.  
    9.     [Range (0,0.99f)]
    10.     public float intensity;
    11.     private Material ImageEffect_002_Mat;
    12.     private RenderTexture pastFrame;
    13.  
    14.     void Start(){
    15.         ImageEffect_002_Mat = new Material (Shader.Find("Hidden/OCASM/Image Effects/ImageEffect_002"));
    16.         pastFrame = new RenderTexture (Screen.width, Screen.height, 16);
    17.     }
    18.  
    19.     void OnRenderImage(RenderTexture src, RenderTexture dst){
    20.         ImageEffect_002_Mat.SetFloat ("_Intensity", intensity);
    21.         ImageEffect_002_Mat.SetTexture ("_BTex", pastFrame);
    22.         Graphics.Blit (src, dst, ImageEffect_002_Mat);
    23.  
    24.         Graphics.Blit (RenderTexture.active, pastFrame);
    25.     }
    26. }
    27.  
    Code (CSharp):
    1. Shader "Hidden/OCASM/Image Effects/ImageEffect_002"
    2. {
    3.     Properties {
    4.         _MainTex ("Input Render Texture", 2D) = "black" {}
    5.         _BTex ("Output Render Texture", 2D) = "black" {}
    6.         _Intensity ("Intensity", Range (0,1)) = 0.5
    7.     }
    8.     SubShader {
    9.  
    10.     // No culling or depth
    11.         Cull Off ZWrite Off ZTest Always
    12.  
    13.         Pass {
    14.             CGPROGRAM
    15.             #pragma vertex vert_img
    16.             #pragma fragment frag
    17.  
    18.             #include "UnityCG.cginc"
    19.  
    20.             uniform sampler2D _MainTex;
    21.             uniform sampler2D _BTex;
    22.             uniform float _Intensity;
    23.  
    24.             float4 frag(v2f_img i) : COLOR{
    25.                 float4 c1 = tex2D(_MainTex, i.uv);
    26.                 i.uv.y = 1 - i.uv.y;
    27.                 float4 c2 = tex2D(_BTex, i.uv);
    28.                 float4 c3 = lerp(c1, c2, _Intensity);
    29.  
    30.                 return c3;
    31.             }
    32.             ENDCG
    33.         }
    34.     }
    35. }
    36.  
    On Forward it's still flipped but I used Deferred so I don't care.
     

    Attached Files:

    Last edited: Jun 15, 2016
  4. sarpsay

    sarpsay

    Joined:
    Apr 14, 2019
    Posts:
    1
    Thank you so much for this. I've been searching for an effect like this everywhere and couldn't find anything. I've used this code for my game and am going to study it to learn more about the shader that you've created, and will make sure to credit you if I use it in my game for the future! Great work!
     
  5. vettorazi

    vettorazi

    Joined:
    Feb 20, 2018
    Posts:
    2
    OCASM Thanks so much. I was looking everywhere for an example like this. Thanks!
     
    OCASM likes this.
unityunity