PageRenderTime 61ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llrender/llpostprocess.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 580 lines | 338 code | 69 blank | 173 comment | 60 complexity | 85162453f5a9a6c349e42ff345713008 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llpostprocess.cpp
  3. * @brief LLPostProcess class implementation
  4. *
  5. * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llpostprocess.h"
  28. #include "llglslshader.h"
  29. #include "llsdserialize.h"
  30. #include "llrender.h"
  31. LLPostProcess * gPostProcess = NULL;
  32. static const unsigned int NOISE_SIZE = 512;
  33. /// CALCULATING LUMINANCE (Using NTSC lum weights)
  34. /// http://en.wikipedia.org/wiki/Luma_%28video%29
  35. static const float LUMINANCE_R = 0.299f;
  36. static const float LUMINANCE_G = 0.587f;
  37. static const float LUMINANCE_B = 0.114f;
  38. static const char * const XML_FILENAME = "postprocesseffects.xml";
  39. LLPostProcess::LLPostProcess(void) :
  40. initialized(false),
  41. mAllEffects(LLSD::emptyMap()),
  42. screenW(1), screenH(1)
  43. {
  44. mSceneRenderTexture = NULL ;
  45. mNoiseTexture = NULL ;
  46. mTempBloomTexture = NULL ;
  47. noiseTextureScale = 1.0f;
  48. /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.
  49. std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME));
  50. LL_DEBUGS2("AppInit", "Shaders") << "Loading PostProcess Effects settings from " << pathName << LL_ENDL;
  51. llifstream effectsXML(pathName);
  52. if (effectsXML)
  53. {
  54. LLPointer<LLSDParser> parser = new LLSDXMLParser();
  55. parser->parse(effectsXML, mAllEffects, LLSDSerialize::SIZE_UNLIMITED);
  56. }
  57. if (!mAllEffects.has("default"))
  58. {
  59. LLSD & defaultEffect = (mAllEffects["default"] = LLSD::emptyMap());
  60. defaultEffect["enable_night_vision"] = LLSD::Boolean(false);
  61. defaultEffect["enable_bloom"] = LLSD::Boolean(false);
  62. defaultEffect["enable_color_filter"] = LLSD::Boolean(false);
  63. /// NVG Defaults
  64. defaultEffect["brightness_multiplier"] = 3.0;
  65. defaultEffect["noise_size"] = 25.0;
  66. defaultEffect["noise_strength"] = 0.4;
  67. // TODO BTest potentially add this to tweaks?
  68. noiseTextureScale = 1.0f;
  69. /// Bloom Defaults
  70. defaultEffect["extract_low"] = 0.95;
  71. defaultEffect["extract_high"] = 1.0;
  72. defaultEffect["bloom_width"] = 2.25;
  73. defaultEffect["bloom_strength"] = 1.5;
  74. /// Color Filter Defaults
  75. defaultEffect["brightness"] = 1.0;
  76. defaultEffect["contrast"] = 1.0;
  77. defaultEffect["saturation"] = 1.0;
  78. LLSD& contrastBase = (defaultEffect["contrast_base"] = LLSD::emptyArray());
  79. contrastBase.append(1.0);
  80. contrastBase.append(1.0);
  81. contrastBase.append(1.0);
  82. contrastBase.append(0.5);
  83. }
  84. setSelectedEffect("default");
  85. */
  86. }
  87. LLPostProcess::~LLPostProcess(void)
  88. {
  89. invalidate() ;
  90. }
  91. // static
  92. void LLPostProcess::initClass(void)
  93. {
  94. //this will cause system to crash at second time login
  95. //if first time login fails due to network connection --- bao
  96. //***llassert_always(gPostProcess == NULL);
  97. //replaced by the following line:
  98. if(gPostProcess)
  99. return ;
  100. gPostProcess = new LLPostProcess();
  101. }
  102. // static
  103. void LLPostProcess::cleanupClass()
  104. {
  105. delete gPostProcess;
  106. gPostProcess = NULL;
  107. }
  108. void LLPostProcess::setSelectedEffect(std::string const & effectName)
  109. {
  110. mSelectedEffectName = effectName;
  111. static_cast<LLSD &>(tweaks) = mAllEffects[effectName];
  112. }
  113. void LLPostProcess::saveEffect(std::string const & effectName)
  114. {
  115. /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.
  116. mAllEffects[effectName] = tweaks;
  117. std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME));
  118. //llinfos << "Saving PostProcess Effects settings to " << pathName << llendl;
  119. llofstream effectsXML(pathName);
  120. LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
  121. formatter->format(mAllEffects, effectsXML);
  122. */
  123. }
  124. void LLPostProcess::invalidate()
  125. {
  126. mSceneRenderTexture = NULL ;
  127. mNoiseTexture = NULL ;
  128. mTempBloomTexture = NULL ;
  129. initialized = FALSE ;
  130. }
  131. void LLPostProcess::apply(unsigned int width, unsigned int height)
  132. {
  133. if (!initialized || width != screenW || height != screenH){
  134. initialize(width, height);
  135. }
  136. if (shadersEnabled()){
  137. doEffects();
  138. }
  139. }
  140. void LLPostProcess::initialize(unsigned int width, unsigned int height)
  141. {
  142. screenW = width;
  143. screenH = height;
  144. createTexture(mSceneRenderTexture, screenW, screenH);
  145. initialized = true;
  146. checkError();
  147. createNightVisionShader();
  148. createBloomShader();
  149. createColorFilterShader();
  150. checkError();
  151. }
  152. inline bool LLPostProcess::shadersEnabled(void)
  153. {
  154. return (tweaks.useColorFilter().asBoolean() ||
  155. tweaks.useNightVisionShader().asBoolean() ||
  156. tweaks.useBloomShader().asBoolean() );
  157. }
  158. void LLPostProcess::applyShaders(void)
  159. {
  160. if (tweaks.useColorFilter()){
  161. applyColorFilterShader();
  162. checkError();
  163. }
  164. if (tweaks.useNightVisionShader()){
  165. /// If any of the above shaders have been called update the frame buffer;
  166. if (tweaks.useColorFilter())
  167. {
  168. U32 tex = mSceneRenderTexture->getTexName() ;
  169. copyFrameBuffer(tex, screenW, screenH);
  170. }
  171. applyNightVisionShader();
  172. checkError();
  173. }
  174. if (tweaks.useBloomShader()){
  175. /// If any of the above shaders have been called update the frame buffer;
  176. if (tweaks.useColorFilter().asBoolean() || tweaks.useNightVisionShader().asBoolean())
  177. {
  178. U32 tex = mSceneRenderTexture->getTexName() ;
  179. copyFrameBuffer(tex, screenW, screenH);
  180. }
  181. applyBloomShader();
  182. checkError();
  183. }
  184. }
  185. void LLPostProcess::applyColorFilterShader(void)
  186. {
  187. /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.
  188. gPostColorFilterProgram.bind();
  189. gGL.getTexUnit(0)->activate();
  190. gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE);
  191. gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture);
  192. getShaderUniforms(colorFilterUniforms, gPostColorFilterProgram.mProgramObject);
  193. glUniform1iARB(colorFilterUniforms["RenderTexture"], 0);
  194. glUniform1fARB(colorFilterUniforms["brightness"], tweaks.getBrightness());
  195. glUniform1fARB(colorFilterUniforms["contrast"], tweaks.getContrast());
  196. float baseI = (tweaks.getContrastBaseR() + tweaks.getContrastBaseG() + tweaks.getContrastBaseB()) / 3.0f;
  197. baseI = tweaks.getContrastBaseIntensity() / ((baseI < 0.001f) ? 0.001f : baseI);
  198. float baseR = tweaks.getContrastBaseR() * baseI;
  199. float baseG = tweaks.getContrastBaseG() * baseI;
  200. float baseB = tweaks.getContrastBaseB() * baseI;
  201. glUniform3fARB(colorFilterUniforms["contrastBase"], baseR, baseG, baseB);
  202. glUniform1fARB(colorFilterUniforms["saturation"], tweaks.getSaturation());
  203. glUniform3fARB(colorFilterUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B);
  204. LLGLEnable blend(GL_BLEND);
  205. gGL.setSceneBlendType(LLRender::BT_REPLACE);
  206. LLGLDepthTest depth(GL_FALSE);
  207. /// Draw a screen space quad
  208. drawOrthoQuad(screenW, screenH, QUAD_NORMAL);
  209. gPostColorFilterProgram.unbind();
  210. */
  211. }
  212. void LLPostProcess::createColorFilterShader(void)
  213. {
  214. /// Define uniform names
  215. colorFilterUniforms["RenderTexture"] = 0;
  216. colorFilterUniforms["brightness"] = 0;
  217. colorFilterUniforms["contrast"] = 0;
  218. colorFilterUniforms["contrastBase"] = 0;
  219. colorFilterUniforms["saturation"] = 0;
  220. colorFilterUniforms["lumWeights"] = 0;
  221. }
  222. void LLPostProcess::applyNightVisionShader(void)
  223. {
  224. /* Do nothing. Needs to be updated to use our current shader system, and to work with the move into llrender.
  225. gPostNightVisionProgram.bind();
  226. gGL.getTexUnit(0)->activate();
  227. gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE);
  228. getShaderUniforms(nightVisionUniforms, gPostNightVisionProgram.mProgramObject);
  229. gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture);
  230. glUniform1iARB(nightVisionUniforms["RenderTexture"], 0);
  231. gGL.getTexUnit(1)->activate();
  232. gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE);
  233. gGL.getTexUnit(1)->bindManual(LLTexUnit::TT_TEXTURE, noiseTexture);
  234. glUniform1iARB(nightVisionUniforms["NoiseTexture"], 1);
  235. glUniform1fARB(nightVisionUniforms["brightMult"], tweaks.getBrightMult());
  236. glUniform1fARB(nightVisionUniforms["noiseStrength"], tweaks.getNoiseStrength());
  237. noiseTextureScale = 0.01f + ((101.f - tweaks.getNoiseSize()) / 100.f);
  238. noiseTextureScale *= (screenH / NOISE_SIZE);
  239. glUniform3fARB(nightVisionUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B);
  240. LLGLEnable blend(GL_BLEND);
  241. gGL.setSceneBlendType(LLRender::BT_REPLACE);
  242. LLGLDepthTest depth(GL_FALSE);
  243. /// Draw a screen space quad
  244. drawOrthoQuad(screenW, screenH, QUAD_NOISE);
  245. gPostNightVisionProgram.unbind();
  246. gGL.getTexUnit(0)->activate();
  247. */
  248. }
  249. void LLPostProcess::createNightVisionShader(void)
  250. {
  251. /// Define uniform names
  252. nightVisionUniforms["RenderTexture"] = 0;
  253. nightVisionUniforms["NoiseTexture"] = 0;
  254. nightVisionUniforms["brightMult"] = 0;
  255. nightVisionUniforms["noiseStrength"] = 0;
  256. nightVisionUniforms["lumWeights"] = 0;
  257. createNoiseTexture(mNoiseTexture);
  258. }
  259. void LLPostProcess::applyBloomShader(void)
  260. {
  261. }
  262. void LLPostProcess::createBloomShader(void)
  263. {
  264. createTexture(mTempBloomTexture, unsigned(screenW * 0.5), unsigned(screenH * 0.5));
  265. /// Create Bloom Extract Shader
  266. bloomExtractUniforms["RenderTexture"] = 0;
  267. bloomExtractUniforms["extractLow"] = 0;
  268. bloomExtractUniforms["extractHigh"] = 0;
  269. bloomExtractUniforms["lumWeights"] = 0;
  270. /// Create Bloom Blur Shader
  271. bloomBlurUniforms["RenderTexture"] = 0;
  272. bloomBlurUniforms["bloomStrength"] = 0;
  273. bloomBlurUniforms["texelSize"] = 0;
  274. bloomBlurUniforms["blurDirection"] = 0;
  275. bloomBlurUniforms["blurWidth"] = 0;
  276. }
  277. void LLPostProcess::getShaderUniforms(glslUniforms & uniforms, GLhandleARB & prog)
  278. {
  279. /// Find uniform locations and insert into map
  280. std::map<const char *, GLuint>::iterator i;
  281. for (i = uniforms.begin(); i != uniforms.end(); ++i){
  282. i->second = glGetUniformLocationARB(prog, i->first);
  283. }
  284. }
  285. void LLPostProcess::doEffects(void)
  286. {
  287. /// Save GL State
  288. glPushAttrib(GL_ALL_ATTRIB_BITS);
  289. glPushClientAttrib(GL_ALL_ATTRIB_BITS);
  290. /// Copy the screen buffer to the render texture
  291. {
  292. U32 tex = mSceneRenderTexture->getTexName() ;
  293. copyFrameBuffer(tex, screenW, screenH);
  294. }
  295. /// Clear the frame buffer.
  296. glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  297. glClear(GL_COLOR_BUFFER_BIT);
  298. /// Change to an orthogonal view
  299. viewOrthogonal(screenW, screenH);
  300. checkError();
  301. applyShaders();
  302. LLGLSLShader::bindNoShader();
  303. checkError();
  304. /// Change to a perspective view
  305. viewPerspective();
  306. /// Reset GL State
  307. glPopClientAttrib();
  308. glPopAttrib();
  309. checkError();
  310. }
  311. void LLPostProcess::copyFrameBuffer(U32 & texture, unsigned int width, unsigned int height)
  312. {
  313. gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture);
  314. glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 0, 0, width, height, 0);
  315. }
  316. void LLPostProcess::drawOrthoQuad(unsigned int width, unsigned int height, QuadType type)
  317. {
  318. #if 0
  319. float noiseX = 0.f;
  320. float noiseY = 0.f;
  321. float screenRatio = 1.0f;
  322. if (type == QUAD_NOISE){
  323. noiseX = ((float) rand() / (float) RAND_MAX);
  324. noiseY = ((float) rand() / (float) RAND_MAX);
  325. screenRatio = (float) width / (float) height;
  326. }
  327. glBegin(GL_QUADS);
  328. if (type != QUAD_BLOOM_EXTRACT){
  329. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height);
  330. } else {
  331. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height * 2.0f);
  332. }
  333. if (type == QUAD_NOISE){
  334. glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
  335. noiseX,
  336. noiseTextureScale + noiseY);
  337. } else if (type == QUAD_BLOOM_COMBINE){
  338. glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, (GLfloat) height * 0.5f);
  339. }
  340. glVertex2f(0.f, (GLfloat) screenH - height);
  341. if (type != QUAD_BLOOM_EXTRACT){
  342. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f);
  343. } else {
  344. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f);
  345. }
  346. if (type == QUAD_NOISE){
  347. glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
  348. noiseX,
  349. noiseY);
  350. } else if (type == QUAD_BLOOM_COMBINE){
  351. glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, 0.f);
  352. }
  353. glVertex2f(0.f, (GLfloat) height + (screenH - height));
  354. if (type != QUAD_BLOOM_EXTRACT){
  355. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, 0.f);
  356. } else {
  357. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, 0.f);
  358. }
  359. if (type == QUAD_NOISE){
  360. glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
  361. screenRatio * noiseTextureScale + noiseX,
  362. noiseY);
  363. } else if (type == QUAD_BLOOM_COMBINE){
  364. glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, 0.f);
  365. }
  366. glVertex2f((GLfloat) width, (GLfloat) height + (screenH - height));
  367. if (type != QUAD_BLOOM_EXTRACT){
  368. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, (GLfloat) height);
  369. } else {
  370. glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, (GLfloat) height * 2.0f);
  371. }
  372. if (type == QUAD_NOISE){
  373. glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
  374. screenRatio * noiseTextureScale + noiseX,
  375. noiseTextureScale + noiseY);
  376. } else if (type == QUAD_BLOOM_COMBINE){
  377. glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, (GLfloat) height * 0.5f);
  378. }
  379. glVertex2f((GLfloat) width, (GLfloat) screenH - height);
  380. glEnd();
  381. #endif
  382. }
  383. void LLPostProcess::viewOrthogonal(unsigned int width, unsigned int height)
  384. {
  385. gGL.matrixMode(LLRender::MM_PROJECTION);
  386. gGL.pushMatrix();
  387. gGL.loadIdentity();
  388. gGL.ortho( 0.f, (GLdouble) width , (GLdouble) height , 0.f, -1.f, 1.f );
  389. gGL.matrixMode(LLRender::MM_MODELVIEW);
  390. gGL.pushMatrix();
  391. gGL.loadIdentity();
  392. }
  393. void LLPostProcess::viewPerspective(void)
  394. {
  395. gGL.matrixMode(LLRender::MM_PROJECTION);
  396. gGL.popMatrix();
  397. gGL.matrixMode(LLRender::MM_MODELVIEW);
  398. gGL.popMatrix();
  399. }
  400. void LLPostProcess::changeOrthogonal(unsigned int width, unsigned int height)
  401. {
  402. viewPerspective();
  403. viewOrthogonal(width, height);
  404. }
  405. void LLPostProcess::createTexture(LLPointer<LLImageGL>& texture, unsigned int width, unsigned int height)
  406. {
  407. std::vector<GLubyte> data(width * height * 4, 0) ;
  408. texture = new LLImageGL(FALSE) ;
  409. if(texture->createGLTexture())
  410. {
  411. gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture->getTexName());
  412. glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, width, height, 0,
  413. GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
  414. gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
  415. gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
  416. }
  417. }
  418. void LLPostProcess::createNoiseTexture(LLPointer<LLImageGL>& texture)
  419. {
  420. std::vector<GLubyte> buffer(NOISE_SIZE * NOISE_SIZE);
  421. for (unsigned int i = 0; i < NOISE_SIZE; i++){
  422. for (unsigned int k = 0; k < NOISE_SIZE; k++){
  423. buffer[(i * NOISE_SIZE) + k] = (GLubyte)((double) rand() / ((double) RAND_MAX + 1.f) * 255.f);
  424. }
  425. }
  426. texture = new LLImageGL(FALSE) ;
  427. if(texture->createGLTexture())
  428. {
  429. gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, texture->getTexName());
  430. LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]);
  431. gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
  432. gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP);
  433. }
  434. }
  435. bool LLPostProcess::checkError(void)
  436. {
  437. GLenum glErr;
  438. bool retCode = false;
  439. glErr = glGetError();
  440. while (glErr != GL_NO_ERROR)
  441. {
  442. // shaderErrorLog << (const char *) gluErrorString(glErr) << std::endl;
  443. char const * err_str_raw = (const char *) gluErrorString(glErr);
  444. if(err_str_raw == NULL)
  445. {
  446. std::ostringstream err_builder;
  447. err_builder << "unknown error number " << glErr;
  448. mShaderErrorString = err_builder.str();
  449. }
  450. else
  451. {
  452. mShaderErrorString = err_str_raw;
  453. }
  454. retCode = true;
  455. glErr = glGetError();
  456. }
  457. return retCode;
  458. }
  459. void LLPostProcess::checkShaderError(GLhandleARB shader)
  460. {
  461. GLint infologLength = 0;
  462. GLint charsWritten = 0;
  463. GLchar *infoLog;
  464. checkError(); // Check for OpenGL errors
  465. glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);
  466. checkError(); // Check for OpenGL errors
  467. if (infologLength > 0)
  468. {
  469. infoLog = (GLchar *)malloc(infologLength);
  470. if (infoLog == NULL)
  471. {
  472. /// Could not allocate infolog buffer
  473. return;
  474. }
  475. glGetInfoLogARB(shader, infologLength, &charsWritten, infoLog);
  476. // shaderErrorLog << (char *) infoLog << std::endl;
  477. mShaderErrorString = (char *) infoLog;
  478. free(infoLog);
  479. }
  480. checkError(); // Check for OpenGL errors
  481. }