PageRenderTime 39ms CodeModel.GetById 9ms app.highlight 25ms RepoModel.GetById 1ms 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
  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
 27#include "linden_common.h"
 28
 29#include "llpostprocess.h"
 30#include "llglslshader.h"
 31#include "llsdserialize.h"
 32#include "llrender.h"
 33
 34
 35LLPostProcess * gPostProcess = NULL;
 36
 37
 38static const unsigned int NOISE_SIZE = 512;
 39
 40/// CALCULATING LUMINANCE (Using NTSC lum weights)
 41/// http://en.wikipedia.org/wiki/Luma_%28video%29
 42static const float LUMINANCE_R = 0.299f;
 43static const float LUMINANCE_G = 0.587f;
 44static const float LUMINANCE_B = 0.114f;
 45
 46static const char * const XML_FILENAME = "postprocesseffects.xml";
 47
 48LLPostProcess::LLPostProcess(void) : 
 49					initialized(false),  
 50					mAllEffects(LLSD::emptyMap()),
 51					screenW(1), screenH(1)
 52{
 53	mSceneRenderTexture = NULL ; 
 54	mNoiseTexture = NULL ;
 55	mTempBloomTexture = NULL ;
 56
 57	noiseTextureScale = 1.0f;
 58					
 59	/*  Do nothing.  Needs to be updated to use our current shader system, and to work with the move into llrender.
 60	std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME));
 61	LL_DEBUGS2("AppInit", "Shaders") << "Loading PostProcess Effects settings from " << pathName << LL_ENDL;
 62
 63	llifstream effectsXML(pathName);
 64
 65	if (effectsXML)
 66	{
 67		LLPointer<LLSDParser> parser = new LLSDXMLParser();
 68
 69		parser->parse(effectsXML, mAllEffects, LLSDSerialize::SIZE_UNLIMITED);
 70	}
 71
 72	if (!mAllEffects.has("default"))
 73	{
 74		LLSD & defaultEffect = (mAllEffects["default"] = LLSD::emptyMap());
 75
 76		defaultEffect["enable_night_vision"] = LLSD::Boolean(false);
 77		defaultEffect["enable_bloom"] = LLSD::Boolean(false);
 78		defaultEffect["enable_color_filter"] = LLSD::Boolean(false);
 79
 80		/// NVG Defaults
 81		defaultEffect["brightness_multiplier"] = 3.0;
 82		defaultEffect["noise_size"] = 25.0;
 83		defaultEffect["noise_strength"] = 0.4;
 84
 85		// TODO BTest potentially add this to tweaks?
 86		noiseTextureScale = 1.0f;
 87		
 88		/// Bloom Defaults
 89		defaultEffect["extract_low"] = 0.95;
 90		defaultEffect["extract_high"] = 1.0;
 91		defaultEffect["bloom_width"] = 2.25;
 92		defaultEffect["bloom_strength"] = 1.5;
 93
 94		/// Color Filter Defaults
 95		defaultEffect["brightness"] = 1.0;
 96		defaultEffect["contrast"] = 1.0;
 97		defaultEffect["saturation"] = 1.0;
 98
 99		LLSD& contrastBase = (defaultEffect["contrast_base"] = LLSD::emptyArray());
100		contrastBase.append(1.0);
101		contrastBase.append(1.0);
102		contrastBase.append(1.0);
103		contrastBase.append(0.5);
104	}
105
106	setSelectedEffect("default");
107	*/
108}
109
110LLPostProcess::~LLPostProcess(void)
111{
112	invalidate() ;
113}
114
115// static
116void LLPostProcess::initClass(void)
117{
118	//this will cause system to crash at second time login
119	//if first time login fails due to network connection --- bao
120	//***llassert_always(gPostProcess == NULL);
121	//replaced by the following line:
122	if(gPostProcess)
123		return ;
124	
125	
126	gPostProcess = new LLPostProcess();
127}
128
129// static
130void LLPostProcess::cleanupClass()
131{
132	delete gPostProcess;
133	gPostProcess = NULL;
134}
135
136void LLPostProcess::setSelectedEffect(std::string const & effectName)
137{
138	mSelectedEffectName = effectName;
139	static_cast<LLSD &>(tweaks) = mAllEffects[effectName];
140}
141
142void LLPostProcess::saveEffect(std::string const & effectName)
143{
144	/*  Do nothing.  Needs to be updated to use our current shader system, and to work with the move into llrender.
145	mAllEffects[effectName] = tweaks;
146
147	std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight", XML_FILENAME));
148	//llinfos << "Saving PostProcess Effects settings to " << pathName << llendl;
149
150	llofstream effectsXML(pathName);
151
152	LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
153
154	formatter->format(mAllEffects, effectsXML);
155	*/
156}
157void LLPostProcess::invalidate()
158{
159	mSceneRenderTexture = NULL ;
160	mNoiseTexture = NULL ;
161	mTempBloomTexture = NULL ;
162	initialized = FALSE ;
163}
164
165void LLPostProcess::apply(unsigned int width, unsigned int height)
166{
167	if (!initialized || width != screenW || height != screenH){
168		initialize(width, height);
169	}
170	if (shadersEnabled()){
171		doEffects();
172	}
173}
174
175void LLPostProcess::initialize(unsigned int width, unsigned int height)
176{
177	screenW = width;
178	screenH = height;
179	createTexture(mSceneRenderTexture, screenW, screenH);
180	initialized = true;
181
182	checkError();
183	createNightVisionShader();
184	createBloomShader();
185	createColorFilterShader();
186	checkError();
187}
188
189inline bool LLPostProcess::shadersEnabled(void)
190{
191	return (tweaks.useColorFilter().asBoolean() ||
192			tweaks.useNightVisionShader().asBoolean() ||
193			tweaks.useBloomShader().asBoolean() );
194
195}
196
197void LLPostProcess::applyShaders(void)
198{
199	if (tweaks.useColorFilter()){
200		applyColorFilterShader();
201		checkError();
202	}	
203	if (tweaks.useNightVisionShader()){
204		/// If any of the above shaders have been called update the frame buffer;
205		if (tweaks.useColorFilter())
206		{
207			U32 tex = mSceneRenderTexture->getTexName() ;
208			copyFrameBuffer(tex, screenW, screenH);
209		}
210		applyNightVisionShader();
211		checkError();
212	}
213	if (tweaks.useBloomShader()){
214		/// If any of the above shaders have been called update the frame buffer;
215		if (tweaks.useColorFilter().asBoolean() || tweaks.useNightVisionShader().asBoolean())
216		{
217			U32 tex = mSceneRenderTexture->getTexName() ;
218			copyFrameBuffer(tex, screenW, screenH);
219		}
220		applyBloomShader();
221		checkError();
222	}
223}
224
225void LLPostProcess::applyColorFilterShader(void)
226{	
227	/*  Do nothing.  Needs to be updated to use our current shader system, and to work with the move into llrender.
228	gPostColorFilterProgram.bind();
229
230	gGL.getTexUnit(0)->activate();
231	gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE);
232
233	gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture);
234
235	getShaderUniforms(colorFilterUniforms, gPostColorFilterProgram.mProgramObject);
236	glUniform1iARB(colorFilterUniforms["RenderTexture"], 0);
237	glUniform1fARB(colorFilterUniforms["brightness"], tweaks.getBrightness());
238	glUniform1fARB(colorFilterUniforms["contrast"], tweaks.getContrast());
239	float baseI = (tweaks.getContrastBaseR() + tweaks.getContrastBaseG() + tweaks.getContrastBaseB()) / 3.0f;
240	baseI = tweaks.getContrastBaseIntensity() / ((baseI < 0.001f) ? 0.001f : baseI);
241	float baseR = tweaks.getContrastBaseR() * baseI;
242	float baseG = tweaks.getContrastBaseG() * baseI;
243	float baseB = tweaks.getContrastBaseB() * baseI;
244	glUniform3fARB(colorFilterUniforms["contrastBase"], baseR, baseG, baseB);
245	glUniform1fARB(colorFilterUniforms["saturation"], tweaks.getSaturation());
246	glUniform3fARB(colorFilterUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B);
247	
248	LLGLEnable blend(GL_BLEND);
249	gGL.setSceneBlendType(LLRender::BT_REPLACE);
250	LLGLDepthTest depth(GL_FALSE);
251		
252	/// Draw a screen space quad
253	drawOrthoQuad(screenW, screenH, QUAD_NORMAL);
254	gPostColorFilterProgram.unbind();
255	*/
256}
257
258void LLPostProcess::createColorFilterShader(void)
259{
260	/// Define uniform names
261	colorFilterUniforms["RenderTexture"] = 0;
262	colorFilterUniforms["brightness"] = 0;
263	colorFilterUniforms["contrast"] = 0;
264	colorFilterUniforms["contrastBase"] = 0;
265	colorFilterUniforms["saturation"] = 0;
266	colorFilterUniforms["lumWeights"] = 0;
267}
268
269void LLPostProcess::applyNightVisionShader(void)
270{	
271	/*  Do nothing.  Needs to be updated to use our current shader system, and to work with the move into llrender.
272	gPostNightVisionProgram.bind();
273
274	gGL.getTexUnit(0)->activate();
275	gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE);
276
277	getShaderUniforms(nightVisionUniforms, gPostNightVisionProgram.mProgramObject);
278	gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, sceneRenderTexture);
279	glUniform1iARB(nightVisionUniforms["RenderTexture"], 0);
280
281	gGL.getTexUnit(1)->activate();
282	gGL.getTexUnit(1)->enable(LLTexUnit::TT_TEXTURE);	
283
284	gGL.getTexUnit(1)->bindManual(LLTexUnit::TT_TEXTURE, noiseTexture);
285	glUniform1iARB(nightVisionUniforms["NoiseTexture"], 1);
286
287	
288	glUniform1fARB(nightVisionUniforms["brightMult"], tweaks.getBrightMult());
289	glUniform1fARB(nightVisionUniforms["noiseStrength"], tweaks.getNoiseStrength());
290	noiseTextureScale = 0.01f + ((101.f - tweaks.getNoiseSize()) / 100.f);
291	noiseTextureScale *= (screenH / NOISE_SIZE);
292
293
294	glUniform3fARB(nightVisionUniforms["lumWeights"], LUMINANCE_R, LUMINANCE_G, LUMINANCE_B);
295	
296	LLGLEnable blend(GL_BLEND);
297	gGL.setSceneBlendType(LLRender::BT_REPLACE);
298	LLGLDepthTest depth(GL_FALSE);
299		
300	/// Draw a screen space quad
301	drawOrthoQuad(screenW, screenH, QUAD_NOISE);
302	gPostNightVisionProgram.unbind();
303	gGL.getTexUnit(0)->activate();
304	*/
305}
306
307void LLPostProcess::createNightVisionShader(void)
308{
309	/// Define uniform names
310	nightVisionUniforms["RenderTexture"] = 0;
311	nightVisionUniforms["NoiseTexture"] = 0;
312	nightVisionUniforms["brightMult"] = 0;	
313	nightVisionUniforms["noiseStrength"] = 0;
314	nightVisionUniforms["lumWeights"] = 0;	
315
316	createNoiseTexture(mNoiseTexture);
317}
318
319void LLPostProcess::applyBloomShader(void)
320{
321
322}
323
324void LLPostProcess::createBloomShader(void)
325{
326	createTexture(mTempBloomTexture, unsigned(screenW * 0.5), unsigned(screenH * 0.5));
327
328	/// Create Bloom Extract Shader
329	bloomExtractUniforms["RenderTexture"] = 0;
330	bloomExtractUniforms["extractLow"] = 0;
331	bloomExtractUniforms["extractHigh"] = 0;	
332	bloomExtractUniforms["lumWeights"] = 0;	
333	
334	/// Create Bloom Blur Shader
335	bloomBlurUniforms["RenderTexture"] = 0;
336	bloomBlurUniforms["bloomStrength"] = 0;	
337	bloomBlurUniforms["texelSize"] = 0;
338	bloomBlurUniforms["blurDirection"] = 0;
339	bloomBlurUniforms["blurWidth"] = 0;
340}
341
342void LLPostProcess::getShaderUniforms(glslUniforms & uniforms, GLhandleARB & prog)
343{
344	/// Find uniform locations and insert into map	
345	std::map<const char *, GLuint>::iterator i;
346	for (i  = uniforms.begin(); i != uniforms.end(); ++i){
347		i->second = glGetUniformLocationARB(prog, i->first);
348	}
349}
350
351void LLPostProcess::doEffects(void)
352{
353	/// Save GL State
354	glPushAttrib(GL_ALL_ATTRIB_BITS);
355	glPushClientAttrib(GL_ALL_ATTRIB_BITS);
356
357	/// Copy the screen buffer to the render texture
358	{
359		U32 tex = mSceneRenderTexture->getTexName() ;
360		copyFrameBuffer(tex, screenW, screenH);
361	}
362
363	/// Clear the frame buffer.
364	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
365	glClear(GL_COLOR_BUFFER_BIT);
366	
367	/// Change to an orthogonal view
368	viewOrthogonal(screenW, screenH);
369	
370	checkError();
371	applyShaders();
372	
373	LLGLSLShader::bindNoShader();
374	checkError();
375
376	/// Change to a perspective view
377	viewPerspective();	
378
379	/// Reset GL State
380	glPopClientAttrib();
381	glPopAttrib();
382	checkError();
383}
384
385void LLPostProcess::copyFrameBuffer(U32 & texture, unsigned int width, unsigned int height)
386{
387	gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture);
388	glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 0, 0, width, height, 0);
389}
390
391void LLPostProcess::drawOrthoQuad(unsigned int width, unsigned int height, QuadType type)
392{
393#if 0
394	float noiseX = 0.f;
395	float noiseY = 0.f;
396	float screenRatio = 1.0f;
397
398	if (type == QUAD_NOISE){
399		noiseX = ((float) rand() / (float) RAND_MAX);
400		noiseY = ((float) rand() / (float) RAND_MAX);
401		screenRatio = (float) width / (float) height;
402	}
403	
404
405	glBegin(GL_QUADS);
406		if (type != QUAD_BLOOM_EXTRACT){
407			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height);
408		} else {
409			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, (GLfloat) height * 2.0f);
410		}
411		if (type == QUAD_NOISE){
412			glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
413									noiseX,
414									noiseTextureScale + noiseY);
415		} else if (type == QUAD_BLOOM_COMBINE){
416			glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, (GLfloat) height * 0.5f);
417		}
418		glVertex2f(0.f, (GLfloat) screenH - height);
419
420		if (type != QUAD_BLOOM_EXTRACT){
421			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f);
422		} else {
423			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.f, 0.f);
424		}
425		if (type == QUAD_NOISE){
426			glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
427									noiseX,
428									noiseY);
429		} else if (type == QUAD_BLOOM_COMBINE){
430			glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.f, 0.f);
431		}
432		glVertex2f(0.f, (GLfloat) height + (screenH - height));
433
434		
435		if (type != QUAD_BLOOM_EXTRACT){
436			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, 0.f);
437		} else {
438			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, 0.f);
439		}
440		if (type == QUAD_NOISE){
441			glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
442									screenRatio * noiseTextureScale + noiseX,
443									noiseY);
444		} else if (type == QUAD_BLOOM_COMBINE){
445			glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, 0.f);
446		}
447		glVertex2f((GLfloat) width, (GLfloat) height + (screenH - height));
448
449		
450		if (type != QUAD_BLOOM_EXTRACT){
451			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width, (GLfloat) height);
452		} else {
453			glMultiTexCoord2fARB(GL_TEXTURE0_ARB, (GLfloat) width * 2.0f, (GLfloat) height * 2.0f);
454		}
455		if (type == QUAD_NOISE){
456			glMultiTexCoord2fARB(GL_TEXTURE1_ARB,
457									screenRatio * noiseTextureScale + noiseX,
458									noiseTextureScale + noiseY);
459		} else if (type == QUAD_BLOOM_COMBINE){
460			glMultiTexCoord2fARB(GL_TEXTURE1_ARB, (GLfloat) width * 0.5f, (GLfloat) height * 0.5f);
461		}
462		glVertex2f((GLfloat) width, (GLfloat) screenH - height);
463	glEnd();
464#endif
465}
466
467void LLPostProcess::viewOrthogonal(unsigned int width, unsigned int height)
468{
469	gGL.matrixMode(LLRender::MM_PROJECTION);
470	gGL.pushMatrix();
471	gGL.loadIdentity();
472	gGL.ortho( 0.f, (GLdouble) width , (GLdouble) height , 0.f, -1.f, 1.f );
473	gGL.matrixMode(LLRender::MM_MODELVIEW);
474	gGL.pushMatrix();
475	gGL.loadIdentity();
476}
477
478void LLPostProcess::viewPerspective(void)
479{
480	gGL.matrixMode(LLRender::MM_PROJECTION);
481	gGL.popMatrix();
482	gGL.matrixMode(LLRender::MM_MODELVIEW);
483	gGL.popMatrix();
484}
485
486void LLPostProcess::changeOrthogonal(unsigned int width, unsigned int height)
487{
488	viewPerspective();
489	viewOrthogonal(width, height);
490}
491
492void LLPostProcess::createTexture(LLPointer<LLImageGL>& texture, unsigned int width, unsigned int height)
493{
494	std::vector<GLubyte> data(width * height * 4, 0) ;
495
496	texture = new LLImageGL(FALSE) ;	
497	if(texture->createGLTexture())
498	{
499		gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_RECT_TEXTURE, texture->getTexName());
500		glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, width, height, 0,
501			GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
502		gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
503		gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
504	}
505}
506
507void LLPostProcess::createNoiseTexture(LLPointer<LLImageGL>& texture)
508{	
509	std::vector<GLubyte> buffer(NOISE_SIZE * NOISE_SIZE);
510	for (unsigned int i = 0; i < NOISE_SIZE; i++){
511		for (unsigned int k = 0; k < NOISE_SIZE; k++){
512			buffer[(i * NOISE_SIZE) + k] = (GLubyte)((double) rand() / ((double) RAND_MAX + 1.f) * 255.f);
513		}
514	}
515
516	texture = new LLImageGL(FALSE) ;
517	if(texture->createGLTexture())
518	{
519		gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, texture->getTexName());
520		LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, NOISE_SIZE, NOISE_SIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, &buffer[0]);
521		gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
522		gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP);
523	}
524}
525
526bool LLPostProcess::checkError(void)
527{
528	GLenum glErr;
529    bool    retCode = false;
530
531    glErr = glGetError();
532    while (glErr != GL_NO_ERROR)
533    {
534		// shaderErrorLog << (const char *) gluErrorString(glErr) << std::endl;
535		char const * err_str_raw = (const char *) gluErrorString(glErr);
536
537		if(err_str_raw == NULL)
538		{
539			std::ostringstream err_builder;
540			err_builder << "unknown error number " << glErr;
541			mShaderErrorString = err_builder.str();
542		}
543		else
544		{
545			mShaderErrorString = err_str_raw;
546		}
547
548        retCode = true;
549        glErr = glGetError();
550    }
551    return retCode;
552}
553
554void LLPostProcess::checkShaderError(GLhandleARB shader)
555{
556    GLint infologLength = 0;
557    GLint charsWritten  = 0;
558    GLchar *infoLog;
559
560    checkError();  // Check for OpenGL errors
561
562    glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength);
563
564    checkError();  // Check for OpenGL errors
565
566    if (infologLength > 0)
567    {
568        infoLog = (GLchar *)malloc(infologLength);
569        if (infoLog == NULL)
570        {
571            /// Could not allocate infolog buffer
572            return;
573        }
574        glGetInfoLogARB(shader, infologLength, &charsWritten, infoLog);
575		// shaderErrorLog << (char *) infoLog << std::endl;
576		mShaderErrorString = (char *) infoLog;
577        free(infoLog);
578    }
579    checkError();  // Check for OpenGL errors
580}