- ContrastStretch.cs
- Scripts /
- ImageEffects /
- Effects /
- Standard Assets /
- Assets /
- project /
1 using System;
2 using UnityEngine;
3
4 namespace UnityStandardAssets.ImageEffects
5 {
6 [ExecuteInEditMode]
7 [AddComponentMenu("Image Effects/Color Adjustments/Contrast Stretch")]
8 public class ContrastStretch : MonoBehaviour
9 {
10 /// Adaptation speed - percents per frame, if playing at 30FPS.
11 /// Default is 0.02 (2% each 1/30s).
12 [Range(0.0001f, 1.0f)]
13 public float adaptationSpeed = 0.02f;
14
15 /// If our scene is really dark (or really bright), we might not want to
16 /// stretch its contrast to the full range.
17 /// limitMinimum=0, limitMaximum=1 is the same as not applying the effect at all.
18 /// limitMinimum=1, limitMaximum=0 is always stretching colors to full range.
19
20 /// The limit on the minimum luminance (0...1) - we won't go above this.
21 [Range(0.0f,1.0f)]
22 public float limitMinimum = 0.2f;
23
24 /// The limit on the maximum luminance (0...1) - we won't go below this.
25 [Range(0.0f, 1.0f)]
26 public float limitMaximum = 0.6f;
27
28
29 // To maintain adaptation levels over time, we need two 1x1 render textures
30 // and ping-pong between them.
31 private RenderTexture[] adaptRenderTex = new RenderTexture[2];
32 private int curAdaptIndex = 0;
33
34
35 // Computes scene luminance (grayscale) image
36 public Shader shaderLum;
37 private Material m_materialLum;
38 protected Material materialLum {
39 get {
40 if ( m_materialLum == null ) {
41 m_materialLum = new Material(shaderLum);
42 m_materialLum.hideFlags = HideFlags.HideAndDontSave;
43 }
44 return m_materialLum;
45 }
46 }
47
48 // Reduces size of the image by 2x2, while computing maximum/minimum values.
49 // By repeatedly applying this shader, we reduce the initial luminance image
50 // to 1x1 image with minimum/maximum luminances found.
51 public Shader shaderReduce;
52 private Material m_materialReduce;
53 protected Material materialReduce {
54 get {
55 if ( m_materialReduce == null ) {
56 m_materialReduce = new Material(shaderReduce);
57 m_materialReduce.hideFlags = HideFlags.HideAndDontSave;
58 }
59 return m_materialReduce;
60 }
61 }
62
63 // Adaptation shader - gradually "adapts" minimum/maximum luminances,
64 // based on currently adapted 1x1 image and the actual 1x1 image of the current scene.
65 public Shader shaderAdapt;
66 private Material m_materialAdapt;
67 protected Material materialAdapt {
68 get {
69 if ( m_materialAdapt == null ) {
70 m_materialAdapt = new Material(shaderAdapt);
71 m_materialAdapt.hideFlags = HideFlags.HideAndDontSave;
72 }
73 return m_materialAdapt;
74 }
75 }
76
77 // Final pass - stretches the color values of the original scene, based on currently
78 // adpated minimum/maximum values.
79 public Shader shaderApply;
80 private Material m_materialApply;
81 protected Material materialApply {
82 get {
83 if ( m_materialApply == null ) {
84 m_materialApply = new Material(shaderApply);
85 m_materialApply.hideFlags = HideFlags.HideAndDontSave;
86 }
87 return m_materialApply;
88 }
89 }
90
91 void Start()
92 {
93 // Disable if we don't support image effects
94 if (!SystemInfo.supportsImageEffects) {
95 enabled = false;
96 return;
97 }
98
99 if (!shaderAdapt.isSupported || !shaderApply.isSupported || !shaderLum.isSupported || !shaderReduce.isSupported) {
100 enabled = false;
101 return;
102 }
103 }
104
105 void OnEnable()
106 {
107 for( int i = 0; i < 2; ++i )
108 {
109 if ( !adaptRenderTex[i] ) {
110 adaptRenderTex[i] = new RenderTexture(1, 1, 0);
111 adaptRenderTex[i].hideFlags = HideFlags.HideAndDontSave;
112 }
113 }
114 }
115
116 void OnDisable()
117 {
118 for( int i = 0; i < 2; ++i )
119 {
120 DestroyImmediate( adaptRenderTex[i] );
121 adaptRenderTex[i] = null;
122 }
123 if ( m_materialLum )
124 DestroyImmediate( m_materialLum );
125 if ( m_materialReduce )
126 DestroyImmediate( m_materialReduce );
127 if ( m_materialAdapt )
128 DestroyImmediate( m_materialAdapt );
129 if ( m_materialApply )
130 DestroyImmediate( m_materialApply );
131 }
132
133
134 /// Apply the filter
135 void OnRenderImage (RenderTexture source, RenderTexture destination)
136 {
137 // Blit to smaller RT and convert to luminance on the way
138 const int TEMP_RATIO = 1; // 4x4 smaller
139 RenderTexture rtTempSrc = RenderTexture.GetTemporary(source.width/TEMP_RATIO, source.height/TEMP_RATIO);
140 Graphics.Blit (source, rtTempSrc, materialLum);
141
142 // Repeatedly reduce this image in size, computing min/max luminance values
143 // In the end we'll have 1x1 image with min/max luminances found.
144 const int FINAL_SIZE = 1;
145 //const int FINAL_SIZE = 1;
146 while( rtTempSrc.width > FINAL_SIZE || rtTempSrc.height > FINAL_SIZE )
147 {
148 const int REDUCE_RATIO = 2; // our shader does 2x2 reduction
149 int destW = rtTempSrc.width / REDUCE_RATIO;
150 if ( destW < FINAL_SIZE ) destW = FINAL_SIZE;
151 int destH = rtTempSrc.height / REDUCE_RATIO;
152 if ( destH < FINAL_SIZE ) destH = FINAL_SIZE;
153 RenderTexture rtTempDst = RenderTexture.GetTemporary(destW,destH);
154 Graphics.Blit (rtTempSrc, rtTempDst, materialReduce);
155
156 // Release old src temporary, and make new temporary the source
157 RenderTexture.ReleaseTemporary( rtTempSrc );
158 rtTempSrc = rtTempDst;
159 }
160
161 // Update viewer's adaptation level
162 CalculateAdaptation( rtTempSrc );
163
164 // Apply contrast strech to the original scene, using currently adapted parameters
165 materialApply.SetTexture("_AdaptTex", adaptRenderTex[curAdaptIndex] );
166 Graphics.Blit (source, destination, materialApply);
167
168 RenderTexture.ReleaseTemporary( rtTempSrc );
169 }
170
171
172 /// Helper function to do gradual adaptation to min/max luminances
173 private void CalculateAdaptation( Texture curTexture )
174 {
175 int prevAdaptIndex = curAdaptIndex;
176 curAdaptIndex = (curAdaptIndex+1) % 2;
177
178 // Adaptation speed is expressed in percents/frame, based on 30FPS.
179 // Calculate the adaptation lerp, based on current FPS.
180 float adaptLerp = 1.0f - Mathf.Pow( 1.0f - adaptationSpeed, 30.0f * Time.deltaTime );
181 const float kMinAdaptLerp = 0.01f;
182 adaptLerp = Mathf.Clamp( adaptLerp, kMinAdaptLerp, 1 );
183
184 materialAdapt.SetTexture("_CurTex", curTexture );
185 materialAdapt.SetVector("_AdaptParams", new Vector4(
186 adaptLerp,
187 limitMinimum,
188 limitMaximum,
189 0.0f
190 ));
191 // clear destination RT so its contents don't need to be restored
192 Graphics.SetRenderTarget(adaptRenderTex[curAdaptIndex]);
193 GL.Clear(false, true, Color.black);
194 Graphics.Blit (
195 adaptRenderTex[prevAdaptIndex],
196 adaptRenderTex[curAdaptIndex],
197 materialAdapt);
198 }
199 }
200 }
2 using UnityEngine;
3
4 namespace UnityStandardAssets.ImageEffects
5 {
6 [ExecuteInEditMode]
7 [AddComponentMenu("Image Effects/Color Adjustments/Contrast Stretch")]
8 public class ContrastStretch : MonoBehaviour
9 {
10 /// Adaptation speed - percents per frame, if playing at 30FPS.
11 /// Default is 0.02 (2% each 1/30s).
12 [Range(0.0001f, 1.0f)]
13 public float adaptationSpeed = 0.02f;
14
15 /// If our scene is really dark (or really bright), we might not want to
16 /// stretch its contrast to the full range.
17 /// limitMinimum=0, limitMaximum=1 is the same as not applying the effect at all.
18 /// limitMinimum=1, limitMaximum=0 is always stretching colors to full range.
19
20 /// The limit on the minimum luminance (0...1) - we won't go above this.
21 [Range(0.0f,1.0f)]
22 public float limitMinimum = 0.2f;
23
24 /// The limit on the maximum luminance (0...1) - we won't go below this.
25 [Range(0.0f, 1.0f)]
26 public float limitMaximum = 0.6f;
27
28
29 // To maintain adaptation levels over time, we need two 1x1 render textures
30 // and ping-pong between them.
31 private RenderTexture[] adaptRenderTex = new RenderTexture[2];
32 private int curAdaptIndex = 0;
33
34
35 // Computes scene luminance (grayscale) image
36 public Shader shaderLum;
37 private Material m_materialLum;
38 protected Material materialLum {
39 get {
40 if ( m_materialLum == null ) {
41 m_materialLum = new Material(shaderLum);
42 m_materialLum.hideFlags = HideFlags.HideAndDontSave;
43 }
44 return m_materialLum;
45 }
46 }
47
48 // Reduces size of the image by 2x2, while computing maximum/minimum values.
49 // By repeatedly applying this shader, we reduce the initial luminance image
50 // to 1x1 image with minimum/maximum luminances found.
51 public Shader shaderReduce;
52 private Material m_materialReduce;
53 protected Material materialReduce {
54 get {
55 if ( m_materialReduce == null ) {
56 m_materialReduce = new Material(shaderReduce);
57 m_materialReduce.hideFlags = HideFlags.HideAndDontSave;
58 }
59 return m_materialReduce;
60 }
61 }
62
63 // Adaptation shader - gradually "adapts" minimum/maximum luminances,
64 // based on currently adapted 1x1 image and the actual 1x1 image of the current scene.
65 public Shader shaderAdapt;
66 private Material m_materialAdapt;
67 protected Material materialAdapt {
68 get {
69 if ( m_materialAdapt == null ) {
70 m_materialAdapt = new Material(shaderAdapt);
71 m_materialAdapt.hideFlags = HideFlags.HideAndDontSave;
72 }
73 return m_materialAdapt;
74 }
75 }
76
77 // Final pass - stretches the color values of the original scene, based on currently
78 // adpated minimum/maximum values.
79 public Shader shaderApply;
80 private Material m_materialApply;
81 protected Material materialApply {
82 get {
83 if ( m_materialApply == null ) {
84 m_materialApply = new Material(shaderApply);
85 m_materialApply.hideFlags = HideFlags.HideAndDontSave;
86 }
87 return m_materialApply;
88 }
89 }
90
91 void Start()
92 {
93 // Disable if we don't support image effects
94 if (!SystemInfo.supportsImageEffects) {
95 enabled = false;
96 return;
97 }
98
99 if (!shaderAdapt.isSupported || !shaderApply.isSupported || !shaderLum.isSupported || !shaderReduce.isSupported) {
100 enabled = false;
101 return;
102 }
103 }
104
105 void OnEnable()
106 {
107 for( int i = 0; i < 2; ++i )
108 {
109 if ( !adaptRenderTex[i] ) {
110 adaptRenderTex[i] = new RenderTexture(1, 1, 0);
111 adaptRenderTex[i].hideFlags = HideFlags.HideAndDontSave;
112 }
113 }
114 }
115
116 void OnDisable()
117 {
118 for( int i = 0; i < 2; ++i )
119 {
120 DestroyImmediate( adaptRenderTex[i] );
121 adaptRenderTex[i] = null;
122 }
123 if ( m_materialLum )
124 DestroyImmediate( m_materialLum );
125 if ( m_materialReduce )
126 DestroyImmediate( m_materialReduce );
127 if ( m_materialAdapt )
128 DestroyImmediate( m_materialAdapt );
129 if ( m_materialApply )
130 DestroyImmediate( m_materialApply );
131 }
132
133
134 /// Apply the filter
135 void OnRenderImage (RenderTexture source, RenderTexture destination)
136 {
137 // Blit to smaller RT and convert to luminance on the way
138 const int TEMP_RATIO = 1; // 4x4 smaller
139 RenderTexture rtTempSrc = RenderTexture.GetTemporary(source.width/TEMP_RATIO, source.height/TEMP_RATIO);
140 Graphics.Blit (source, rtTempSrc, materialLum);
141
142 // Repeatedly reduce this image in size, computing min/max luminance values
143 // In the end we'll have 1x1 image with min/max luminances found.
144 const int FINAL_SIZE = 1;
145 //const int FINAL_SIZE = 1;
146 while( rtTempSrc.width > FINAL_SIZE || rtTempSrc.height > FINAL_SIZE )
147 {
148 const int REDUCE_RATIO = 2; // our shader does 2x2 reduction
149 int destW = rtTempSrc.width / REDUCE_RATIO;
150 if ( destW < FINAL_SIZE ) destW = FINAL_SIZE;
151 int destH = rtTempSrc.height / REDUCE_RATIO;
152 if ( destH < FINAL_SIZE ) destH = FINAL_SIZE;
153 RenderTexture rtTempDst = RenderTexture.GetTemporary(destW,destH);
154 Graphics.Blit (rtTempSrc, rtTempDst, materialReduce);
155
156 // Release old src temporary, and make new temporary the source
157 RenderTexture.ReleaseTemporary( rtTempSrc );
158 rtTempSrc = rtTempDst;
159 }
160
161 // Update viewer's adaptation level
162 CalculateAdaptation( rtTempSrc );
163
164 // Apply contrast strech to the original scene, using currently adapted parameters
165 materialApply.SetTexture("_AdaptTex", adaptRenderTex[curAdaptIndex] );
166 Graphics.Blit (source, destination, materialApply);
167
168 RenderTexture.ReleaseTemporary( rtTempSrc );
169 }
170
171
172 /// Helper function to do gradual adaptation to min/max luminances
173 private void CalculateAdaptation( Texture curTexture )
174 {
175 int prevAdaptIndex = curAdaptIndex;
176 curAdaptIndex = (curAdaptIndex+1) % 2;
177
178 // Adaptation speed is expressed in percents/frame, based on 30FPS.
179 // Calculate the adaptation lerp, based on current FPS.
180 float adaptLerp = 1.0f - Mathf.Pow( 1.0f - adaptationSpeed, 30.0f * Time.deltaTime );
181 const float kMinAdaptLerp = 0.01f;
182 adaptLerp = Mathf.Clamp( adaptLerp, kMinAdaptLerp, 1 );
183
184 materialAdapt.SetTexture("_CurTex", curTexture );
185 materialAdapt.SetVector("_AdaptParams", new Vector4(
186 adaptLerp,
187 limitMinimum,
188 limitMaximum,
189 0.0f
190 ));
191 // clear destination RT so its contents don't need to be restored
192 Graphics.SetRenderTarget(adaptRenderTex[curAdaptIndex]);
193 GL.Clear(false, true, Color.black);
194 Graphics.Blit (
195 adaptRenderTex[prevAdaptIndex],
196 adaptRenderTex[curAdaptIndex],
197 materialAdapt);
198 }
199 }
200 }