1 /*
2  Copyright (c) 2015 yvt
3 
4  This file is part of OpenSpades.
5 
6  OpenSpades is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OpenSpades is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OpenSpades.  If not, see <http://www.gnu.org/licenses/>.
18 
19  */
20 
21 #include <vector>
22 
23 #include <Core/Debug.h>
24 #include <Core/Math.h>
25 #include "GLAutoExposureFilter.h"
26 #include "GLProgram.h"
27 #include "GLProgramAttribute.h"
28 #include "GLProgramUniform.h"
29 #include "GLQuadRenderer.h"
30 #include "GLRenderer.h"
31 #include "IGLDevice.h"
32 #include "GLSettings.h"
33 
34 namespace spades {
35 	namespace draw {
GLAutoExposureFilter(GLRenderer * renderer)36 		GLAutoExposureFilter::GLAutoExposureFilter(GLRenderer *renderer) : renderer(renderer) {
37 			thru = renderer->RegisterProgram("Shaders/PostFilters/PassThrough.program");
38 			preprocess =
39 			  renderer->RegisterProgram("Shaders/PostFilters/AutoExposurePreprocess.program");
40 			computeGain = renderer->RegisterProgram("Shaders/PostFilters/AutoExposure.program");
41 
42 			IGLDevice *dev = renderer->GetGLDevice();
43 
44 			exposureTexture = dev->GenTexture();
45 
46 			dev->BindTexture(IGLDevice::Texture2D, exposureTexture);
47 
48 			dev->TexImage2D(IGLDevice::Texture2D, 0, IGLDevice::RGBA16F, 1, 1, 0, IGLDevice::RGBA,
49 			                IGLDevice::UnsignedByte, NULL);
50 			SPLog("Brightness Texture Allocated");
51 			dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMagFilter,
52 			                  IGLDevice::Nearest);
53 			dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureMinFilter,
54 			                  IGLDevice::Nearest);
55 			dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapS,
56 			                  IGLDevice::ClampToEdge);
57 			dev->TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
58 			                  IGLDevice::ClampToEdge);
59 
60 			exposureFramebuffer = dev->GenFramebuffer();
61 			dev->BindFramebuffer(IGLDevice::Framebuffer, exposureFramebuffer);
62 
63 			dev->FramebufferTexture2D(IGLDevice::Framebuffer, IGLDevice::ColorAttachment0,
64 			                          IGLDevice::Texture2D, exposureTexture, 0);
65 			dev->Viewport(0, 0, 1, 1);
66 			dev->ClearColor(1.f, 1.f, 1.f, 1.f);
67 			dev->Clear(IGLDevice::ColorBufferBit);
68 
69 			dev->BindFramebuffer(IGLDevice::Framebuffer, 0);
70 			dev->BindTexture(IGLDevice::Texture2D, 0);
71 
72 			SPLog("Brightness Framebuffer Allocated");
73 		}
74 
~GLAutoExposureFilter()75 		GLAutoExposureFilter::~GLAutoExposureFilter() {
76 			IGLDevice *dev = renderer->GetGLDevice();
77 			dev->DeleteTexture(exposureTexture);
78 			dev->DeleteFramebuffer(exposureFramebuffer);
79 		}
80 
81 		namespace {
82 			struct Level {
83 				int w, h;
84 				GLColorBuffer buffer;
85 			};
86 		}
87 
Filter(GLColorBuffer input,float dt)88 		GLColorBuffer GLAutoExposureFilter::Filter(GLColorBuffer input, float dt) {
89 			SPADES_MARK_FUNCTION();
90 
91 			std::vector<Level> levels;
92 
93 			IGLDevice *dev = renderer->GetGLDevice();
94 			GLQuadRenderer qr(dev);
95 
96 			GLSettings &settings = renderer->GetSettings();
97 
98 			static GLProgramAttribute thruPosition("positionAttribute");
99 			static GLProgramUniform thruColor("colorUniform");
100 			static GLProgramUniform thruTexture("mainTexture");
101 			static GLProgramUniform thruTexCoordRange("texCoordRange");
102 
103 			thruPosition(thru);
104 			thruColor(thru);
105 			thruTexture(thru);
106 			thruTexCoordRange(thru);
107 
108 			static GLProgramAttribute preprocessPosition("positionAttribute");
109 			static GLProgramUniform preprocessColor("colorUniform");
110 			static GLProgramUniform preprocessTexture("mainTexture");
111 			static GLProgramUniform preprocessTexCoordRange("texCoordRange");
112 
113 			preprocessPosition(preprocess);
114 			preprocessColor(preprocess);
115 			preprocessTexture(preprocess);
116 			preprocessTexCoordRange(preprocess);
117 
118 			static GLProgramAttribute computeGainPosition("positionAttribute");
119 			static GLProgramUniform computeGainColor("colorUniform");
120 			static GLProgramUniform computeGainTexture("mainTexture");
121 			static GLProgramUniform computeGainTexCoordRange("texCoordRange");
122 			static GLProgramUniform computeGainMinGain("minGain");
123 			static GLProgramUniform computeGainMaxGain("maxGain");
124 
125 			computeGainPosition(computeGain);
126 			computeGainColor(computeGain);
127 			computeGainTexture(computeGain);
128 			computeGainTexCoordRange(computeGain);
129 			computeGainMinGain(computeGain);
130 			computeGainMaxGain(computeGain);
131 
132 			preprocess->Use();
133 			preprocessColor.SetValue(1.f, 1.f, 1.f, 1.f);
134 			preprocessTexture.SetValue(0);
135 			preprocessTexCoordRange.SetValue(0.f, 0.f, 1.f, 1.f);
136 
137 			thru->Use();
138 			thruColor.SetValue(1.f, 1.f, 1.f, 1.f);
139 			thruTexture.SetValue(0);
140 			thruTexCoordRange.SetValue(0.f, 0.f, 1.f, 1.f);
141 
142 			dev->Enable(IGLDevice::Blend, false);
143 			dev->ActiveTexture(0);
144 
145 			// downsample until it becomes 1x1x
146 			GLColorBuffer buffer = input;
147 			bool firstLevel = true;
148 
149 			while (buffer.GetWidth() > 1 || buffer.GetHeight() > 1) {
150 				int prevW = buffer.GetWidth();
151 				int prevH = buffer.GetHeight();
152 				int newW = (prevW + 1) / 2;
153 				int newH = (prevH + 1) / 2;
154 				GLColorBuffer newLevel = input.GetManager()->CreateBufferHandle(newW, newH);
155 
156 				dev->BindTexture(IGLDevice::Texture2D, buffer.GetTexture());
157 				dev->BindFramebuffer(IGLDevice::Framebuffer, newLevel.GetFramebuffer());
158 				if (firstLevel) {
159 					preprocess->Use();
160 					qr.SetCoordAttributeIndex(preprocessPosition());
161 					firstLevel = false;
162 				} else {
163 					thru->Use();
164 					qr.SetCoordAttributeIndex(thruPosition());
165 				}
166 				dev->Viewport(0, 0, newLevel.GetWidth(), newLevel.GetHeight());
167 				dev->ClearColor(1.f, 1.f, 1.f, 1.f);
168 				dev->Clear(IGLDevice::ColorBufferBit);
169 				qr.Draw();
170 				dev->BindTexture(IGLDevice::Texture2D, 0);
171 
172 				buffer = newLevel;
173 			}
174 
175 			// compute estimated brightness on GPU
176 			dev->Enable(IGLDevice::Blend, true);
177 			dev->BlendFunc(IGLDevice::SrcAlpha, IGLDevice::OneMinusSrcAlpha);
178 
179 			float minExposure = settings.r_hdrAutoExposureMin;
180 			float maxExposure = settings.r_hdrAutoExposureMax;
181 
182 			// safety
183 			minExposure = std::min(std::max(minExposure, -10.0f), 10.0f);
184 			maxExposure = std::min(std::max(maxExposure, minExposure), 10.0f);
185 
186 			// adaption speed control
187 			if ((float)settings.r_hdrAutoExposureSpeed < 0.0f) {
188 				settings.r_hdrAutoExposureSpeed = 0.0f;
189 			}
190 			float rate = 1.0f - std::pow(0.01f, dt * settings.r_hdrAutoExposureSpeed);
191 
192 			computeGain->Use();
193 			computeGainTexCoordRange.SetValue(0.f, 0.f, 1.f, 1.f);
194 			computeGainTexture.SetValue(0);
195 			computeGainColor.SetValue(1.f, 1.f, 1.f, rate);
196 			computeGainMinGain.SetValue(std::pow(2.0f, minExposure));
197 			computeGainMaxGain.SetValue(std::pow(2.0f, maxExposure));
198 			qr.SetCoordAttributeIndex(computeGainPosition());
199 			dev->BindFramebuffer(IGLDevice::Framebuffer, exposureFramebuffer);
200 			dev->BindTexture(IGLDevice::Texture2D, buffer.GetTexture());
201 			dev->Viewport(0, 0, 1, 1);
202 			qr.Draw();
203 			dev->BindTexture(IGLDevice::Texture2D, 0);
204 
205 			// apply exposure adjustment
206 			thru->Use();
207 			thruColor.SetValue(1.f, 1.f, 1.f, 1.f);
208 			thruTexCoordRange.SetValue(0.f, 0.f, 1.f, 1.f);
209 			thruTexture.SetValue(0);
210 			dev->Enable(IGLDevice::Blend, true);
211 			dev->BlendFunc(IGLDevice::DestColor, IGLDevice::Zero); // multiply
212 			qr.SetCoordAttributeIndex(thruPosition());
213 			dev->BindTexture(IGLDevice::Texture2D, exposureTexture);
214 			dev->BindFramebuffer(IGLDevice::Framebuffer, input.GetFramebuffer());
215 			dev->Viewport(0, 0, input.GetWidth(), input.GetHeight());
216 
217 			qr.Draw();
218 			dev->BindTexture(IGLDevice::Texture2D, 0);
219 
220 			dev->BlendFunc(IGLDevice::SrcAlpha, IGLDevice::OneMinusSrcAlpha);
221 
222 			return input;
223 		}
224 	}
225 }
226