llpostprocess.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:17k
源码类别:

游戏引擎

开发平台:

C++ Builder

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