Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / bloom_effect.cpp
blob8beec7d221fc02bca98c377fa8728cfb0bd59bbe
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013-2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
22 // NEL3D
23 #include "nel/3d/u_driver.h"
24 #include "nel/3d/u_scene.h"
25 #include "nel/3d/u_camera.h"
27 //3D
28 #include "nel/3d/driver_user.h"
29 #include "nel/3d/texture_bloom.h"
30 #include "nel/3d/texture_user.h"
32 #include "nel/3d/bloom_effect.h"
35 using namespace NLMISC;
36 using namespace NL3D;
37 using namespace std;
39 #ifdef DEBUG_NEW
40 #define new DEBUG_NEW
41 #endif
43 namespace NL3D
47 // vertex program used to blur texture
48 static const char *TextureOffset =
49 "!!VP1.0 \n\
50 MOV o[COL0].x, c[8].x; \n\
51 MOV o[COL0].y, c[8].y; \n\
52 MOV o[COL0].z, c[8].z; \n\
53 MOV o[COL0].w, c[8].w; \n\
54 MOV o[HPOS].x, v[OPOS].x; \n\
55 MOV o[HPOS].y, v[OPOS].y; \n\
56 MOV o[HPOS].z, v[OPOS].z; \n\
57 MOV o[HPOS].w, c[9].w; \n\
58 ADD o[TEX0], v[TEX0], c[10]; \n\
59 ADD o[TEX1], v[TEX0], c[11]; \n\
60 ADD o[TEX2], v[TEX0], c[12]; \n\
61 ADD o[TEX3], v[TEX0], c[13]; \n\
62 END \n";
65 static NLMISC::CSmartPtr<CVertexProgram> TextureOffsetVertexProgram;
68 //-----------------------------------------------------------------------------------------------------------
70 CBloomEffect::CBloomEffect()
72 if (!TextureOffsetVertexProgram)
74 TextureOffsetVertexProgram = new CVertexProgram(TextureOffset);
77 _Driver = NULL;
78 _Scene = NULL;
79 _SquareBloom = true;
80 _DensityBloom = 128;
81 _Init = false;
83 _BlurFinalTex = NULL;
84 _BlurHorizontalTex = NULL;
87 //-----------------------------------------------------------------------------------------------------------
89 CBloomEffect::~CBloomEffect()
91 if (_Init)
93 if (!_DisplayBlurMat.empty())
95 if (_Driver) _Driver->deleteMaterial(_DisplayBlurMat);
98 if (!_DisplaySquareBlurMat.empty())
100 if (_Driver) _Driver->deleteMaterial(_DisplaySquareBlurMat);
103 if (!_BlurMat.empty())
105 if (_Driver) _Driver->deleteMaterial(_BlurMat);
110 //-----------------------------------------------------------------------------------------------------------
112 void CBloomEffect::init()
114 if (!((CDriverUser *)_Driver)->getDriver()->supportBloomEffect())
115 return;
117 CDriverUser *dru = static_cast<CDriverUser *>(_Driver);
118 IDriver *drv = dru->getDriver();
120 _BlurWidth = 256;
121 _BlurHeight = 256;
123 // initialize blur material
124 _BlurMat = _Driver->createMaterial();
125 CMaterial * matObject = _BlurMat.getObjectPtr();
126 _BlurMat.initUnlit();
127 _BlurMat.setColor(CRGBA::White);
128 _BlurMat.setBlend (false);
129 _BlurMat.setAlphaTest (false);
130 matObject->setBlendFunc (CMaterial::one, CMaterial::zero);
131 matObject->setZWrite(false);
132 matObject->setZFunc(CMaterial::always);
133 matObject->setDoubleSided(true);
135 // initialize stages of fixed pipeline
136 CRGBA constantCol1(85, 85, 85, 85);
137 CRGBA constantCol2(43, 43, 43, 43);
138 // stage 0
139 matObject->texConstantColor(0, constantCol1);
140 matObject->texEnvOpRGB(0, CMaterial::Modulate);
141 matObject->texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
142 matObject->texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor);
144 // stage 1
145 matObject->texConstantColor(1, constantCol2);
146 matObject->texEnvOpRGB(1, CMaterial::Mad);
147 matObject->texEnvArg0RGB(1, CMaterial::Texture, CMaterial::SrcColor);
148 matObject->texEnvArg1RGB(1, CMaterial::Constant, CMaterial::SrcColor);
149 matObject->texEnvArg2RGB(1, CMaterial::Previous, CMaterial::SrcColor);
151 // stage 2
152 matObject->texConstantColor(2, constantCol1);
153 matObject->texEnvOpRGB(2, CMaterial::Mad);
154 matObject->texEnvArg0RGB(2, CMaterial::Texture, CMaterial::SrcColor);
155 matObject->texEnvArg1RGB(2, CMaterial::Constant, CMaterial::SrcColor);
156 matObject->texEnvArg2RGB(2, CMaterial::Previous, CMaterial::SrcColor);
158 // stage 3
159 matObject->texConstantColor(3, constantCol2);
160 matObject->texEnvOpRGB(3, CMaterial::Mad);
161 matObject->texEnvArg0RGB(3, CMaterial::Texture, CMaterial::SrcColor);
162 matObject->texEnvArg1RGB(3, CMaterial::Constant, CMaterial::SrcColor);
163 matObject->texEnvArg2RGB(3, CMaterial::Previous, CMaterial::SrcColor);
165 // initialize linear blur material
166 _DisplayBlurMat = _Driver->createMaterial();
167 CMaterial * matObjectFinal = _DisplayBlurMat.getObjectPtr();
168 _DisplayBlurMat.initUnlit();
169 _DisplayBlurMat.setColor(CRGBA::White);
170 matObjectFinal->setBlend(true);
171 matObjectFinal->setBlendFunc(CMaterial::one, CMaterial::invsrccolor);
172 matObjectFinal->setZWrite(false);
173 matObjectFinal->setZFunc(CMaterial::always);
174 matObjectFinal->setDoubleSided(true);
176 // matObjectFinal->setTexture(0, _BlurFinalTex);
177 matObjectFinal->texEnvOpRGB(0, CMaterial::Modulate);
178 matObjectFinal->texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
179 matObjectFinal->texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor);
181 // initialize square blur material
182 _DisplaySquareBlurMat = _Driver->createMaterial();
183 matObjectFinal = _DisplaySquareBlurMat.getObjectPtr();
184 _DisplaySquareBlurMat.initUnlit();
185 _DisplaySquareBlurMat.setColor(CRGBA::White);
186 matObjectFinal->setBlend(true);
187 matObjectFinal->setBlendFunc(CMaterial::one, CMaterial::invsrccolor);
188 matObjectFinal->setZWrite(false);
189 matObjectFinal->setZFunc(CMaterial::always);
190 matObjectFinal->setDoubleSided(true);
192 matObjectFinal->texEnvOpRGB(0, CMaterial::Modulate);
193 matObjectFinal->texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
194 matObjectFinal->texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor);
196 matObjectFinal->texEnvOpRGB(1, CMaterial::Modulate);
197 matObjectFinal->texEnvArg0RGB(1, CMaterial::Texture, CMaterial::SrcColor);
198 matObjectFinal->texEnvArg1RGB(1, CMaterial::Previous, CMaterial::SrcColor);
200 // initialize quads
201 _BlurQuad.V0 = CVector(-1.f, -1.f, 0.5f);
202 _BlurQuad.V1 = CVector(1.f, -1.f, 0.5f);
203 _BlurQuad.V2 = CVector(1.f, 1.f, 0.5f);
204 _BlurQuad.V3 = CVector(-1.f, 1.f, 0.5f);
205 if (drv->textureCoordinateAlternativeMode())
207 _BlurQuad.Uv0 = CUV(0.f, 1.f);
208 _BlurQuad.Uv1 = CUV(1.f, 1.f);
209 _BlurQuad.Uv2 = CUV(1.f, 0.f);
210 _BlurQuad.Uv3 = CUV(0.f, 0.f);
212 else
214 _BlurQuad.Uv0 = CUV(0.f, 0.f);
215 _BlurQuad.Uv1 = CUV(1.f, 0.f);
216 _BlurQuad.Uv2 = CUV(1.f, 1.f);
217 _BlurQuad.Uv3 = CUV(0.f, 1.f);
220 _Init = true;
223 //-----------------------------------------------------------------------------------------------------------
225 void CBloomEffect::applyBloom()
227 if (!((CDriverUser *)_Driver)->getDriver()->supportBloomEffect())
228 return;
230 // don't activate bloom when PolygonMode is different from Filled
231 if (_Driver->getPolygonMode() != UDriver::Filled) return;
233 if (_Driver->getWindowWidth()==0 || _Driver->getWindowHeight()==0)
234 return;
236 if (!_Init)
237 init();
239 CDriverUser *dru = static_cast<CDriverUser *>(_Driver);
240 IDriver *drv = dru->getDriver();
242 // backup
243 bool fogEnabled = _Driver->fogEnabled();
244 _Driver->enableFog(false);
246 NL3D::ITexture *renderTarget = drv->getRenderTarget();
247 nlassert(renderTarget);
248 nlassert(renderTarget->isBloomTexture());
250 uint width = renderTarget->getWidth();
251 uint height = renderTarget->getHeight();
252 bool mode2D = static_cast<CTextureBloom *>(renderTarget)->isMode2D();
253 nlassert(renderTarget->getUploadFormat() == ITexture::Auto);
255 if (width >= 256) _BlurWidth = 256;
256 else _BlurWidth = raiseToNextPowerOf2(width) / 2;
257 if (height >= 256) _BlurHeight = 256;
258 else _BlurHeight = raiseToNextPowerOf2(height) / 2;
260 nlassert(!_BlurFinalTex);
261 _BlurFinalTex = _Driver->getRenderTargetManager().getRenderTarget(_BlurWidth, _BlurHeight, true);
262 nlassert(!_BlurHorizontalTex);
263 _BlurHorizontalTex = _Driver->getRenderTargetManager().getRenderTarget(_BlurWidth, _BlurHeight, true);
265 _DisplayBlurMat.getObjectPtr()->setTexture(0, _BlurFinalTex->getITexture());
266 _DisplaySquareBlurMat.getObjectPtr()->setTexture(0, _BlurFinalTex->getITexture());
267 _DisplaySquareBlurMat.getObjectPtr()->setTexture(1, _BlurFinalTex->getITexture());
269 CTextureUser texNull;
270 dru->setRenderTarget(texNull);
272 // Stretch original render target into blur texture
273 CTextureUser txt1(renderTarget);
274 CTextureUser txt2(_BlurFinalTex->getITexture());
275 CRect rect1(0, 0, width, height);
276 CRect rect2(0, 0, _BlurWidth, _BlurHeight);
277 dru->stretchRect(_Scene, txt1, rect1, txt2, rect2);
278 _Driver->setMatrixMode2D11();
280 // horizontal blur pass
281 doBlur(true);
283 // vertical blur pass
284 doBlur(false);
286 // apply blur with a blend operation
287 drv->setRenderTarget(renderTarget);
288 _Driver->setMatrixMode2D11();
289 applyBlur();
291 // cleanup material texture references
292 _DisplayBlurMat.getObjectPtr()->setTexture(0, NULL);
293 _DisplaySquareBlurMat.getObjectPtr()->setTexture(0, NULL);
294 _DisplaySquareBlurMat.getObjectPtr()->setTexture(1, NULL);
295 _BlurMat.getObjectPtr()->setTexture(0, NULL);
296 _BlurMat.getObjectPtr()->setTexture(1, NULL);
297 _BlurMat.getObjectPtr()->setTexture(2, NULL);
298 _BlurMat.getObjectPtr()->setTexture(3, NULL);
300 // restore
301 _Driver->enableFog(fogEnabled);
303 // recycle render targets
304 _Driver->getRenderTargetManager().recycleRenderTarget(_BlurFinalTex);
305 _BlurFinalTex = NULL;
306 _Driver->getRenderTargetManager().recycleRenderTarget(_BlurHorizontalTex);
307 _BlurHorizontalTex = NULL;
310 //-----------------------------------------------------------------------------------------------------------
312 void CBloomEffect::applyBlur()
314 NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver();
316 // initialize vertex program
317 drvInternal->activeVertexProgram(TextureOffsetVertexProgram);
318 drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f);
319 drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f);
321 // initialize blur material
322 UMaterial displayBlurMat;
323 if(_SquareBloom)
325 displayBlurMat = _DisplaySquareBlurMat;
327 else
329 displayBlurMat = _DisplayBlurMat;
331 CMaterial * matObjectFinal = displayBlurMat.getObjectPtr();
333 uint8 d = _DensityBloom;
334 CRGBA constCoeff(d, d, d, d);
335 matObjectFinal->texConstantColor(0, constCoeff);
337 // display quad
338 _Driver->drawQuad(_BlurQuad, displayBlurMat);
340 // disable vertex program
341 drvInternal->activeVertexProgram(NULL);
344 //-----------------------------------------------------------------------------------------------------------
346 void CBloomEffect::doBlur(bool horizontalBlur)
348 CVector2f blurVec;
349 ITexture * startTexture;
350 ITexture * endTexture;
352 // set displayed texture and render target texture of the pass
353 if (horizontalBlur)
355 blurVec = CVector2f(1.f, 0.f);
356 startTexture = _BlurFinalTex->getITexture();
357 endTexture = _BlurHorizontalTex->getITexture();
359 else
361 blurVec = CVector2f(0.f, 1.f);
362 startTexture = _BlurHorizontalTex->getITexture();
363 endTexture = _BlurFinalTex->getITexture();
366 NL3D::IDriver *drvInternal = ((CDriverUser *) _Driver)->getDriver();
367 CTextureUser txt(endTexture);
368 // initialize render target
369 if(!((CDriverUser *) _Driver)->setRenderTarget(txt, 0, 0, _BlurWidth, _BlurHeight))
371 nlwarning("setRenderTarget return false with blur texture for bloom effect\n");
372 return;
374 _Driver->setMatrixMode2D11();
376 // initialize vertex program
377 drvInternal->activeVertexProgram(TextureOffsetVertexProgram);
378 drvInternal->setUniform4f(IDriver::VertexProgram, 8, 255.f, 255.f, 255.f, 255.f);
379 drvInternal->setUniform4f(IDriver::VertexProgram, 9, 0.0f, 0.f, 0.f, 1.f);
381 // set several decal constants in order to obtain in the render target texture a mix of color
382 // of a texel and its neighbored texels on the axe of the pass.
383 float decalL, decal2L, decalR, decal2R;
384 if (drvInternal->textureCoordinateAlternativeMode())
386 if (horizontalBlur)
388 decalL = 0.5f;
389 decal2L = -0.5f;
390 decalR = 1.5f;
391 decal2R = 2.5f;
393 else
395 decalL = 0.0f;
396 decal2L = -1.0f;
397 decalR = 1.0f;
398 decal2R = 2.0f;
401 else
403 decalL = -0.5f;
404 decal2L = -1.5f;
405 decalR = 0.5f;
406 decal2R = 1.5f;
408 drvInternal->setUniform2f(IDriver::VertexProgram, 10, (decalR/(float)_BlurWidth)*blurVec.x, (decalR/(float)_BlurHeight)*blurVec.y);
409 drvInternal->setUniform2f(IDriver::VertexProgram, 11, (decal2R/(float)_BlurWidth)*blurVec.x, (decal2R/(float)_BlurHeight)*blurVec.y);
410 drvInternal->setUniform2f(IDriver::VertexProgram, 12, (decalL/(float)_BlurWidth)*blurVec.x, (decalL/(float)_BlurHeight)*blurVec.y);
411 drvInternal->setUniform2f(IDriver::VertexProgram, 13, (decal2L/(float)_BlurWidth)*blurVec.x, (decal2L/(float)_BlurHeight)*blurVec.y);
413 // initialize material textures
414 CMaterial * matObject = _BlurMat.getObjectPtr();
415 matObject->setTexture(0, startTexture);
416 matObject->setTexture(1, startTexture);
417 matObject->setTexture(2, startTexture);
418 matObject->setTexture(3, startTexture);
420 // display
421 _Driver->drawQuad(_BlurQuad, _BlurMat);
423 // disable render target and vertex program
424 drvInternal->activeVertexProgram(NULL);
425 CTextureUser cu;
426 ((CDriverUser *)_Driver)->setRenderTarget(cu, 0, 0, 0, 0);
429 }; // NL3D