Radium Engine  1.5.0
ForwardRenderer.cpp
1 #include <Engine/Data/ShaderConfigFactory.hpp>
2 #include <Engine/Data/ShaderConfiguration.hpp>
3 #include <Engine/Rendering/ForwardRenderer.hpp>
4 
5 #include <Core/Containers/MakeShared.hpp>
6 #include <Core/Geometry/TopologicalMesh.hpp>
7 #include <Core/Utils/Color.hpp>
8 #include <Core/Utils/Log.hpp>
9 
10 #include <Engine/Data/LambertianMaterial.hpp>
11 #include <Engine/Data/Material.hpp>
12 #include <Engine/Data/RenderParameters.hpp>
13 #include <Engine/Data/ShaderProgramManager.hpp>
14 #include <Engine/Data/Texture.hpp>
15 #include <Engine/Data/ViewingParameters.hpp>
16 #include <Engine/OpenGL.hpp>
17 #include <Engine/Rendering/DebugRender.hpp>
18 #include <Engine/Rendering/RenderObject.hpp>
19 #include <Engine/Scene/DefaultLightManager.hpp>
20 #include <Engine/Scene/Light.hpp>
21 #include <globjects/Framebuffer.h>
22 
23 /* Test Point cloud parameter provider */
24 #include <Core/RaCore.hpp>
25 #include <Engine/Data/ShaderProgram.hpp>
26 #include <Engine/Scene/GeometryComponent.hpp>
27 
28 #include <Engine/Scene/SystemDisplay.hpp>
29 
30 #include <map>
31 
32 #include <globjects/Texture.h>
33 
34 namespace Ra {
35 using namespace Core;
36 using namespace Core::Utils; // log
37 
38 namespace Engine {
39 namespace Rendering {
40 namespace {
41 const GLenum buffers[] = { GL_COLOR_ATTACHMENT0,
42  GL_COLOR_ATTACHMENT1,
43  GL_COLOR_ATTACHMENT2,
44  GL_COLOR_ATTACHMENT3,
45  GL_COLOR_ATTACHMENT4,
46  GL_COLOR_ATTACHMENT5,
47  GL_COLOR_ATTACHMENT6,
48  GL_COLOR_ATTACHMENT7 };
49 }
50 
51 ForwardRenderer::ForwardRenderer() : Renderer() {}
52 
53 ForwardRenderer::~ForwardRenderer() = default;
54 
55 void ForwardRenderer::initializeInternal() {
56  initShaders();
57  initBuffers();
58 
59  auto lightManager = new Scene::DefaultLightManager();
60  Ra::Engine::RadiumEngine::getInstance()->registerSystem( "DefaultLightManager", lightManager );
61  m_lightmanagers.push_back( lightManager );
62 
63  if ( !DebugRender::getInstance() ) {
64  DebugRender::createInstance();
65  DebugRender::getInstance()->initialize();
66  }
67 }
68 
69 void ForwardRenderer::initShaders() {
71  auto resourcesRootDir { RadiumEngine::getInstance()->getResourcesDir() };
72  m_shaderProgramManager->addShaderProgram(
73  { { "Hdr2Ldr" },
74  resourcesRootDir + "Shaders/2DShaders/Basic2D.vert.glsl",
75  resourcesRootDir + "Shaders/2DShaders/Hdr2Ldr.frag.glsl" } );
76  m_shaderProgramManager->addShaderProgram(
77  { { "ComposeOIT" },
78  resourcesRootDir + "Shaders/2DShaders/Basic2D.vert.glsl",
79  resourcesRootDir + "Shaders/2DShaders/ComposeOIT.frag.glsl" } );
80 
81  Data::ShaderConfiguration wireframe { { "Wireframe" },
82  resourcesRootDir + "Shaders/Lines/Wireframe.vert.glsl",
83  resourcesRootDir + "Shaders/Lines/Wireframe.frag.glsl" };
84  wireframe.addShader( Data::ShaderType::ShaderType_GEOMETRY,
85  resourcesRootDir + "Shaders/Lines/Wireframe.geom.glsl" );
86  Data::ShaderConfigurationFactory::addConfiguration( wireframe );
87  m_shaderProgramManager->addShaderProgram( wireframe );
88 }
89 
90 void ForwardRenderer::initBuffers() {
91  m_fbo = std::make_unique<globjects::Framebuffer>();
92  m_oitFbo = std::make_unique<globjects::Framebuffer>();
93  m_postprocessFbo = std::make_unique<globjects::Framebuffer>();
94  m_uiXrayFbo = std::make_unique<globjects::Framebuffer>();
95  m_volumeFbo = std::make_unique<globjects::Framebuffer>();
96  // Forward renderer internal textures texture
97 
98  Data::TextureParameters texparams;
99  texparams.width = m_width;
100  texparams.height = m_height;
101  texparams.target = GL_TEXTURE_2D;
102 
103  // Depth texture
104  texparams.minFilter = GL_NEAREST;
105  texparams.magFilter = GL_NEAREST;
106  texparams.internalFormat = GL_DEPTH_COMPONENT24;
107  texparams.format = GL_DEPTH_COMPONENT;
108  texparams.type = GL_UNSIGNED_INT;
109  texparams.name = "Depth (fw renderer)";
110  m_textures[RendererTextures_Depth] = std::make_unique<Data::Texture>( texparams );
111 
112  // Color texture
113  texparams.internalFormat = GL_RGBA32F;
114  texparams.format = GL_RGBA;
115  texparams.type = GL_SCALAR;
116  texparams.minFilter = GL_LINEAR;
117  texparams.magFilter = GL_LINEAR;
118 
119  texparams.name = "HDR";
120  m_textures[RendererTextures_HDR] = std::make_unique<Data::Texture>( texparams );
121 
122  texparams.name = "Normal";
123  m_textures[RendererTextures_Normal] = std::make_unique<Data::Texture>( texparams );
124 
125  texparams.name = "Diffuse";
126  m_textures[RendererTextures_Diffuse] = std::make_unique<Data::Texture>( texparams );
127 
128  texparams.name = "Specular";
129  m_textures[RendererTextures_Specular] = std::make_unique<Data::Texture>( texparams );
130 
131  texparams.name = "OIT Accum";
132  m_textures[RendererTextures_OITAccum] = std::make_unique<Data::Texture>( texparams );
133 
134  texparams.name = "OIT Revealage";
135  m_textures[RendererTextures_OITRevealage] = std::make_unique<Data::Texture>( texparams );
136 
137  texparams.name = "Volume";
138  m_textures[RendererTextures_Volume] = std::make_unique<Data::Texture>( texparams );
139 
140  m_secondaryTextures["Depth (fw)"] = m_textures[RendererTextures_Depth].get();
141  m_secondaryTextures["HDR Texture"] = m_textures[RendererTextures_HDR].get();
142  m_secondaryTextures["Normal Texture"] = m_textures[RendererTextures_Normal].get();
143  m_secondaryTextures["Diffuse Texture"] = m_textures[RendererTextures_Diffuse].get();
144  m_secondaryTextures["Specular Texture"] = m_textures[RendererTextures_Specular].get();
145  m_secondaryTextures["OIT Accum"] = m_textures[RendererTextures_OITAccum].get();
146  m_secondaryTextures["OIT Revealage"] = m_textures[RendererTextures_OITRevealage].get();
147 
148  // Volume texture is not exposed ...
149  m_secondaryTextures["Volume"] = m_textures[RendererTextures_Volume].get();
150 }
151 
152 void ForwardRenderer::updateStepInternal( const Data::ViewingParameters& renderData ) {
153  CORE_UNUSED( renderData );
154  // TODO : Improve the way RO are distributed in fancy (opaque), transparent and volume
155  // to simplify rendering loop and code maintenance
156  // i.e. Volume should considered as transparent but stored in the volumetric list and
157  // transparent-but-not-volume object should be kept in the fancy list, ...
158  m_transparentRenderObjects.clear();
159  m_volumetricRenderObjects.clear();
160  for ( auto it = m_fancyRenderObjects.begin(); it != m_fancyRenderObjects.end(); ) {
161  if ( ( *it )->isTransparent() ) {
162  m_transparentRenderObjects.push_back( *it );
163  it = m_fancyRenderObjects.erase( it );
164  }
165  else {
166  auto material = ( *it )->getMaterial();
167  if ( material &&
168  material->getMaterialAspect() == Data::Material::MaterialAspect::MAT_DENSITY ) {
169  m_volumetricRenderObjects.push_back( *it );
170  it = m_fancyRenderObjects.erase( it );
171  }
172  else { ++it; }
173  }
174  }
175  m_fancyTransparentCount = m_transparentRenderObjects.size();
176  m_fancyVolumetricCount = m_volumetricRenderObjects.size();
177 
178  // simple hack to clean wireframes ...
179  if ( m_fancyRenderObjects.size() < m_wireframes.size() ) { m_wireframes.clear(); }
180 }
181 
182 template <typename IndexContainerType>
183 void computeIndices( Core::Geometry::LineMesh::IndexContainerType& indices,
184  IndexContainerType& other ) {
185 
186  for ( const auto& index : other ) {
187  auto s = index.size();
188  for ( unsigned int i = 0; i < s; ++i ) {
189  int i1 = index[i];
190  int i2 = index[( i + 1 ) % s];
191  if ( i1 > i2 ) std::swap( i1, i2 );
192  indices.emplace_back( i1, i2 );
193  }
194  }
195 
196  std::sort( indices.begin(),
197  indices.end(),
198  []( const Core::Geometry::LineMesh::IndexType& a,
199  const Core::Geometry::LineMesh::IndexType& b ) {
200  return a[0] < b[0] || ( a[0] == b[0] && a[1] < b[1] );
201  } );
202  indices.erase( std::unique( indices.begin(), indices.end() ), indices.end() );
203 }
204 
205 // store LineMesh and Core, define the observer functor to update data one core update for wireframe
206 // linemesh
207 template <typename CoreGeometry>
208 class VerticesUpdater
209 {
210  public:
211  VerticesUpdater( std::shared_ptr<Data::LineMesh> disp, CoreGeometry& core ) :
212  m_disp { disp }, m_core { core } {};
213 
214  void operator()() { m_disp->getCoreGeometry().setVertices( m_core.vertices() ); }
215  std::shared_ptr<Data::LineMesh> m_disp;
216  CoreGeometry& m_core;
217 };
218 
219 template <typename CoreGeometry>
220 class IndicesUpdater
221 {
222  public:
223  IndicesUpdater( std::shared_ptr<Data::LineMesh> disp, CoreGeometry& core ) :
224  m_disp { disp }, m_core { core } {};
225 
226  void operator()() {
227  auto lineIndices = m_disp->getCoreGeometry().getIndicesWithLock();
228  computeIndices( lineIndices, m_core.getIndices() );
229  m_disp->getCoreGeometry().indicesUnlock();
230  }
231  std::shared_ptr<Data::LineMesh> m_disp;
232  CoreGeometry& m_core;
233 };
234 
235 // create a linemesh to draw wireframe given a core mesh
236 template <typename CoreGeometry>
237 void setupLineMesh( std::shared_ptr<Data::LineMesh>& disp, CoreGeometry& core ) {
238 
239  Core::Geometry::LineMesh lines;
240  Core::Geometry::LineMesh::IndexContainerType indices;
241 
242  lines.setVertices( core.vertices() );
243  computeIndices( indices, core.getIndices() );
244  if ( indices.size() > 0 ) {
245  lines.setIndices( std::move( indices ) );
246  disp =
247  Ra::Core::make_shared<Data::LineMesh>( std::string( "wireframe" ), std::move( lines ) );
248  disp->updateGL();
249 
250  // add observer
251  auto handle = core.template getAttribHandle<typename CoreGeometry::Point>(
252  Ra::Core::Geometry::getAttribName( Ra::Core::Geometry::VERTEX_POSITION ) );
253  core.vertexAttribs().getAttrib( handle ).attach( VerticesUpdater( disp, core ) );
254  core.attach( IndicesUpdater( disp, core ) );
255  }
256  else { disp.reset(); }
257 }
258 
259 void ForwardRenderer::renderInternal( const Data::ViewingParameters& renderData ) {
260 
261  m_fbo->bind();
262 
263  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
264  GL_ASSERT( glDepthMask( GL_TRUE ) );
265  GL_ASSERT( glColorMask( 1, 1, 1, 1 ) );
266 
267  GL_ASSERT( glDrawBuffers( 4, buffers ) );
268  if ( m_wireframe ) {
270  glEnable( GL_POLYGON_OFFSET_FILL );
271  glPolygonOffset( 1.f, 3.f );
272  // GL_ASSERT( glDepthRange( 0.001, 1.0 ) );
273  }
274  else { glDisable( GL_POLYGON_OFFSET_FILL ); }
275  static const auto clearZeros = Core::Utils::Color::Black().cast<GL_SCALAR_PLAIN>().eval();
276  static const auto clearOnes = Core::Utils::Color::White().cast<GL_SCALAR_PLAIN>().eval();
277  static const float clearDepth { 1.0f };
278 
280  auto bgColor = getBackgroundColor().cast<GL_SCALAR_PLAIN>().eval();
281  GL_ASSERT( glClearBufferfv( GL_COLOR, 0, bgColor.data() ) ); // Clear color
282  GL_ASSERT( glClearBufferfv( GL_COLOR, 1, clearZeros.data() ) ); // Clear normals
283  GL_ASSERT( glClearBufferfv( GL_COLOR, 2, clearZeros.data() ) ); // Clear diffuse
284  GL_ASSERT( glClearBufferfv( GL_COLOR, 3, clearZeros.data() ) ); // Clear specular
285  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) ); // Clear depth
286 
287  // render background objects (eg sky box)
288  renderBackground( renderData );
289 
290  // Z prepass
291  GL_ASSERT( glDepthFunc( GL_LESS ) );
292  GL_ASSERT( glDisable( GL_BLEND ) );
293  GL_ASSERT( glPointSize( 3.f ) );
294 
295  // Set in RenderParam the configuration about ambiant lighting (instead of hard constant
296  // direclty in shaders)
297 
298  for ( const auto& ro : m_fancyRenderObjects ) {
299  ro->render( {}, renderData, DefaultRenderingPasses::Z_PREPASS );
300  }
301  // Transparent objects are rendered in the Z-prepass, but only their fully opaque fragments
302  // (if any) might influence the z-buffer.
303  // Rendering transparent objects assuming that they
304  // discard all their non-opaque fragments
305  for ( const auto& ro : m_transparentRenderObjects ) {
306  ro->render( {}, renderData, DefaultRenderingPasses::Z_PREPASS );
307  }
308  // Volumetric objects are not rendered in the Z-prepass
309 
310  // Opaque Lighting pass
311  GL_ASSERT( glDepthFunc( GL_LEQUAL ) );
312  GL_ASSERT( glDepthMask( GL_FALSE ) );
313 
314  GL_ASSERT( glEnable( GL_BLEND ) );
315  GL_ASSERT( glBlendFunc( GL_ONE, GL_ONE ) );
316 
317  GL_ASSERT( glDrawBuffers( 1, buffers ) ); // Draw color texture
318 
319  // Radium V2 : this render loop might be greatly improved by inverting light and objects
320  // loop.
321  // Make shaders bounded only once, minimize full stats-changes, ...
322  if ( m_lightmanagers[0]->count() > 0 ) {
323  // for ( const auto& l : m_lights )
324  for ( size_t i = 0; i < m_lightmanagers[0]->count(); ++i ) {
325  const auto l = m_lightmanagers[0]->getLight( i );
326  for ( const auto& ro : m_fancyRenderObjects ) {
327  ro->render(
328  l->getRenderParameters(), renderData, DefaultRenderingPasses::LIGHTING_OPAQUE );
329  }
330  // Rendering transparent objects assuming that they discard all their non-opaque
331  // fragments
332  for ( const auto& ro : m_transparentRenderObjects ) {
333  ro->render(
334  l->getRenderParameters(), renderData, DefaultRenderingPasses::LIGHTING_OPAQUE );
335  }
336  }
337  }
338  else { LOG( logINFO ) << "Opaque : no light sources, unable to render"; }
339 
340  // Transparency (blending) pass
341  if ( !m_transparentRenderObjects.empty() ) {
342  m_fbo->unbind();
343 
344  m_oitFbo->bind();
345 
346  GL_ASSERT( glDrawBuffers( 2, buffers ) );
347  GL_ASSERT( glClearBufferfv( GL_COLOR, 0, clearZeros.data() ) );
348  GL_ASSERT( glClearBufferfv( GL_COLOR, 1, clearOnes.data() ) );
349 
350  GL_ASSERT( glDepthFunc( GL_LESS ) );
351  GL_ASSERT( glEnable( GL_BLEND ) );
352 
353  GL_ASSERT( glBlendEquation( GL_FUNC_ADD ) );
354  GL_ASSERT( glBlendFunci( 0, GL_ONE, GL_ONE ) );
355  GL_ASSERT( glBlendFunci( 1, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA ) );
356 
357  if ( m_lightmanagers[0]->count() > 0 ) {
358  // for ( const auto& l : m_lights )
359  for ( size_t i = 0; i < m_lightmanagers[0]->count(); ++i ) {
360  const auto l = m_lightmanagers[0]->getLight( i );
361 
362  for ( const auto& ro : m_transparentRenderObjects ) {
363  ro->render( l->getRenderParameters(),
364  renderData,
365  DefaultRenderingPasses::LIGHTING_TRANSPARENT );
366  }
367  }
368  }
369  else { LOG( logINFO ) << "Transparent : no light sources, unable to render"; }
370 
371  m_oitFbo->unbind();
372 
373  m_fbo->bind();
374  GL_ASSERT( glDrawBuffers( 1, buffers ) );
375  GL_ASSERT( glDisable( GL_DEPTH_TEST ) );
376  GL_ASSERT( glBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA ) );
377  {
378  auto shader = m_shaderProgramManager->getShaderProgram( "ComposeOIT" );
379  shader->bind();
380  shader->setUniform( "u_OITSumColor", m_textures[RendererTextures_OITAccum].get(), 0 );
381  shader->setUniform(
382  "u_OITSumWeight", m_textures[RendererTextures_OITRevealage].get(), 1 );
383 
384  m_quadMesh->render( shader );
385  }
386  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
387  }
388 
389  // Volumetric pass
390  // Z-test is enabled but z-write must be disable to allow access to the z-buffer in the
391  // shader.
392  // This pass render in its own FBO and copy the result to the main colortexture
393  if ( m_lightmanagers[0]->count() > 0 ) {
394  if ( !m_volumetricRenderObjects.empty() ) {
395 
396  m_volumeFbo->bind();
397  GL_ASSERT( glDrawBuffers( 1, buffers ) );
398  static const auto alpha = Core::Utils::Color::Alpha().cast<GL_SCALAR_PLAIN>().eval();
399  GL_ASSERT( glClearBufferfv( GL_COLOR, 0, alpha.data() ) );
400  GL_ASSERT( glDisable( GL_BLEND ) );
401 
402  Data::RenderParameters composeParams;
403  composeParams.addParameter( "imageColor", m_textures[RendererTextures_HDR].get() );
404  composeParams.addParameter( "imageDepth", m_textures[RendererTextures_Depth].get() );
405  Data::RenderParameters passParams;
406  passParams.addParameter( "compose_data", composeParams );
407 
408  for ( size_t i = 0; i < m_lightmanagers[0]->count(); ++i ) {
409  const auto l = m_lightmanagers[0]->getLight( i );
410 
411  passParams.addParameter( "light_source", l->getRenderParameters() );
412 
413  for ( const auto& ro : m_volumetricRenderObjects ) {
414  ro->render(
415  passParams, renderData, DefaultRenderingPasses::LIGHTING_VOLUMETRIC );
416  }
417  }
418  m_volumeFbo->unbind();
419  m_fbo->bind();
420  GL_ASSERT( glDrawBuffers( 1, buffers ) );
421  GL_ASSERT( glDisable( GL_DEPTH_TEST ) );
422  GL_ASSERT( glEnable( GL_BLEND ) );
423  GL_ASSERT( glBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA ) );
424  {
425  auto shader = m_shaderProgramManager->getShaderProgram( "ComposeVolume" );
426  shader->bind();
427  shader->setUniform( "volumeImage", m_textures[RendererTextures_Volume].get(), 0 );
428  m_quadMesh->render( shader );
429  }
430  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
431  }
432  }
433 
434  if ( m_wireframe ) {
435  m_fbo->bind();
436 
437  glDisable( GL_POLYGON_OFFSET_FILL );
438  GL_ASSERT( glDepthFunc( GL_LEQUAL ) );
439  GL_ASSERT( glEnable( GL_BLEND ) );
440  glBlendEquationSeparate( GL_FUNC_ADD, GL_FUNC_ADD );
441  glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO );
442  GL_ASSERT( glDrawBuffers( 1, buffers ) ); // Draw color texture
443 
444  auto drawWireframe = [this, &renderData]( const auto& ro ) {
445  std::shared_ptr<Data::Displayable> wro;
446 
447  WireMap::iterator it = m_wireframes.find( ro.get() );
448  if ( it == m_wireframes.end() ) {
449  std::shared_ptr<Data::LineMesh> disp;
450 
454 
455  auto displayable = ro->getMesh();
456  auto tm = std::dynamic_pointer_cast<trimesh>( displayable );
457  auto tp = std::dynamic_pointer_cast<polymesh>( displayable );
458  auto tq = std::dynamic_pointer_cast<quadmesh>( displayable );
459 
460  auto processLineMesh = []( auto cm, std::shared_ptr<Data::LineMesh>& lm ) {
461  if ( cm->getRenderMode() ==
462  Data::AttribArrayDisplayable::MeshRenderMode::RM_TRIANGLES ) {
463  setupLineMesh( lm, cm->getCoreGeometry() );
464  }
465  };
466  if ( tm ) { processLineMesh( tm, disp ); }
467  if ( tp ) { processLineMesh( tp, disp ); }
468  if ( tq ) { processLineMesh( tq, disp ); }
469 
470  m_wireframes[ro.get()] = disp;
471  wro = disp;
472  }
473  else { wro = it->second; }
474 
475  const Data::ShaderProgram* shader =
476  m_shaderProgramManager->getShaderProgram( "Wireframe" );
477 
478  if ( shader && wro ) {
479  shader->bind();
480  if ( ro->isVisible() ) {
481  wro->updateGL();
482 
483  Core::Matrix4 modelMatrix = ro->getTransformAsMatrix();
484  shader->setUniform( "transform.proj", renderData.projMatrix );
485  shader->setUniform( "transform.view", renderData.viewMatrix );
486  shader->setUniform( "transform.model", modelMatrix );
487  shader->setUniform( "viewport", Core::Vector2 { m_width, m_height } );
488  wro->render( shader );
489 
490  GL_CHECK_ERROR;
491  }
492  }
493  };
494 
495  for ( const auto& ro : m_fancyRenderObjects ) {
496  drawWireframe( ro );
497  }
498  for ( const auto& ro : m_transparentRenderObjects ) {
499  drawWireframe( ro );
500  }
501  }
502 
503  // Restore state
504  GL_ASSERT( glDepthFunc( GL_LESS ) );
505  GL_ASSERT( glDisable( GL_BLEND ) );
506  m_fbo->unbind();
507 }
508 
509 // Draw debug stuff, do not overwrite depth map but do depth testing
510 void ForwardRenderer::debugInternal( const Data::ViewingParameters& renderData ) {
511  if ( m_drawDebug ) {
512  m_postprocessFbo->bind();
513  GL_ASSERT( glDisable( GL_BLEND ) );
514  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
515  GL_ASSERT( glDepthMask( GL_FALSE ) );
516  GL_ASSERT( glDepthFunc( GL_LESS ) );
517 
518  glDrawBuffers( 1, buffers );
519  for ( const auto& ro : m_debugRenderObjects ) {
520  ro->render( {}, renderData );
521  }
522 
523  DebugRender::getInstance()->render( renderData.viewMatrix, renderData.projMatrix );
524 
525  m_postprocessFbo->unbind();
526 
527  m_uiXrayFbo->bind();
528  // Draw X rayed objects always on top of normal objects
529  GL_ASSERT( glDepthMask( GL_TRUE ) );
530  GL_ASSERT( glClear( GL_DEPTH_BUFFER_BIT ) );
531  Data::RenderParameters xrayLightParams;
532  xrayLightParams.addParameter( "light.color", Ra::Core::Utils::Color::Grey( 5.0 ) );
533  xrayLightParams.addParameter( "light.type", Scene::Light::LightType::DIRECTIONAL );
534  xrayLightParams.addParameter( "light.directional.direction", Core::Vector3( 0, -1, 0 ) );
535  for ( const auto& ro : m_xrayRenderObjects ) {
536  if ( ro->isVisible() ) { ro->render( xrayLightParams, renderData ); }
537  }
538  m_uiXrayFbo->unbind();
539  }
540 }
541 
542 // Draw UI stuff, always drawn on top of everything else + clear ZMask
543 void ForwardRenderer::uiInternal( const Data::ViewingParameters& renderData ) {
544 
545  m_uiXrayFbo->bind();
546  glDrawBuffers( 1, buffers );
547  // Enable z-test
548  GL_ASSERT( glDepthMask( GL_TRUE ) );
549  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
550  GL_ASSERT( glDepthFunc( GL_LESS ) );
551  GL_ASSERT( glClear( GL_DEPTH_BUFFER_BIT ) );
552  for ( const auto& ro : m_uiRenderObjects ) {
553  if ( ro->isVisible() ) {
554  auto shader = ro->getRenderTechnique()->getShader();
555  if ( !shader ) {
556  LOG( logERROR ) << "shader not found" << ro->getName() << " "
557  << ro->getRenderTechnique()->getConfiguration().getName();
558  }
559  else {
560 
561  // bind data
562  shader->bind();
563 
564  Core::Matrix4 M = ro->getTransformAsMatrix();
565  Core::Matrix4 MV = renderData.viewMatrix * M;
566  Core::Vector3 V = MV.block<3, 1>( 0, 3 );
567  Scalar d = V.norm();
568 
569  Core::Matrix4 S = Core::Matrix4::Identity();
570  S.coeffRef( 0, 0 ) = S.coeffRef( 1, 1 ) = S.coeffRef( 2, 2 ) = d;
571 
572  M = M * S;
573 
574  shader->setUniform( "transform.proj", renderData.projMatrix );
575  shader->setUniform( "transform.view", renderData.viewMatrix );
576  shader->setUniform( "transform.model", M );
577 
578  auto shaderParameter = ro->getRenderTechnique()->getParametersProvider();
579  if ( shaderParameter != nullptr ) shaderParameter->getParameters().bind( shader );
580 
581  // render
582  ro->getMesh()->render( shader );
583  }
584  }
585  }
586  m_uiXrayFbo->unbind();
587 }
588 
589 void ForwardRenderer::postProcessInternal( const Data::ViewingParameters& renderData ) {
590  CORE_UNUSED( renderData );
591 
592  m_postprocessFbo->bind();
593 
594  GL_ASSERT( glDrawBuffers( 1, buffers ) );
595 
596  GL_ASSERT( glDisable( GL_DEPTH_TEST ) );
597  GL_ASSERT( glDepthMask( GL_FALSE ) );
598 
599  const Data::ShaderProgram* shader = m_shaderProgramManager->getShaderProgram( "Hdr2Ldr" );
600  shader->bind();
601  shader->setUniform( "screenTexture", m_textures[RendererTextures_HDR].get(), 0 );
602  m_quadMesh->render( shader );
603 
604  GL_ASSERT( glDepthMask( GL_TRUE ) );
605  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
606 
607  m_postprocessFbo->unbind();
608 }
609 
610 void ForwardRenderer::resizeInternal() {
611  m_pingPongSize = std::pow( uint( 2 ), uint( std::log2( std::min( m_width, m_height ) ) ) );
612 
613  m_textures[RendererTextures_Depth]->resize( m_width, m_height );
614  m_textures[RendererTextures_HDR]->resize( m_width, m_height );
615  m_textures[RendererTextures_Normal]->resize( m_width, m_height );
616  m_textures[RendererTextures_Diffuse]->resize( m_width, m_height );
617  m_textures[RendererTextures_Specular]->resize( m_width, m_height );
618  m_textures[RendererTextures_OITAccum]->resize( m_width, m_height );
619  m_textures[RendererTextures_OITRevealage]->resize( m_width, m_height );
620  m_textures[RendererTextures_Volume]->resize( m_width, m_height );
621 
622  m_fbo->bind();
623  m_fbo->attachTexture( GL_DEPTH_ATTACHMENT, m_textures[RendererTextures_Depth]->texture() );
624  m_fbo->attachTexture( GL_COLOR_ATTACHMENT0, m_textures[RendererTextures_HDR]->texture() );
625  m_fbo->attachTexture( GL_COLOR_ATTACHMENT1, m_textures[RendererTextures_Normal]->texture() );
626  m_fbo->attachTexture( GL_COLOR_ATTACHMENT2, m_textures[RendererTextures_Diffuse]->texture() );
627  m_fbo->attachTexture( GL_COLOR_ATTACHMENT3, m_textures[RendererTextures_Specular]->texture() );
628  if ( m_fbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) {
629  LOG( logERROR ) << "FBO Error (ForwardRenderer::m_fbo): " << m_fbo->checkStatus();
630  }
631 
632  m_volumeFbo->bind();
633  m_volumeFbo->attachTexture( GL_DEPTH_ATTACHMENT,
634  m_textures[RendererTextures_Depth]->texture() );
635  m_volumeFbo->attachTexture( GL_COLOR_ATTACHMENT0,
636  m_textures[RendererTextures_Volume]->texture() );
637  if ( m_volumeFbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) {
638  LOG( logERROR ) << "FBO Error (ForwardRenderer::m_volumeFbo) : "
639  << m_volumeFbo->checkStatus();
640  }
641 
642  m_oitFbo->bind();
643  m_oitFbo->attachTexture( GL_DEPTH_ATTACHMENT, m_textures[RendererTextures_Depth]->texture() );
644  m_oitFbo->attachTexture( GL_COLOR_ATTACHMENT0,
645  m_textures[RendererTextures_OITAccum]->texture() );
646  m_oitFbo->attachTexture( GL_COLOR_ATTACHMENT1,
647  m_textures[RendererTextures_OITRevealage]->texture() );
648  if ( m_oitFbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) {
649  LOG( logERROR ) << "FBO Error (ForwardRenderer::m_oitFbo) : " << m_oitFbo->checkStatus();
650  }
651 
652  m_postprocessFbo->bind();
653  m_postprocessFbo->attachTexture( GL_DEPTH_ATTACHMENT,
654  m_textures[RendererTextures_Depth]->texture() );
655  m_postprocessFbo->attachTexture( GL_COLOR_ATTACHMENT0, m_fancyTexture->texture() );
656  if ( m_postprocessFbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) {
657  LOG( logERROR ) << "FBO Error (ForwardRenderer::m_postprocessFbo) : "
658  << m_postprocessFbo->checkStatus();
659  }
660 
661  // FIXED : when m_postprocessFbo use the RendererTextures_Depth, the depth buffer is erased
662  // and is therefore useless for future computation. Do not use this post-process FBO to
663  // render eveything else than the scene. Create several FBO with ther own configuration
664  // (uncomment Renderer::m_depthTexture->texture() to see the difference.)
665  m_uiXrayFbo->bind();
666  m_uiXrayFbo->attachTexture( GL_DEPTH_ATTACHMENT, Renderer::m_depthTexture->texture() );
667  m_uiXrayFbo->attachTexture( GL_COLOR_ATTACHMENT0, m_fancyTexture->texture() );
668  if ( m_uiXrayFbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) {
669  LOG( logERROR ) << "FBO Error (ForwardRenderer::m_uiXrayFbo) : "
670  << m_uiXrayFbo->checkStatus();
671  }
672  // finished with fbo, undbind to bind default
673  globjects::Framebuffer::unbind();
674 }
675 
676 /* Test Point cloud parameter provider */
677 /*
678  * WARNING : this class is here only for testing and experimentation purpose.
679  * It will be replace soon by a better management of the way components could add specific
680  * properties to a rendertechnique
681  * TODO : see PR Draft and gist subShaderBlob
682  */
683 class PointCloudParameterProvider : public Data::ShaderParameterProvider
684 {
685  public:
686  PointCloudParameterProvider( std::shared_ptr<Data::Material> mat,
687  Scene::PointCloudComponent* pointCloud ) :
688  ShaderParameterProvider(), m_displayMaterial( mat ), m_component( pointCloud ) {}
689  ~PointCloudParameterProvider() override = default;
690  void updateGL() override {
691  m_displayMaterial->updateGL();
692  auto& renderParameters = getParameters();
693  renderParameters.mergeReplaceParameters( m_displayMaterial->getParameters() );
694  renderParameters.addParameter( "pointCloudSplatRadius", m_component->getSplatSize() );
695  }
696 
697  private:
698  std::shared_ptr<Data::Material> m_displayMaterial;
699  Scene::PointCloudComponent* m_component;
700 };
701 
702 /*
703  * Build renderTechnique for Forward Renderer : this is the default in Radium, so create Default
704  * Render Technique
705  */
706 bool ForwardRenderer::buildRenderTechnique( RenderObject* ro ) const {
707  auto material = ro->getMaterial();
708  if ( !material ) {
709  LOG( logWARNING ) << "ForwardRenderer : no material found when building RenderTechnique"
710  << " - adding red Lambertian material";
711  auto defMat = new Data::LambertianMaterial( "ForwardRenderer::Default material" );
712  defMat->m_color = Ra::Core::Utils::Color::Red();
713  material.reset( defMat );
714  }
715  auto builder = EngineRenderTechniques::getDefaultTechnique( material->getMaterialName() );
716  auto rt = Core::make_shared<RenderTechnique>();
717  // define the technique for rendering this RenderObject (here, using the default from
718  // Material name)
719  // NOTE : Setting transparency to true does not hurt performances here. It will just allow to
720  // change the transparency of an object online
721  builder.second( *rt, true /*material->isTransparent()*/ );
722  // If renderObject is a point cloud, add geometry shader for splatting
723  auto RenderedGeometry = ro->getMesh().get();
724 
725  /*
726  * WARNING : this way of managing specific geometries is here only for testing and
727  * experimentation purpose. It will be replace soon by a better management of the way
728  * components could add specific properties to a rendertechnique
729  * TODO : see PR Draft and gist subShaderBlob
730  */
731  if ( RenderedGeometry && RenderedGeometry->getNumFaces() == 0 ) {
732  auto pointCloud = dynamic_cast<Scene::PointCloudComponent*>( ro->getComponent() );
733  if ( pointCloud ) {
734  auto addGeomShader = [&rt]( Core::Utils::Index pass ) {
735  if ( rt->hasConfiguration( pass ) ) {
736  Data::ShaderConfiguration config = rt->getConfiguration( pass );
737  config.addShader( Data::ShaderType_GEOMETRY,
738  RadiumEngine::getInstance()->getResourcesDir() +
739  "Shaders/Points/PointCloud.geom.glsl" );
740  rt->setConfiguration( config, pass );
741  }
742  };
743 
744  addGeomShader( DefaultRenderingPasses::LIGHTING_OPAQUE );
745  addGeomShader( DefaultRenderingPasses::LIGHTING_TRANSPARENT );
746  addGeomShader( DefaultRenderingPasses::Z_PREPASS );
747  // construct the parameter provider for the technique
748  auto pr = std::make_shared<PointCloudParameterProvider>( material, pointCloud );
749  rt->setParametersProvider( pr );
750  }
751  else { rt->setParametersProvider( material ); }
752  }
753  else {
754  // make the material the parameter provider for the technique
755  rt->setParametersProvider( material );
756  }
757  ro->setRenderTechnique( rt );
758  return true;
759 }
760 
761 void ForwardRenderer::updateShadowMaps() {
762  // Radium V2 : implement shadow mapping
763 }
764 
765 } // namespace Rendering
766 } // namespace Engine
767 } // namespace Ra
An engine mesh owning CoreGeometry, with indices.
Definition: Mesh.hpp:335
void addParameter(const std::string &name, T value, typename std::enable_if<!std::is_class< T > {}, bool >::type=true)
Add a parameter by value.
void addShader(ShaderType type, const std::string &name)
virtual void updateGL()=0
Update the OpenGL states used by the ShaderParameterProvider. These state could be the ones from an a...
void setUniform(const char *name, const T &value) const
Uniform setters.
DefaultLightManager. A simple Light Manager with a list of lights.
Definition: Cage.cpp:3
the set of viewing parameters extracted from the camera and given to the renderer