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