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