Search Unity

Help with Auto-Exposure RnederTextures

Discussion in 'Shaders' started by WGermany, May 28, 2014.

  1. WGermany

    WGermany

    Joined:
    Jun 27, 2013
    Posts:
    78
    Hello everyone. Although I will be posting a script I felt it was relevant to shaders. I am having trouble making an Auto Exposure effect, when you look at something bright the screen gets darker in order to compensate for the high delta in luminance, and vice versa. The problem I am having is I don't know how to handle the render textures. Ive tried a bunch of things from other files I've seen around the web and I don't understand and can't figure out on my own how to get them to work properly. I even looked at Unity's example in their tone mapper effect but I didn't want to use that as Unity's current implementation has a lot of black screen issues. Im guessing that I need to create an internal render texture and keep it in memory somehow? My current implementation just blits render textures every frame like crazy which of course is dumb I just wanted to test out the effect and it works fine its just the handling of render textures where I have no idea what to do. Everything works by the way it just has the problem listed above, just to make that clear.

    CurLuminance and Last Luminance are where the problem resides. I don't know what to do with them and they both have their own samplers in the shader. I can post all of the shader code but its a mess hehe. Sorry for the messy code, I need to get better at that.

    Code:

    Code (csharp):
    1. float4 AdaptLuminance(v2f_img i) : COLOR
    2.         {
    3.             float lastLum = exp(tex2D(_LastLumTex, i.uv).x);
    4.             float currentLum = tex2D(_CurLumTex, i.uv).x;
    5.                
    6.             // Adapt the luminance using Pattanaik's technique
    7.             float Tau = 0.45f;    
    8.             _AdaptedLum = lastLum + (currentLum - lastLum) * (1 - exp(-_Ta * Tau));
    9.            
    10.             //adaptedLum = clamp(adaptedLum, _Min, _Max);
    11.            
    12.             return float4(log(_AdaptedLum), 1.0f, 1.0f, 1.0f);
    13.         }

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [ExecuteInEditMode]
    5. public class AutoExposure : MonoBehaviour {
    6.  
    7.     #region Variables
    8.     public Shader AutoExposureShader;
    9.     public float AdaptationSpeed = 1.0f;
    10.     public float currentExposure;
    11.     public float Exposure = 1.0f;
    12.     public float Minimum = 0.55f;
    13.     public float Maximum = 1.25f;
    14.     public bool BlueShift = true;
    15.     public float BlueShiftAmount = 4.0f;
    16.     public bool ShowAvgLuminance = true;
    17.     private float Ratio;
    18.     private RenderTexture RTT_Lum;
    19.     private RenderTexture RTT_0;
    20.     private RenderTexture RTT_1;
    21.     private RenderTexture RTT_2;
    22.     private RenderTexture LuminanceTex;
    23.     private RenderTexture CurLuminance;
    24.     private RenderTexture LastLuminance;
    25.     private Material AutoExposureMaterial;
    26.     #endregion
    27.    
    28.     #region Properties
    29.     Material ExposureMaterial
    30.     {
    31.         get
    32.         {
    33.             if(AutoExposureMaterial == null)
    34.             {
    35.                 AutoExposureMaterial = new Material(AutoExposureShader);
    36.                 AutoExposureMaterial.hideFlags = HideFlags.HideAndDontSave;
    37.             }
    38.             return AutoExposureMaterial;
    39.         }
    40.     }
    41.     #endregion
    42.     // Use this for initialization
    43.     void Start ()
    44.     {
    45.         if(!SystemInfo.supportsImageEffects)
    46.         {
    47.             enabled = false;
    48.             return;
    49.         }
    50.     }
    51.    
    52.     void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture)
    53.     {
    54.         if(AutoExposureShader != null)
    55.         {
    56.             ExposureMaterial.SetFloat("_GlobalExposure", Exposure);
    57.             ExposureMaterial.SetFloat("_AdaptationSpeed", AdaptationSpeed);
    58.             ExposureMaterial.SetFloat("_BlueShiftAmt", BlueShiftAmount);
    59.             ExposureMaterial.SetFloat("_Max", Maximum);
    60.             ExposureMaterial.SetFloat("_Min", Minimum);
    61.             RTT_Lum = RenderTexture.GetTemporary(sourceTexture.width, sourceTexture.height, 0, RenderTextureFormat.ARGBHalf);
    62.             Graphics.Blit(sourceTexture, RTT_Lum, ExposureMaterial, 4);
    63.             //Downsample from current screen size to 256x256
    64.             RTT_0 = RenderTexture.GetTemporary(256, 256, 0, RenderTextureFormat.ARGBHalf);
    65.             //Make sure Bilinear filtering is enabled
    66.             RTT_0.filterMode = FilterMode.Bilinear;
    67.             //LuminanceTex.filterMode = FilterMode.Bilinear;
    68.             Graphics.Blit(RTT_Lum, RTT_0, ExposureMaterial, 0);
    69.             RenderTexture.ReleaseTemporary(RTT_Lum);
    70.             //Downsample to a 1x1 texture and store this as the "LuminanceTex"
    71.             RTT_1 = RenderTexture.GetTemporary(64, 64, 0, RenderTextureFormat.ARGBHalf);
    72.             RTT_1.filterMode = FilterMode.Bilinear;
    73.             //From 256x256 ---> 64x64
    74.             Graphics.Blit(RTT_0, RTT_1, ExposureMaterial, 1);
    75.             RenderTexture.ReleaseTemporary(RTT_0);
    76.             //From 64x64 ---> 8x8
    77.             RTT_2 = RenderTexture.GetTemporary(8, 8, 0, RenderTextureFormat.ARGBHalf);
    78.             RTT_2.filterMode = FilterMode.Bilinear;
    79.             Graphics.Blit(RTT_1, RTT_2, ExposureMaterial, 1);
    80.             RenderTexture.ReleaseTemporary(RTT_1);
    81.             //From 8x8 ---> 1x1 (Luminance)
    82.             LuminanceTex = RenderTexture.GetTemporary(1, 1, 0, RenderTextureFormat.ARGBHalf);
    83.             Graphics.Blit(RTT_2, LuminanceTex, ExposureMaterial, 1);
    84.             RenderTexture.ReleaseTemporary(RTT_2);
    85.             RenderTexture.ReleaseTemporary(LuminanceTex);
    86.             //Store luminance and use it to calculate auto-exposure
    87.             ExposureMaterial.SetFloat("_Ta", Time.deltaTime * AdaptationSpeed);
    88.             CurLuminance = RenderTexture.GetTemporary(1, 1, 0, RenderTextureFormat.ARGBHalf);
    89.             LastLuminance = RenderTexture.GetTemporary(1, 1, 0, RenderTextureFormat.ARGBHalf);
    90.             CurLuminance.hideFlags = HideFlags.DontSave;
    91.             LastLuminance.hideFlags = HideFlags.DontSave;
    92.             ExposureMaterial.SetTexture("_CurLumTex", CurLuminance);
    93.             ExposureMaterial.SetFloat("_AdaptedLum", currentExposure);
    94.             Graphics.Blit(CurLuminance, LastLuminance, ExposureMaterial, 5);
    95.             ExposureMaterial.SetTexture("_LastLumTex", LastLuminance);
    96.             ExposureMaterial.SetFloat("_AdaptedLum", currentExposure);
    97.             Graphics.Blit(LastLuminance, LuminanceTex, ExposureMaterial, 5);
    98.             Graphics.Blit(LuminanceTex, destTexture, ExposureMaterial, 2);
    99.             ExposureMaterial.SetTexture("_Origin", sourceTexture);
    100.             ExposureMaterial.SetTexture("_LuminanceTex", LuminanceTex);
    101.             Graphics.Blit(sourceTexture, destTexture, ExposureMaterial, 3);
    102.            
    103.             Swap(CurLuminance, LastLuminance);
    104.            
    105.             //RenderTexture.ReleaseTemporary(CurLuminance);
    106.             //RenderTexture.ReleaseTemporary(LastLuminance);
    107.            
    108.         }
    109.     }
    110.    
    111.     void Swap (RenderTexture RT_1, RenderTexture RT_2)
    112.     {
    113.         RenderTexture temp = RT_1;
    114.         RT_1 = RT_2;
    115.         RT_2 = RT_1;
    116.         temp = null;
    117.        
    118.     }
    119.    
    120.     // Update is called once per frame
    121.     void Update ()
    122.     {
    123.         if(BlueShift == true)
    124.         {
    125.             Shader.EnableKeyword("BLUESHIFTON");
    126.             Shader.DisableKeyword("BLUESHIFTOFF");
    127.         }
    128.         if(BlueShift == false)
    129.         {
    130.             Shader.DisableKeyword("BLUESHIFTON");
    131.             Shader.EnableKeyword("BLUESHIFTOFF");
    132.         }
    133.     }
    134.    
    135.     void OnGUI ()
    136.     {
    137.         Vector2 size = new Vector2 (200f, 200f);
    138.         Ratio = (float)Screen.width / Screen.height;
    139.         float margin = 10;
    140.         if (ShowAvgLuminance)
    141.             GUI.DrawTexture (new Rect (margin, Screen.height - (size.y + margin), size.x, size.y), LuminanceTex, ScaleMode.StretchToFill, false, 1);
    142.     }
    143.    
    144.     void OnDisable ()
    145.     {
    146.         if(AutoExposureMaterial)
    147.         {
    148.             DestroyImmediate(AutoExposureMaterial);
    149.             DestroyImmediate(CurLuminance);
    150.             DestroyImmediate(LastLuminance);
    151.         }
    152.        
    153.     }
    154.    
    155.    
    156. }
     
    Last edited: May 29, 2014
  2. metaleap

    metaleap

    Joined:
    Oct 3, 2012
    Posts:
    589
    Without having studied your lengthy script code, I wanted to point out that Unity Pro (and Pro trial) have something like this in the "Image Effects" Standard Assets package. Used for Reinhard with automatic log luminance. So this might be the easiest way to figure out how to make it work in a Unity context -- check that built-in script+shader combo.
     
  3. WGermany

    WGermany

    Joined:
    Jun 27, 2013
    Posts:
    78
    Thanks, I'll look over it again and see what I can get from it but It doesn't really help me much. BTW great job on your custom deferred rendering! I plan to do the same too ;)