1 // SuperTuxKart - a fun racing game with go-kart
2 // Copyright (C) 2015 SuperTuxKart-Team
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 3
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 #ifndef SERVER_ONLY
19
20 #include "graphics/lighting_passes.hpp"
21 #include "config/user_config.hpp"
22 #include "graphics/central_settings.hpp"
23 #include "graphics/frame_buffer.hpp"
24 #include "graphics/glwrap.hpp"
25 #include "graphics/irr_driver.hpp"
26 #include "graphics/light.hpp"
27 #include "graphics/post_processing.hpp"
28 #include "graphics/rtts.hpp"
29 #include "graphics/shadow_matrices.hpp"
30 #include "graphics/texture_shader.hpp"
31 #include "modes/world.hpp"
32 #include "tracks/track.hpp"
33 #include "utils/profiler.hpp"
34
35 class LightBaseClass
36 {
37 public:
38 struct PointLightInfo
39 {
40 float posX;
41 float posY;
42 float posZ;
43 float energy;
44 float red;
45 float green;
46 float blue;
47 float radius;
48 };
49 public:
50 static const unsigned int MAXLIGHT = 32;
51 public:
52 static struct PointLightInfo m_point_lights_info[MAXLIGHT];
53 }; // LightBaseClass
54
55 const unsigned int LightBaseClass::MAXLIGHT;
56
57 // ============================================================================
58 LightBaseClass::PointLightInfo m_point_lights_info[LightBaseClass::MAXLIGHT];
59
60 // ============================================================================
61 class PointLightShader : public TextureShader < PointLightShader, 2 >
62 {
63 public:
64 GLuint vbo;
65 GLuint vao;
PointLightShader()66 PointLightShader()
67 {
68 loadProgram(OBJECT, GL_VERTEX_SHADER, "pointlight.vert",
69 GL_FRAGMENT_SHADER, "pointlight.frag");
70
71 assignUniforms();
72 assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED,
73 1, "dtex", ST_NEAREST_FILTERED);
74 glGenVertexArrays(1, &vao);
75 glBindVertexArray(vao);
76
77 glGenBuffers(1, &vbo);
78 glBindBuffer(GL_ARRAY_BUFFER, vbo);
79 glBufferData(GL_ARRAY_BUFFER,
80 LightBaseClass::MAXLIGHT * sizeof(LightBaseClass::PointLightInfo),
81 0, GL_DYNAMIC_DRAW);
82
83 GLuint attrib_Position = glGetAttribLocation(m_program, "Position");
84 GLuint attrib_Color = glGetAttribLocation(m_program, "Color");
85 GLuint attrib_Energy = glGetAttribLocation(m_program, "Energy");
86 GLuint attrib_Radius = glGetAttribLocation(m_program, "Radius");
87
88 glEnableVertexAttribArray(attrib_Position);
89 glVertexAttribPointer(attrib_Position, 3, GL_FLOAT, GL_FALSE,
90 sizeof(LightBaseClass::PointLightInfo), 0);
91 glEnableVertexAttribArray(attrib_Energy);
92 glVertexAttribPointer(attrib_Energy, 1, GL_FLOAT, GL_FALSE,
93 sizeof(LightBaseClass::PointLightInfo),
94 (GLvoid*)(3 * sizeof(float)));
95 glEnableVertexAttribArray(attrib_Color);
96 glVertexAttribPointer(attrib_Color, 3, GL_FLOAT, GL_FALSE,
97 sizeof(LightBaseClass::PointLightInfo),
98 (GLvoid*)(4 * sizeof(float)));
99 glEnableVertexAttribArray(attrib_Radius);
100 glVertexAttribPointer(attrib_Radius, 1, GL_FLOAT, GL_FALSE,
101 sizeof(LightBaseClass::PointLightInfo),
102 (GLvoid*)(7 * sizeof(float)));
103
104 glVertexAttribDivisorARB(attrib_Position, 1);
105 glVertexAttribDivisorARB(attrib_Energy, 1);
106 glVertexAttribDivisorARB(attrib_Color, 1);
107 glVertexAttribDivisorARB(attrib_Radius, 1);
108 } // PointLightShader
~PointLightShader()109 ~PointLightShader()
110 {
111 glDeleteBuffers(1, &vbo);
112 glDeleteVertexArrays(1, &vao);
113 } // PointLightShader
114 }; // PointLightShader
115
116
117
118
119 // ============================================================================
120 class PointLightScatterShader : public TextureShader<PointLightScatterShader,
121 1, float, core::vector3df>
122 {
123 public:
124 GLuint vao;
PointLightScatterShader()125 PointLightScatterShader()
126 {
127 loadProgram(OBJECT, GL_VERTEX_SHADER, "pointlight.vert",
128 GL_FRAGMENT_SHADER, "pointlightscatter.frag");
129
130 assignUniforms("density", "fogcol");
131 assignSamplerNames(0, "dtex", ST_NEAREST_FILTERED);
132
133 glGenVertexArrays(1, &vao);
134 glBindVertexArray(vao);
135
136 glBindBuffer(GL_ARRAY_BUFFER, PointLightShader::getInstance()->vbo);
137
138 GLuint attrib_Position = glGetAttribLocation(m_program, "Position");
139 GLuint attrib_Color = glGetAttribLocation(m_program, "Color");
140 GLuint attrib_Energy = glGetAttribLocation(m_program, "Energy");
141 GLuint attrib_Radius = glGetAttribLocation(m_program, "Radius");
142
143 glEnableVertexAttribArray(attrib_Position);
144 glVertexAttribPointer(attrib_Position, 3, GL_FLOAT, GL_FALSE,
145 sizeof(LightBaseClass::PointLightInfo), 0);
146 glEnableVertexAttribArray(attrib_Energy);
147 glVertexAttribPointer(attrib_Energy, 1, GL_FLOAT, GL_FALSE,
148 sizeof(LightBaseClass::PointLightInfo),
149 (GLvoid*)(3 * sizeof(float)));
150 glEnableVertexAttribArray(attrib_Color);
151 glVertexAttribPointer(attrib_Color, 3, GL_FLOAT, GL_FALSE,
152 sizeof(LightBaseClass::PointLightInfo),
153 (GLvoid*)(4 * sizeof(float)));
154 glEnableVertexAttribArray(attrib_Radius);
155 glVertexAttribPointer(attrib_Radius, 1, GL_FLOAT, GL_FALSE,
156 sizeof(LightBaseClass::PointLightInfo),
157 (GLvoid*)(7 * sizeof(float)));
158
159 glVertexAttribDivisorARB(attrib_Position, 1);
160 glVertexAttribDivisorARB(attrib_Energy, 1);
161 glVertexAttribDivisorARB(attrib_Color, 1);
162 glVertexAttribDivisorARB(attrib_Radius, 1);
163 } // PointLightScatterShader
~PointLightScatterShader()164 ~PointLightScatterShader()
165 {
166 glDeleteVertexArrays(1, &vao);
167 } // ~PointLightScatterShader
168 };
169
170 // ============================================================================
171 class IBLShader : public TextureShader<IBLShader, 4>
172 {
173 public:
IBLShader()174 IBLShader()
175 {
176 loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert",
177 GL_FRAGMENT_SHADER, "IBL.frag");
178 assignUniforms();
179 assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED,
180 1, "dtex", ST_NEAREST_FILTERED,
181 2, "probe", ST_TRILINEAR_CUBEMAP,
182 3, "albedo",ST_NEAREST_FILTERED);
183 } // IBLShader
184 }; // IBLShader
185
186 // ============================================================================
187 class DegradedIBLShader : public TextureShader<DegradedIBLShader, 1>
188 {
189 public:
DegradedIBLShader()190 DegradedIBLShader()
191 {
192 loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert",
193 GL_FRAGMENT_SHADER, "degraded_ibl.frag");
194 assignUniforms();
195 assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED);
196 } // DegradedIBLShader
197 }; // DegradedIBLShader
198
199 // ============================================================================
200 class ShadowedSunLightShaderPCF : public TextureShader<ShadowedSunLightShaderPCF,
201 3, float, float, float,
202 float, float,
203 core::vector3df, video::SColorf>
204 {
205 public:
ShadowedSunLightShaderPCF()206 ShadowedSunLightShaderPCF()
207 {
208 loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert",
209 GL_FRAGMENT_SHADER, "sunlightshadow.frag");
210
211 // Use 8 to circumvent a catalyst bug when binding sampler
212 assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED,
213 1, "dtex", ST_NEAREST_FILTERED,
214 8, "shadowtex", ST_SHADOW_SAMPLER);
215 assignUniforms("split0", "split1", "split2", "splitmax", "shadow_res",
216 "sundirection", "sun_color");
217 } // ShadowedSunLightShaderPCF
218 // ------------------------------------------------------------------------
render(GLuint normal_depth_texture,GLuint depth_stencil_texture,const FrameBuffer * shadow_framebuffer,const core::vector3df & direction,const video::SColorf & col)219 void render(GLuint normal_depth_texture,
220 GLuint depth_stencil_texture,
221 const FrameBuffer* shadow_framebuffer,
222 const core::vector3df &direction,
223 const video::SColorf &col)
224 {
225 setTextureUnits(normal_depth_texture,
226 depth_stencil_texture,
227 shadow_framebuffer->getDepthTexture() );
228 drawFullScreenEffect(ShadowMatrices::m_shadow_split[1],
229 ShadowMatrices::m_shadow_split[2],
230 ShadowMatrices::m_shadow_split[3],
231 ShadowMatrices::m_shadow_split[4],
232 float(UserConfigParams::m_shadows_resolution),
233 direction, col);
234
235 } // render
236 }; // ShadowedSunLightShaderPCF
237
238 // ============================================================================
239 class SunLightShader : public TextureShader<SunLightShader, 2,
240 core::vector3df, video::SColorf>
241 {
242 public:
SunLightShader()243 SunLightShader()
244 {
245 loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert",
246 GL_FRAGMENT_SHADER, "sunlight.frag");
247
248 assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED,
249 1, "dtex", ST_NEAREST_FILTERED);
250 assignUniforms("sundirection", "sun_color");
251 } // SunLightShader
252 // ------------------------------------------------------------------------
render(const core::vector3df & direction,const video::SColorf & col,GLuint normal_depth_texture,GLuint depth_stencil_texture)253 void render(const core::vector3df &direction, const video::SColorf &col,
254 GLuint normal_depth_texture,
255 GLuint depth_stencil_texture)
256 {
257 glEnable(GL_BLEND);
258 glDisable(GL_DEPTH_TEST);
259 glBlendFunc(GL_ONE, GL_ONE);
260 glBlendEquation(GL_FUNC_ADD);
261
262 setTextureUnits(normal_depth_texture, depth_stencil_texture);
263 drawFullScreenEffect(direction, col);
264 } // render
265 }; // SunLightShader
266
267 // ============================================================================
renderPointLights(unsigned count,GLuint normal_depth_rander_target,GLuint depth_stencil_texture)268 static void renderPointLights(unsigned count,
269 GLuint normal_depth_rander_target,
270 GLuint depth_stencil_texture)
271 {
272 glEnable(GL_BLEND);
273 glBlendEquation(GL_FUNC_ADD);
274 glBlendFunc(GL_ONE, GL_ONE);
275 glEnable(GL_DEPTH_TEST);
276 glDepthMask(GL_FALSE);
277
278 PointLightShader::getInstance()->use();
279
280 glBindBuffer(GL_ARRAY_BUFFER, PointLightShader::getInstance()->vbo);
281 glBufferSubData(GL_ARRAY_BUFFER, 0,
282 count * sizeof(LightBaseClass::PointLightInfo),
283 m_point_lights_info);
284
285 glBindVertexArray(PointLightShader::getInstance()->vao);
286 PointLightShader::getInstance()->setTextureUnits(
287 normal_depth_rander_target,
288 depth_stencil_texture);
289 PointLightShader::getInstance()->setUniforms();
290
291 glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count);
292 } // renderPointLights
293
294 // ----------------------------------------------------------------------------
renderEnvMap(GLuint normal_depth_texture,GLuint depth_stencil_texture,GLuint specular_probe,GLuint albedo_buffer)295 void LightingPasses::renderEnvMap(GLuint normal_depth_texture,
296 GLuint depth_stencil_texture,
297 GLuint specular_probe,
298 GLuint albedo_buffer)
299 {
300 glDisable(GL_DEPTH_TEST);
301 glEnable(GL_BLEND);
302 glBlendEquation(GL_FUNC_ADD);
303 glBlendFunc(GL_ONE, GL_ONE);
304
305 if (specular_probe == 0 || UserConfigParams::m_degraded_IBL)
306 {
307 DegradedIBLShader::getInstance()->use();
308 glBindVertexArray(SharedGPUObjects::getFullScreenQuadVAO());
309
310 DegradedIBLShader::getInstance()
311 ->setTextureUnits(normal_depth_texture);
312 DegradedIBLShader::getInstance()->setUniforms();
313 }
314 else
315 {
316 IBLShader::getInstance()->use();
317 glBindVertexArray(SharedGPUObjects::getFullScreenQuadVAO());
318
319 IBLShader::getInstance()->setTextureUnits(
320 normal_depth_texture,
321 depth_stencil_texture,
322 specular_probe,
323 albedo_buffer);
324 IBLShader::getInstance()->setUniforms();
325 }
326
327 glDrawArrays(GL_TRIANGLES, 0, 3);
328 glBindVertexArray(0);
329 glBindBuffer(GL_ARRAY_BUFFER, 0);
330 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
331 glEnable(GL_DEPTH_TEST);
332 glDisable(GL_BLEND);
333 } // renderEnvMap
334
335 // ----------------------------------------------------------------------------
renderSunlight(const core::vector3df & direction,const video::SColorf & col,GLuint normal_depth_texture,GLuint depth_stencil_texture)336 void LightingPasses::renderSunlight(const core::vector3df &direction,
337 const video::SColorf &col,
338 GLuint normal_depth_texture,
339 GLuint depth_stencil_texture)
340 {
341 SunLightShader::getInstance()->render(direction, col,
342 normal_depth_texture,
343 depth_stencil_texture);
344 } // renderSunlight
345
346
347 // ----------------------------------------------------------------------------
updateLightsInfo(scene::ICameraSceneNode * const camnode,float dt)348 void LightingPasses::updateLightsInfo(scene::ICameraSceneNode * const camnode,
349 float dt)
350 {
351 std::vector<LightNode *> lights = irr_driver->getLights();
352 const u32 lightcount = (u32)lights.size();
353 const core::vector3df &campos = camnode->getAbsolutePosition();
354
355 std::vector<LightNode *> BucketedLN[15];
356 for (unsigned int i = 0; i < lightcount; i++)
357 {
358 if (!lights[i]->isVisible())
359 continue;
360
361 if (!lights[i]->isPointLight())
362 {
363 lights[i]->render();
364 continue;
365 }
366 const core::vector3df &lightpos =
367 (lights[i]->getAbsolutePosition() - campos);
368 unsigned idx = (unsigned)(lightpos.getLength() / 10);
369 if (idx > 14)
370 idx = 14;
371 BucketedLN[idx].push_back(lights[i]);
372 }
373
374 m_point_light_count = 0;
375 bool multiplayer = (RaceManager::get()->getNumLocalPlayers() > 1);
376
377 for (unsigned i = 0; i < 15; i++)
378 {
379 for (unsigned j = 0; j < BucketedLN[i].size(); j++)
380 {
381 if (++m_point_light_count >= LightBaseClass::MAXLIGHT)
382 {
383 LightNode* light_node = BucketedLN[i].at(j);
384 light_node->setEnergyMultiplier(0.0f);
385 }
386 else
387 {
388 LightNode* light_node = BucketedLN[i].at(j);
389
390 float em = light_node->getEnergyMultiplier();
391 if (em < 1.0f)
392 {
393 // In single-player, fade-in lights.
394 // In multi-player, can't do that, the light objects are shared by all players
395 if (multiplayer)
396 light_node->setEnergyMultiplier(1.0f);
397 else
398 light_node->setEnergyMultiplier(std::min(1.0f, em + dt));
399 }
400
401 const core::vector3df &pos = light_node->getAbsolutePosition();
402 m_point_lights_info[m_point_light_count].posX = pos.X;
403 m_point_lights_info[m_point_light_count].posY = pos.Y;
404 m_point_lights_info[m_point_light_count].posZ = pos.Z;
405
406 m_point_lights_info[m_point_light_count].energy =
407 light_node->getEffectiveEnergy();
408
409 const core::vector3df &col = light_node->getColor();
410 m_point_lights_info[m_point_light_count].red = col.X;
411 m_point_lights_info[m_point_light_count].green = col.Y;
412 m_point_lights_info[m_point_light_count].blue = col.Z;
413
414 // Light radius
415 m_point_lights_info[m_point_light_count].radius = light_node->getRadius();
416 }
417 }
418 if (m_point_light_count > LightBaseClass::MAXLIGHT)
419 {
420 irr_driver->setLastLightBucketDistance(i * 10);
421 break;
422 }
423 }
424
425 m_point_light_count++;
426 } // updateLightsInfo
427
428 // ----------------------------------------------------------------------------
renderLights(bool has_shadow,GLuint normal_depth_texture,GLuint depth_stencil_texture,GLuint albedo_texture,const FrameBuffer * shadow_framebuffer,GLuint specular_probe)429 void LightingPasses::renderLights( bool has_shadow,
430 GLuint normal_depth_texture,
431 GLuint depth_stencil_texture,
432 GLuint albedo_texture,
433 const FrameBuffer* shadow_framebuffer,
434 GLuint specular_probe)
435 {
436 {
437 ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_ENVMAP));
438 renderEnvMap(normal_depth_texture,
439 depth_stencil_texture,
440 specular_probe,
441 albedo_texture);
442 }
443
444 // Render sunlight if and only if track supports shadow
445 const Track* const track = Track::getCurrentTrack();
446 if (!track|| track->hasShadows())
447 {
448 ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_SUN));
449 if (World::getWorld() && CVS->isShadowEnabled() && has_shadow)
450 {
451 glEnable(GL_BLEND);
452 glDisable(GL_DEPTH_TEST);
453 glBlendFunc(GL_ONE, GL_ONE);
454 glBlendEquation(GL_FUNC_ADD);
455 ShadowedSunLightShaderPCF::getInstance()->render(normal_depth_texture,
456 depth_stencil_texture,
457 shadow_framebuffer,
458 irr_driver->getSunDirection(),
459 irr_driver->getSunColor());
460 }
461 else
462 renderSunlight(irr_driver->getSunDirection(),
463 irr_driver->getSunColor(),
464 normal_depth_texture,
465 depth_stencil_texture);
466 }
467
468 //points lights
469 {
470 ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_POINTLIGHTS));
471 renderPointLights(std::min(m_point_light_count, LightBaseClass::MAXLIGHT),
472 normal_depth_texture,
473 depth_stencil_texture);
474 }
475 } // renderLights
476
477 // ----------------------------------------------------------------------------
renderLightsScatter(GLuint depth_stencil_texture,const FrameBuffer & half1_framebuffer,const FrameBuffer & half2_framebuffer,const PostProcessing * post_processing)478 void LightingPasses::renderLightsScatter(GLuint depth_stencil_texture,
479 const FrameBuffer& half1_framebuffer,
480 const FrameBuffer& half2_framebuffer,
481 const PostProcessing* post_processing)
482 {
483 half1_framebuffer.bind();
484 glClearColor(0., 0., 0., 0.);
485 glClear(GL_COLOR_BUFFER_BIT);
486
487 const Track * const track = Track::getCurrentTrack();
488
489 // This function is only called once per frame - thus no need for setters.
490 float start = track->getFogStart() + .001f;
491 const video::SColor tmpcol = track->getFogColor();
492
493 core::vector3df col(tmpcol.getRed() / 255.0f,
494 tmpcol.getGreen() / 255.0f,
495 tmpcol.getBlue() / 255.0f);
496
497 glDepthMask(GL_FALSE);
498 glEnable(GL_BLEND);
499 glBlendEquation(GL_FUNC_ADD);
500 glBlendFunc(GL_ONE, GL_ONE);
501
502 glEnable(GL_DEPTH_TEST);
503 core::vector3df col2(1., 1., 1.);
504
505 PointLightScatterShader::getInstance()->use();
506 glBindVertexArray(PointLightScatterShader::getInstance()->vao);
507
508 PointLightScatterShader::getInstance()
509 ->setTextureUnits(depth_stencil_texture);
510 PointLightScatterShader::getInstance()
511 ->setUniforms(1.f / (40.f * start), col2);
512
513 glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4,
514 std::min(m_point_light_count,
515 LightBaseClass::MAXLIGHT));
516
517 glDisable(GL_BLEND);
518 post_processing->renderGaussian6Blur(half1_framebuffer,
519 half2_framebuffer, 5., 5.);
520 } // renderLightsScatter
521
522 #endif
523