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