diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index 69fb0582..6bf1d12d 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -932,10 +932,6 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass, // FIXME orig: const string& shaderName = shaderKey.first; string shaderName = shaderKey.first; Shader::Type stype = (Shader::Type)shaderKey.second; - if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false) && - shaderName.substr(0, shaderName.find("/")) == "Shaders") { - shaderName = "Compositor/" + shaderName; - } string fileName = SGModelLib::findDataFile(shaderName, options); if (fileName.empty()) { @@ -1493,8 +1489,7 @@ static std::mutex realizeTechniques_lock; bool Effect::realizeTechniques(const SGReaderWriterOptions* options) { std::lock_guard g(realizeTechniques_lock); - if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false)) - mergeSchemesFallbacks(this, options); + mergeSchemesFallbacks(this, options); if (_isRealized) return true; diff --git a/simgear/scene/viewer/ClusteredShading.cxx b/simgear/scene/viewer/ClusteredShading.cxx index f9a648ae..a0031f38 100644 --- a/simgear/scene/viewer/ClusteredShading.cxx +++ b/simgear/scene/viewer/ClusteredShading.cxx @@ -18,11 +18,9 @@ #include -#include -#include #include +#include #include -#include #include #include @@ -33,15 +31,10 @@ namespace simgear { namespace compositor { -const int MAX_LIGHT_INDICES = 524288; // 1 MB (2 bytes per index) -const int MAX_POINTLIGHTS = 256; -const int MAX_SPOTLIGHTS = 256; - -// Size in floats (4 bytes) of the light struct to be passed to the GLSL shader. -// It must be a multiple of the size of a vec4 as per the std140 layout rules. -// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt -const int POINTLIGHT_BLOCK_SIZE = 20; -const int SPOTLIGHT_BLOCK_SIZE = 28; +const int MAX_POINTLIGHTS = 1024; +const int MAX_SPOTLIGHTS = 1024; +// A light group is a group of 4 light indices packed into a single RGBA texel +const int MAX_LIGHT_GROUPS_PER_CLUSTER = 255; ClusteredShading::ClusteredShading(osg::Camera *camera, const SGPropertyNode *config) : @@ -67,102 +60,76 @@ ClusteredShading::ClusteredShading(osg::Camera *camera, ss->addUniform(_slice_scale.get()); _slice_bias = new osg::Uniform("fg_ClusteredSliceBias", 0.0f); ss->addUniform(_slice_bias.get()); + _horizontal_tiles = new osg::Uniform("fg_ClusteredHorizontalTiles", 0); + ss->addUniform(_horizontal_tiles.get()); + _vertical_tiles = new osg::Uniform("fg_ClusteredVerticalTiles", 0); + ss->addUniform(_vertical_tiles.get()); - // Create and associate the light grid 3D texture + // Create and associate the cluster 3D texture //////////////////////////////////////////////////////////////////////////// - _light_grid = new osg::Image; - _light_grid->setInternalTextureFormat(GL_RGB32UI_EXT); - // Image allocation happens in setupSubfrusta() because the light grid size - // can change at runtime (viewport resize) + _clusters = new osg::Image; + // Image allocation happens in setupSubfrusta() because the number of + // clusters can change at runtime (viewport resize) - osg::ref_ptr light_grid_tex = new osg::Texture3D; - light_grid_tex->setResizeNonPowerOfTwoHint(false); - light_grid_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER); - light_grid_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER); - light_grid_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER); - light_grid_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST); - light_grid_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST); - light_grid_tex->setImage(_light_grid.get()); + osg::ref_ptr clusters_tex = new osg::Texture3D; + clusters_tex->setInternalFormat(GL_RGBA32F); + clusters_tex->setResizeNonPowerOfTwoHint(false); + clusters_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER); + clusters_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER); + clusters_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER); + clusters_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST); + clusters_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST); + clusters_tex->setImage(_clusters.get()); - int light_grid_bind_unit = config->getIntValue("grid-bind-unit", 11); + int clusters_bind_unit = config->getIntValue("clusters-bind-unit", 11); ss->setTextureAttributeAndModes( - light_grid_bind_unit, light_grid_tex.get(), osg::StateAttribute::ON); + clusters_bind_unit, clusters_tex.get(), osg::StateAttribute::ON); - osg::ref_ptr light_grid_uniform = - new osg::Uniform("fg_ClusteredLightGrid", light_grid_bind_unit); - ss->addUniform(light_grid_uniform.get()); + osg::ref_ptr clusters_uniform = + new osg::Uniform("fg_Clusters", clusters_bind_unit); + ss->addUniform(clusters_uniform.get()); - // Create and associate the light indices TBO - //////////////////////////////////////////////////////////////////////////// - _light_indices = new osg::Image; - _light_indices->allocateImage( - MAX_LIGHT_INDICES, 1, 1, GL_RED_INTEGER_EXT, GL_UNSIGNED_SHORT); + _pointlights = new osg::Image; + _pointlights->allocateImage(5, MAX_POINTLIGHTS, 1, GL_RGBA, GL_FLOAT); - osg::ref_ptr light_indices_tbo = - new osg::TextureBuffer; - light_indices_tbo->setInternalFormat(GL_R16UI); - light_indices_tbo->setImage(_light_indices.get()); + osg::ref_ptr pointlights_tex = new osg::Texture2D; + pointlights_tex->setInternalFormat(GL_RGBA32F); + pointlights_tex->setResizeNonPowerOfTwoHint(false); + pointlights_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER); + pointlights_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER); + pointlights_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER); + pointlights_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST); + pointlights_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST); + pointlights_tex->setImage(_pointlights.get()); - int light_indices_bind_unit = config->getIntValue("indices-bind-unit", 12); - ss->setTextureAttribute(light_indices_bind_unit, light_indices_tbo.get()); + int pointlights_bind_unit = config->getIntValue("pointlights-bind-unit", 12); + ss->setTextureAttributeAndModes( + pointlights_bind_unit, pointlights_tex.get(), osg::StateAttribute::ON); - osg::ref_ptr light_indices_uniform = - new osg::Uniform("fg_ClusteredLightIndices", light_indices_bind_unit); - ss->addUniform(light_indices_uniform.get()); + osg::ref_ptr pointlights_uniform = + new osg::Uniform("fg_ClusteredPointLights", pointlights_bind_unit); + ss->addUniform(pointlights_uniform.get()); - // Create and associate the pointlight data UBO - //////////////////////////////////////////////////////////////////////////// - _pointlight_data = new osg::FloatArray(MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE); + _spotlights = new osg::Image; + _spotlights->allocateImage(7, MAX_SPOTLIGHTS, 1, GL_RGBA, GL_FLOAT); - osg::ref_ptr pointlight_data_ubo = - new osg::UniformBufferObject; - _pointlight_data->setBufferObject(pointlight_data_ubo.get()); + osg::ref_ptr spotlights_tex = new osg::Texture2D; + spotlights_tex->setInternalFormat(GL_RGBA32F); + spotlights_tex->setResizeNonPowerOfTwoHint(false); + spotlights_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER); + spotlights_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER); + spotlights_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER); + spotlights_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST); + spotlights_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST); + spotlights_tex->setImage(_spotlights.get()); - int pointlight_ubo_index = config->getIntValue("pointlight-ubo-index", 5); -#if OSG_VERSION_LESS_THAN(3,6,0) - osg::ref_ptr pointlight_data_ubb = - new osg::UniformBufferBinding( - pointlight_ubo_index, - pointlight_data_ubo.get(), - 0, - MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); -#else - osg::ref_ptr pointlight_data_ubb = - new osg::UniformBufferBinding( - pointlight_ubo_index, - _pointlight_data.get(), - 0, - MAX_POINTLIGHTS * POINTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); -#endif - pointlight_data_ubb->setDataVariance(osg::Object::DYNAMIC); - ss->setAttribute(pointlight_data_ubb.get(), osg::StateAttribute::ON); + int spotlights_bind_unit = config->getIntValue("spotlights-bind-unit", 13); + ss->setTextureAttributeAndModes( + spotlights_bind_unit, spotlights_tex.get(), osg::StateAttribute::ON); - // Create and associate the spotlight data UBO - //////////////////////////////////////////////////////////////////////////// - _spotlight_data = new osg::FloatArray(MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE); - - osg::ref_ptr spotlight_data_ubo = - new osg::UniformBufferObject; - _spotlight_data->setBufferObject(spotlight_data_ubo.get()); - - int spotlight_ubo_index = config->getIntValue("spotlight-ubo-index", 6); -#if OSG_VERSION_LESS_THAN(3,6,0) - osg::ref_ptr spotlight_data_ubb = - new osg::UniformBufferBinding( - spotlight_ubo_index, - spotlight_data_ubo.get(), - 0, - MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); -#else - osg::ref_ptr spotlight_data_ubb = - new osg::UniformBufferBinding( - spotlight_ubo_index, - _spotlight_data.get(), - 0, - MAX_SPOTLIGHTS * SPOTLIGHT_BLOCK_SIZE * sizeof(GLfloat)); -#endif - spotlight_data_ubb->setDataVariance(osg::Object::DYNAMIC); - ss->setAttribute(spotlight_data_ubb.get(), osg::StateAttribute::ON); + osg::ref_ptr spotlights_uniform = + new osg::Uniform("fg_ClusteredSpotLights", spotlights_bind_unit); + ss->addUniform(spotlights_uniform.get()); } ClusteredShading::~ClusteredShading() @@ -198,6 +165,7 @@ ClusteredShading::update(const SGLightList &light_list) spot.direction = osg::Vec4f(0.0f, 0.0f, -1.0f, 0.0f) * (osg::computeLocalToWorld(light->getParentalNodePaths()[0]) * _camera->getViewMatrix()); + spot.direction.normalize(); float range = light->getRange(); float angle = light->getSpotCutoff() * SG_DEGREES_TO_RADIANS; @@ -237,11 +205,15 @@ ClusteredShading::update(const SGLightList &light_list) _x_step = (_tile_size / float(width)) * 2.0; _y_step = (_tile_size / float(height)) * 2.0; - _light_grid->allocateImage(_n_htiles, _n_vtiles, _depth_slices, - GL_RGB_INTEGER_EXT, GL_UNSIGNED_INT); + _clusters->allocateImage(_n_htiles, _n_vtiles * _depth_slices, + MAX_LIGHT_GROUPS_PER_CLUSTER + 1, + GL_RGBA, GL_FLOAT); _subfrusta.reset(new Subfrustum[_n_htiles * _n_vtiles]); } + _horizontal_tiles->set(_n_htiles); + _vertical_tiles->set(_n_vtiles); + for (int y = 0; y < _n_vtiles; ++y) { float ymin = -1.0 + _y_step * float(y); float ymax = ymin + _y_step; @@ -270,8 +242,6 @@ ClusteredShading::update(const SGLightList &light_list) } } - _global_light_count = 0; - if (_depth_slices == 1) { // Just run the light assignment on the main thread to avoid the // unnecessary threading overhead @@ -289,9 +259,8 @@ ClusteredShading::update(const SGLightList &light_list) } // Force upload of the image data - _light_grid->dirty(); + _clusters->dirty(); - // Upload pointlight and spotlight data writePointlightData(); writeSpotlightData(); } @@ -309,93 +278,112 @@ ClusteredShading::threadFunc(int thread_id) void ClusteredShading::assignLightsToSlice(int slice) { - size_t z_offset = slice * _n_htiles * _n_vtiles; - float near = getDepthForSlice(slice); float far = getDepthForSlice(slice + 1); + osg::Vec4f near_plane(0.0f, 0.0f, -1.0f, -near); osg::Vec4f far_plane (0.0f, 0.0f, 1.0f, far); - GLuint *grid = reinterpret_cast(_light_grid->data()); - GLushort *indices = reinterpret_cast(_light_indices->data()); + GLfloat *clusters = reinterpret_cast(_clusters->data()); - for (int i = 0; i < (_n_htiles * _n_vtiles); ++i) { - Subfrustum subfrustum = _subfrusta[i]; - subfrustum.plane[4] = near_plane; - subfrustum.plane[5] = far_plane; + for (int j = 0; j < _n_vtiles; ++j) { + for (int i = 0; i < _n_htiles; ++i) { + Subfrustum subfrustum = _subfrusta[i]; + subfrustum.plane[4] = near_plane; + subfrustum.plane[5] = far_plane; - GLuint start_offset = _global_light_count; - GLuint local_point_count = 0; - GLuint local_spot_count = 0; + GLuint term = 0; + GLuint point_count = 0; + GLuint spot_count = 0; + GLuint total_count = 0; - // Test point lights - for (GLushort point_iterator = 0; - point_iterator < _point_bounds.size(); - ++point_iterator) { - PointlightBound point = _point_bounds[point_iterator]; + // Test point lights + for (GLushort point_iterator = 0; + point_iterator < _point_bounds.size(); + ++point_iterator) { + PointlightBound point = _point_bounds[point_iterator]; - // Perform frustum-sphere collision tests - float distance = 0.0f; - for (int j = 0; j < 6; j++) { - distance = subfrustum.plane[j] * point.position + point.range; - if (distance <= 0.0f) - break; + // Perform frustum-sphere collision tests + float distance = 0.0f; + for (int n = 0; n < 6; ++n) { + distance = subfrustum.plane[n] * point.position + point.range; + if (distance <= 0.0f) + break; + } + + if (distance > 0.0f) { + size_t p = + (total_count / 4 + 1) * _n_htiles * _n_vtiles * _depth_slices + + slice * _n_htiles * _n_vtiles + + j * _n_htiles + + i; + clusters[p * 4 + term] = float(point_iterator); + ++term; + ++point_count; + ++total_count; + } + + if (term >= 4) + term = 0; + + if ((total_count / 4 + term) >= MAX_LIGHT_GROUPS_PER_CLUSTER) { + throw sg_range_exception( + "Number of light groups per cluster is over the hardcoded limit (" + + std::to_string(MAX_LIGHT_GROUPS_PER_CLUSTER) + ")"); + } } - if (distance > 0.0f) { - // Update light index list - indices[_global_light_count] = point_iterator; - ++local_point_count; - ++_global_light_count; // Atomic increment + // Test spot lights + for (GLushort spot_iterator = 0; + spot_iterator < _spot_bounds.size(); + ++spot_iterator) { + SpotlightBound spot = _spot_bounds[spot_iterator]; + + // Perform frustum-sphere collision tests + float distance = 0.0f; + for (int n = 0; n < 6; ++n) { + distance = subfrustum.plane[n] * spot.bounding_sphere.center + + spot.bounding_sphere.radius; + if (distance <= 0.0f) + break; + } + + if (distance > 0.0f) { + size_t p = + (total_count / 4 + 1) * _n_htiles * _n_vtiles * _depth_slices + + slice * _n_htiles * _n_vtiles + + j * _n_htiles + + i; + clusters[p * 4 + term] = float(spot_iterator); + ++term; + ++spot_count; + ++total_count; + } + + if (term >= 4) + term = 0; + + if ((total_count / 4 + term) >= MAX_LIGHT_GROUPS_PER_CLUSTER) { + throw sg_range_exception( + "Number of light groups per cluster is over the hardcoded limit (" + + std::to_string(MAX_LIGHT_GROUPS_PER_CLUSTER) + ")"); + } } - if (_global_light_count >= MAX_LIGHT_INDICES) { - throw sg_range_exception( - "Clustered shading light index count is over the hardcoded limit (" - + std::to_string(MAX_LIGHT_INDICES) + ")"); - } + clusters[(slice * _n_htiles * _n_vtiles + + j * _n_htiles + + i) * 4 + 0] = point_count; + clusters[(slice * _n_htiles * _n_vtiles + + j * _n_htiles + + i) * 4 + 1] = spot_count; } - - // Test spot lights - for (GLushort spot_iterator = 0; - spot_iterator < _spot_bounds.size(); - ++spot_iterator) { - SpotlightBound spot = _spot_bounds[spot_iterator]; - - // Perform frustum-sphere collision tests - float distance = 0.0f; - for (int j = 0; j < 6; j++) { - distance = subfrustum.plane[j] * spot.bounding_sphere.center - + spot.bounding_sphere.radius; - if (distance <= 0.0f) - break; - } - - if (distance > 0.0f) { - // Update light index list - indices[_global_light_count] = spot_iterator; - ++local_spot_count; - ++_global_light_count; // Atomic increment - } - - if (_global_light_count >= MAX_LIGHT_INDICES) { - throw sg_range_exception( - "Clustered shading light index count is over the hardcoded limit (" - + std::to_string(MAX_LIGHT_INDICES) + ")"); - } - } - - // Update light grid - grid[(z_offset + i) * 3 + 0] = start_offset; - grid[(z_offset + i) * 3 + 1] = local_point_count; - grid[(z_offset + i) * 3 + 2] = local_spot_count; } } void ClusteredShading::writePointlightData() { - GLfloat *data = reinterpret_cast(&(*_pointlight_data)[0]); + GLfloat *data = reinterpret_cast(_pointlights->data()); for (const auto &point : _point_bounds) { // vec4 position @@ -425,13 +413,13 @@ ClusteredShading::writePointlightData() *data++ = point.light->getRange(); // No padding needed as the resulting size is a multiple of vec4 } - _pointlight_data->dirty(); + _pointlights->dirty(); } void ClusteredShading::writeSpotlightData() { - GLfloat *data = reinterpret_cast(&(*_spotlight_data)[0]); + GLfloat *data = reinterpret_cast(_spotlights->data()); for (const auto &spot : _spot_bounds) { // vec4 position @@ -468,9 +456,11 @@ ClusteredShading::writeSpotlightData() *data++ = spot.cos_cutoff; // float exponent *data++ = spot.light->getSpotExponent(); - // Needs 2N padding (8 bytes) + // Needs 2 float padding + *data++ = 0.0f; + *data++ = 0.0f; } - _spotlight_data->dirty(); + _spotlights->dirty(); } float diff --git a/simgear/scene/viewer/ClusteredShading.hxx b/simgear/scene/viewer/ClusteredShading.hxx index fed11f1f..1fa6366a 100644 --- a/simgear/scene/viewer/ClusteredShading.hxx +++ b/simgear/scene/viewer/ClusteredShading.hxx @@ -67,33 +67,32 @@ protected: osg::ref_ptr _slice_scale; osg::ref_ptr _slice_bias; + osg::ref_ptr _horizontal_tiles; + osg::ref_ptr _vertical_tiles; - int _tile_size; - int _depth_slices; - int _num_threads; - int _slices_per_thread; - int _slices_remainder; + int _tile_size = 0; + int _depth_slices = 0; + int _num_threads = 0; + int _slices_per_thread = 0; + int _slices_remainder = 0; - float _zNear; - float _zFar; + float _zNear = 0.0f; + float _zFar = 0.0f; - int _n_htiles; - int _n_vtiles; + int _n_htiles = 0; + int _n_vtiles = 0; - float _x_step; - float _y_step; + float _x_step = 0.0f; + float _y_step = 0.0f; - osg::ref_ptr _light_grid; - osg::ref_ptr _light_indices; - osg::ref_ptr _pointlight_data; - osg::ref_ptr _spotlight_data; + osg::ref_ptr _clusters; + osg::ref_ptr _pointlights; + osg::ref_ptr _spotlights; std::unique_ptr _subfrusta; std::vector _point_bounds; std::vector _spot_bounds; - - std::atomic _global_light_count; }; } // namespace compositor diff --git a/simgear/scene/viewer/Compositor.cxx b/simgear/scene/viewer/Compositor.cxx index ffaccdba..3be5bb69 100644 --- a/simgear/scene/viewer/Compositor.cxx +++ b/simgear/scene/viewer/Compositor.cxx @@ -55,6 +55,11 @@ Compositor::create(osg::View *view, osg::ref_ptr compositor = new Compositor(view, gc, viewport); compositor->_name = property_list->getStringValue("name"); + gc->getState()->setUseModelViewAndProjectionUniforms( + property_list->getBoolValue("use-osg-uniforms", false)); + gc->getState()->setUseVertexAttributeAliasing( + property_list->getBoolValue("use-vertex-attribute-aliasing", false)); + // Read all buffers first so passes can use them PropertyList p_buffers = property_list->getChildren("buffer"); for (auto const &p_buffer : p_buffers) { @@ -139,6 +144,11 @@ Compositor::Compositor(osg::View *view, Compositor::~Compositor() { + // Remove slave cameras from the viewer + for (const auto &pass : _passes) { + unsigned int index = _view->findSlaveIndexForCamera(pass->camera); + _view->removeSlave(index); + } } void diff --git a/simgear/scene/viewer/CompositorBuffer.cxx b/simgear/scene/viewer/CompositorBuffer.cxx index 80071ef4..a8cd2209 100644 --- a/simgear/scene/viewer/CompositorBuffer.cxx +++ b/simgear/scene/viewer/CompositorBuffer.cxx @@ -104,7 +104,7 @@ buildBuffer(Compositor *compositor, const SGPropertyNode *node, } osg::ref_ptr buffer = new Buffer; - osg::Texture *texture; + osg::Texture *texture = nullptr; int width = 0; const SGPropertyNode *p_width = getPropertyChild(node, "width"); diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 625aae76..d1d23303 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -99,7 +99,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, camera->setCullMaskLeft(pass->cull_mask); camera->setCullMaskRight(pass->cull_mask); - osg::Vec4f clear_color(0.0f, 0.0f, 0.0f, 0.0f); + osg::Vec4f clear_color(0.0f, 0.0f, 0.0f, 1.0f); const SGPropertyNode *p_clear_color = root->getChild("clear-color"); if (p_clear_color) clear_color = toOsg(p_clear_color->getValue()); @@ -376,9 +376,9 @@ RegisterPassBuilder registerQuadPass("quad"); //------------------------------------------------------------------------------ -class ShadowMapCullCallback : public osg::NodeCallback { +class CSMCullCallback : public osg::NodeCallback { public: - ShadowMapCullCallback(const std::string &suffix) { + CSMCullCallback(const std::string &suffix) { _light_matrix_uniform = new osg::Uniform( osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix); } @@ -445,14 +445,16 @@ protected: osg::observer_ptr _light; }; -struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback { +struct CSMUpdateCallback : public Pass::PassUpdateCallback { public: - ShadowMapUpdateCallback(ShadowMapCullCallback *cull_callback, - const std::string &light_name, - float near_m, float far_m, - int sm_width, int sm_height) : + CSMUpdateCallback(CSMCullCallback *cull_callback, + const std::string &light_name, + bool render_at_night, + float near_m, float far_m, + int sm_width, int sm_height) : _cull_callback(cull_callback), _light_finder(new LightFinder(light_name)), + _render_at_night(render_at_night), _near_m(near_m), _far_m(far_m) { _half_sm_size = osg::Vec2d((double)sm_width, (double)sm_height) / 2.0; @@ -485,6 +487,19 @@ public: osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix); _cull_callback->setRealInverseViewMatrix(view_inverse); + if (!_render_at_night) { + osg::Vec3 camera_pos = osg::Vec3(0.0f, 0.0f, 0.0f) * view_inverse; + camera_pos.normalize(); + float cos_light_angle = camera_pos * light_dir; + if (cos_light_angle < -0.1) { + // Night + camera->setCullMask(0); + } else { + // Day + camera->setCullMask(pass.cull_mask); + } + } + // Calculate the light's point of view transformation matrices. // Taken from Project Rembrandt. double left, right, bottom, top, zNear, zFar; @@ -532,14 +547,15 @@ public: } protected: - osg::observer_ptr _cull_callback; + osg::observer_ptr _cull_callback; osg::ref_ptr _light_finder; + bool _render_at_night; float _near_m; float _far_m; osg::Vec2d _half_sm_size; }; -struct ShadowMapPassBuilder : public PassBuilder { +struct CSMPassBuilder : public PassBuilder { virtual Pass *build(Compositor *compositor, const SGPropertyNode *root, const SGReaderWriterOptions *options) { osg::ref_ptr pass = PassBuilder::build(compositor, root, options); @@ -550,24 +566,27 @@ struct ShadowMapPassBuilder : public PassBuilder { //camera->setComputeNearFarMode( // osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); - ShadowMapCullCallback *cull_callback = new ShadowMapCullCallback(pass->name); + CSMCullCallback *cull_callback = new CSMCullCallback(pass->name); camera->setCullCallback(cull_callback); - std::string light_name = root->getStringValue("light-name"); + // Use the Sun as the default light source + std::string light_name = root->getStringValue("light-name", "FGLightSource"); + bool render_at_night = root->getBoolValue("render-at-night", true); float near_m = root->getFloatValue("near-m"); float far_m = root->getFloatValue("far-m"); int sm_width = camera->getViewport()->width(); int sm_height = camera->getViewport()->height(); - pass->update_callback = new ShadowMapUpdateCallback( + pass->update_callback = new CSMUpdateCallback( cull_callback, light_name, + render_at_night, near_m, far_m, sm_width, sm_height); return pass.release(); } }; -RegisterPassBuilder registerShadowMapPass("shadow-map"); +RegisterPassBuilder registerCSMPass("csm"); //------------------------------------------------------------------------------ @@ -709,8 +728,8 @@ public: if (!shadow_pass_name.empty()) { Pass *shadow_pass = compositor->getPass(shadow_pass_name); if (shadow_pass) { - ShadowMapCullCallback *cullcb = - dynamic_cast( + CSMCullCallback *cullcb = + dynamic_cast( shadow_pass->camera->getCullCallback()); if (cullcb) { camera->getOrCreateStateSet()->addUniform(