From b3b863a3c46c358ae3faa4d657b08674d4736dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Thu, 15 Apr 2021 11:00:09 +0200 Subject: [PATCH] Preliminary changes for the HDR pipeline - Add an Effect to stars and planets so they don't disappear when not using the fixed-pipeline. - Allow usage of the shadow mapping related uniforms in 'quad' passes as well. - Add extra buffer formats (some of them only work under the core profile). - Better handling of mipmapping in the Compositor. --- simgear/scene/sky/sky.cxx | 4 +- simgear/scene/sky/stars.cxx | 16 +- simgear/scene/sky/stars.hxx | 7 +- simgear/scene/viewer/Compositor.cxx | 6 +- simgear/scene/viewer/Compositor.hxx | 2 +- simgear/scene/viewer/CompositorBuffer.cxx | 33 ++- simgear/scene/viewer/CompositorPass.cxx | 234 ++++++++++++---------- 7 files changed, 179 insertions(+), 123 deletions(-) diff --git a/simgear/scene/sky/sky.cxx b/simgear/scene/sky/sky.cxx index e2cb1efb..6b8924dd 100644 --- a/simgear/scene/sky/sky.cxx +++ b/simgear/scene/sky/sky.cxx @@ -96,10 +96,10 @@ void SGSky::build( double h_radius_m, pre_transform->addChild(_ephTransform.get()); planets = new SGStars; - _ephTransform->addChild( planets->build(eph.getNumPlanets(), eph.getPlanets(), h_radius_m) ); + _ephTransform->addChild( planets->build(eph.getNumPlanets(), eph.getPlanets(), h_radius_m, options) ); stars = new SGStars(property_tree_node); - _ephTransform->addChild( stars->build(eph.getNumStars(), eph.getStars(), h_radius_m) ); + _ephTransform->addChild( stars->build(eph.getNumStars(), eph.getStars(), h_radius_m, options) ); moon = new SGMoon; _ephTransform->addChild( moon->build(tex_path, moon_size) ); diff --git a/simgear/scene/sky/stars.cxx b/simgear/scene/sky/stars.cxx index a079cdee..84af1fea 100644 --- a/simgear/scene/sky/stars.cxx +++ b/simgear/scene/sky/stars.cxx @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include #include @@ -48,6 +51,8 @@ #include "stars.hxx" +using namespace simgear; + // Constructor SGStars::SGStars( SGPropertyNode* props ) : old_phase(-1) @@ -67,8 +72,15 @@ SGStars::~SGStars( void ) { // initialize the stars object and connect it into our scene graph root osg::Node* -SGStars::build( int num, const SGVec3d star_data[], double star_dist ) { - osg::Geode* geode = new osg::Geode; +SGStars::build( int num, const SGVec3d star_data[], double star_dist, + SGReaderWriterOptions* options ) { + EffectGeode* geode = new EffectGeode; + geode->setName("Stars"); + + Effect* effect = makeEffect("Effects/stars", true, options); + if (effect) + geode->setEffect(effect); + osg::StateSet* stateSet = geode->getOrCreateStateSet(); stateSet->setRenderBinDetails(-9, "RenderBin"); diff --git a/simgear/scene/sky/stars.hxx b/simgear/scene/sky/stars.hxx index 10b0140c..6efd7f1d 100644 --- a/simgear/scene/sky/stars.hxx +++ b/simgear/scene/sky/stars.hxx @@ -35,6 +35,10 @@ #include #include +namespace simgear { +class SGReaderWriterOptions; +} + class SGStars : public SGReferenced { osg::ref_ptr cl; @@ -52,7 +56,8 @@ public: ~SGStars( void ); // initialize the stars structure - osg::Node* build( int num, const SGVec3d star_data[], double star_dist ); + osg::Node* build( int num, const SGVec3d star_data[], double star_dist, + simgear::SGReaderWriterOptions* options ); // repaint the planet magnitudes based on current value of // sun_angle in degrees relative to verticle (so we can make them diff --git a/simgear/scene/viewer/Compositor.cxx b/simgear/scene/viewer/Compositor.cxx index ede9d0f2..5b1d8a70 100644 --- a/simgear/scene/viewer/Compositor.cxx +++ b/simgear/scene/viewer/Compositor.cxx @@ -34,7 +34,7 @@ #include "CompositorUtil.hxx" -class LightDirectionCallback : public osg::Uniform::Callback { +class SunDirectionCallback : public osg::Uniform::Callback { public: virtual void operator()(osg::Uniform *uniform, osg::NodeVisitor *nv) { SGUpdateVisitor *uv = dynamic_cast(nv); @@ -140,10 +140,10 @@ Compositor::Compositor(osg::View *view, new osg::Uniform("fg_CameraPositionGeod", osg::Vec3f()), new osg::Uniform("fg_NearFarPlanes", osg::Vec3f()), new osg::Uniform("fg_Fcoef", 0.0f), - new osg::Uniform("fg_LightDirection", osg::Vec3f()) + new osg::Uniform("fg_SunDirection", osg::Vec3f()) } { - _uniforms[LIGHT_DIRECTION]->setUpdateCallback(new LightDirectionCallback); + _uniforms[SUN_DIRECTION]->setUpdateCallback(new SunDirectionCallback); } Compositor::~Compositor() diff --git a/simgear/scene/viewer/Compositor.hxx b/simgear/scene/viewer/Compositor.hxx index 0fccc2d9..9e8f17f2 100644 --- a/simgear/scene/viewer/Compositor.hxx +++ b/simgear/scene/viewer/Compositor.hxx @@ -59,7 +59,7 @@ public: CAMERA_POSITION_GEOD, NEAR_FAR_PLANES, FCOEF, - LIGHT_DIRECTION, + SUN_DIRECTION, TOTAL_BUILTIN_UNIFORMS }; diff --git a/simgear/scene/viewer/CompositorBuffer.cxx b/simgear/scene/viewer/CompositorBuffer.cxx index 869cf12d..476324c9 100644 --- a/simgear/scene/viewer/CompositorBuffer.cxx +++ b/simgear/scene/viewer/CompositorBuffer.cxx @@ -16,6 +16,7 @@ #include "CompositorBuffer.hxx" +#include #include #include #include @@ -23,7 +24,6 @@ #include #include #include -#include #include #include @@ -45,13 +45,18 @@ struct BufferFormat { PropStringMap buffer_format_map { {"rgb8", {GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE}}, {"rgba8", {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}}, - {"rgb16f", {GL_RGB16F_ARB, GL_RGBA, GL_FLOAT}}, + {"rgb16f", {GL_RGB16F_ARB, GL_RGBA, GL_HALF_FLOAT}}, {"rgb32f", {GL_RGB32F_ARB, GL_RGBA, GL_FLOAT}}, - {"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}}, + {"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_HALF_FLOAT}}, {"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}}, + {"r8", {GL_R8, GL_RED, GL_UNSIGNED_BYTE}}, + {"r16f", {GL_R16F, GL_RED, GL_HALF_FLOAT}}, {"r32f", {GL_R32F, GL_RED, GL_FLOAT}}, - {"rg16f", {GL_RG16F, GL_RG, GL_FLOAT}}, + {"rg16f", {GL_RG16F, GL_RG, GL_HALF_FLOAT}}, {"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}}, +#if defined(OSG_GL3_FEATURES) + {"r11g11b10f", {GL_R11F_G11F_B10F, GL_RGB, GL_FLOAT}}, +#endif {"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}}, {"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}}, {"depth32f", {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}}, @@ -136,17 +141,32 @@ buildBuffer(Compositor *compositor, const SGPropertyNode *node, if (p_depth) depth = p_depth->getIntValue(); + auto get_mipmap_levels = [&]() -> int { + int mipmap_levels = 0; + const SGPropertyNode *p_mipmap_levels = node->getNode("mipmap-levels"); + if (p_mipmap_levels) { + if (p_mipmap_levels->getStringValue() == std::string("auto")) + mipmap_levels = 1 + floor(log2((float)max(max(width, height), depth))); + else + mipmap_levels = p_mipmap_levels->getIntValue(); + } + return mipmap_levels; + }; + if (type == "1d") { osg::Texture1D *tex1D = new osg::Texture1D; tex1D->setTextureWidth(width); + tex1D->setNumMipmapLevels(get_mipmap_levels()); texture = tex1D; } else if (type == "2d") { osg::Texture2D *tex2D = new osg::Texture2D; tex2D->setTextureSize(width, height); + tex2D->setNumMipmapLevels(get_mipmap_levels()); texture = tex2D; } else if (type == "2d-array") { osg::Texture2DArray *tex2D_array = new osg::Texture2DArray; tex2D_array->setTextureSize(width, height, depth); + tex2D_array->setNumMipmapLevels(get_mipmap_levels()); texture = tex2D_array; } else if (type == "2d-multisample") { osg::Texture2DMultisample *tex2DMS = new osg::Texture2DMultisample; @@ -156,6 +176,7 @@ buildBuffer(Compositor *compositor, const SGPropertyNode *node, } else if (type == "3d") { osg::Texture3D *tex3D = new osg::Texture3D; tex3D->setTextureSize(width, height, depth); + tex3D->setNumMipmapLevels(get_mipmap_levels()); texture = tex3D; } else if (type == "rect") { osg::TextureRectangle *tex_rect = new osg::TextureRectangle; @@ -164,6 +185,7 @@ buildBuffer(Compositor *compositor, const SGPropertyNode *node, } else if (type == "cubemap") { osg::TextureCubeMap *tex_cubemap = new osg::TextureCubeMap; tex_cubemap->setTextureSize(width, height); + tex_cubemap->setNumMipmapLevels(get_mipmap_levels()); texture = tex_cubemap; } else { SG_LOG(SG_INPUT, SG_ALERT, "Unknown texture type '" << type << "'"); @@ -181,7 +203,8 @@ buildBuffer(Compositor *compositor, const SGPropertyNode *node, texture->setSourceType(format.source_type); } else { texture->setInternalFormat(GL_RGBA); - SG_LOG(SG_INPUT, SG_WARN, "Unknown buffer format specified, using RGBA"); + SG_LOG(SG_INPUT, SG_WARN, "Unknown buffer format '" + << node->getStringValue("format") << "', using RGBA"); } osg::Texture::FilterMode filter_mode = osg::Texture::LINEAR; diff --git a/simgear/scene/viewer/CompositorPass.cxx b/simgear/scene/viewer/CompositorPass.cxx index 787fa9ad..78049fc8 100644 --- a/simgear/scene/viewer/CompositorPass.cxx +++ b/simgear/scene/viewer/CompositorPass.cxx @@ -55,6 +55,76 @@ PropStringMap buffer_component_map = { {"packed-depth-stencil", osg::Camera::PACKED_DEPTH_STENCIL_BUFFER} }; +class CSMCullCallback : public osg::NodeCallback { +public: + CSMCullCallback(const std::string &suffix) { + _light_matrix_uniform = new osg::Uniform( + osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix); + } + + virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) { + osg::Camera *camera = static_cast(node); + + traverse(node, nv); + + // The light matrix uniform is updated after the traverse in case the + // OSG near/far plane calculations were enabled + osg::Matrixf light_matrix = + // Include the real camera inverse view matrix because if the shader + // used world coordinates, there would be precision issues. + _real_inverse_view * + camera->getViewMatrix() * + camera->getProjectionMatrix() * + // Bias matrices + osg::Matrix::translate(1.0, 1.0, 1.0) * + osg::Matrix::scale(0.5, 0.5, 0.5); + _light_matrix_uniform->set(light_matrix); + } + + void setRealInverseViewMatrix(const osg::Matrix &matrix) { + _real_inverse_view = matrix; + } + + osg::Uniform *getLightMatrixUniform() const { + return _light_matrix_uniform.get(); + } + +protected: + osg::Matrix _real_inverse_view; + osg::ref_ptr _light_matrix_uniform; +}; + +class LightFinder : public osg::NodeVisitor { +public: + LightFinder(const std::string &name) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _name(name) {} + virtual void apply(osg::Node &node) { + // Only traverse the scene graph if we haven't found a light yet (or if + // the one we found earlier is no longer valid). + if (getLight().valid()) + return; + + if (node.getName() == _name) { + osg::LightSource *light_source = + dynamic_cast(&node); + if (light_source) + _light = light_source->getLight(); + } + + traverse(node); + } + osg::ref_ptr getLight() const { + osg::ref_ptr light_ref; + _light.lock(light_ref); + return light_ref; + } +protected: + std::string _name; + osg::observer_ptr _light; +}; + +//------------------------------------------------------------------------------ Pass * PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, @@ -128,7 +198,8 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, osg::DisplaySettings::ImplicitBufferAttachmentMask implicit_attachments = 0; std::stringstream att_ss; std::string att_bit; - att_ss << root->getStringValue("implicit-attachment-mask", "color depth"); + // No implicit attachments by default + att_ss << root->getStringValue("implicit-attachment-mask", ""); while (att_ss >> att_bit) { if (att_bit == "color") implicit_attachments |= osg::DisplaySettings::IMPLICIT_COLOR_BUFFER_ATTACHMENT; else if (att_bit == "depth") implicit_attachments |= osg::DisplaySettings::IMPLICIT_DEPTH_BUFFER_ATTACHMENT; @@ -136,6 +207,30 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, } camera->setImplicitBufferAttachmentMask(implicit_attachments, implicit_attachments); + // Set some global state + camera->getOrCreateStateSet()->setMode(GL_TEXTURE_CUBE_MAP_SEAMLESS, + osg::StateAttribute::ON); + + PropertyList p_shadow_passes = root->getChildren("use-shadow-pass"); + for (const auto &p_shadow_pass : p_shadow_passes) { + std::string shadow_pass_name = p_shadow_pass->getStringValue(); + if (!shadow_pass_name.empty()) { + Pass *shadow_pass = compositor->getPass(shadow_pass_name); + if (shadow_pass) { + CSMCullCallback *cullcb = + dynamic_cast( + shadow_pass->camera->getCullCallback()); + if (cullcb) { + camera->getOrCreateStateSet()->addUniform( + cullcb->getLightMatrixUniform()); + } else { + SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '" + << shadow_pass_name << "is not a shadow pass"); + } + } + } + } + PropertyList p_bindings = root->getChildren("binding"); for (auto const &p_binding : p_bindings) { if (!checkConditional(p_binding)) @@ -242,6 +337,7 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, multisample_samples, multisample_color_samples); + float mipmap_resize_factor = 1.0f / pow(2.0f, float(level)); if (!viewport_absolute && (p_attachment->getIndex() == viewport_attachment)) { if ((buffer->width_scale == 0.0f) && @@ -250,8 +346,10 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, // relative coordinates to shape the viewport. float x = p_viewport->getFloatValue("x", 0.0f); float y = p_viewport->getFloatValue("y", 0.0f); - float width = p_viewport->getFloatValue("width", 1.0f); - float height = p_viewport->getFloatValue("height", 1.0f); + float width = p_viewport->getFloatValue("width", 1.0f) + * mipmap_resize_factor; + float height = p_viewport->getFloatValue("height", 1.0f) + * mipmap_resize_factor; camera->setViewport(x * texture->getTextureWidth(), y * texture->getTextureHeight(), width * texture->getTextureWidth(), @@ -260,13 +358,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root, // This is a pass that should match the physical viewport // size. Store the scales so we can resize the pass later // if the physical viewport changes size. - pass->viewport_width_scale = buffer->width_scale; - pass->viewport_height_scale = buffer->height_scale; + pass->viewport_width_scale = buffer->width_scale + * mipmap_resize_factor; + pass->viewport_height_scale = buffer->height_scale + * mipmap_resize_factor; camera->setViewport( 0, 0, - buffer->width_scale * compositor->getViewport()->width(), - buffer->height_scale * compositor->getViewport()->height()); + pass->viewport_width_scale * compositor->getViewport()->width(), + pass->viewport_height_scale * compositor->getViewport()->height()); } } } catch (sg_exception &e) { @@ -332,6 +432,10 @@ public: for (const auto &uniform : compositor->getUniforms()) ss->addUniform(uniform); + int cubemap_face = root->getIntValue("cubemap-face", -1); + if (cubemap_face >= 0) + ss->addUniform(new osg::Uniform("fg_CubemapFace", cubemap_face)); + return pass.release(); } protected: @@ -388,75 +492,6 @@ RegisterPassBuilder registerQuadPass("quad"); //------------------------------------------------------------------------------ -class CSMCullCallback : public osg::NodeCallback { -public: - CSMCullCallback(const std::string &suffix) { - _light_matrix_uniform = new osg::Uniform( - osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix); - } - - virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) { - osg::Camera *camera = static_cast(node); - - traverse(node, nv); - - // The light matrix uniform is updated after the traverse in case the - // OSG near/far plane calculations were enabled - osg::Matrixf light_matrix = - // Include the real camera inverse view matrix because if the shader - // used world coordinates, there would be precision issues. - _real_inverse_view * - camera->getViewMatrix() * - camera->getProjectionMatrix() * - // Bias matrices - osg::Matrix::translate(1.0, 1.0, 1.0) * - osg::Matrix::scale(0.5, 0.5, 0.5); - _light_matrix_uniform->set(light_matrix); - } - - void setRealInverseViewMatrix(const osg::Matrix &matrix) { - _real_inverse_view = matrix; - } - - osg::Uniform *getLightMatrixUniform() const { - return _light_matrix_uniform.get(); - } - -protected: - osg::Matrix _real_inverse_view; - osg::ref_ptr _light_matrix_uniform; -}; - -class LightFinder : public osg::NodeVisitor { -public: - LightFinder(const std::string &name) : - osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), - _name(name) {} - virtual void apply(osg::Node &node) { - // Only traverse the scene graph if we haven't found a light yet (or if - // the one we found earlier is no longer valid). - if (getLight().valid()) - return; - - if (node.getName() == _name) { - osg::LightSource *light_source = - dynamic_cast(&node); - if (light_source) - _light = light_source->getLight(); - } - - traverse(node); - } - osg::ref_ptr getLight() const { - osg::ref_ptr light_ref; - _light.lock(light_ref); - return light_ref; - } -protected: - std::string _name; - osg::observer_ptr _light; -}; - struct CSMUpdateCallback : public Pass::PassUpdateCallback { public: CSMUpdateCallback(CSMCullCallback *cull_callback, @@ -631,26 +666,26 @@ public: camera->setViewMatrix(view_matrix); camera->setProjectionMatrix(new_proj_matrix); } else { - osg::Vec3 camera_pos = osg::Vec3(0.0, 0.0, 0.0) * - osg::Matrix::inverse(view_matrix); + osg::Vec4d camera_pos4 = osg::Vec4d(0.0, 0.0, 0.0, 1.0) * + osg::Matrixd::inverse(view_matrix); + osg::Vec3d camera_pos(camera_pos4.x(), camera_pos4.y(), camera_pos4.z()); - typedef std::pair CubemapFace; + typedef std::pair CubemapFace; const CubemapFace id[] = { - CubemapFace(osg::Vec3( 1, 0, 0), osg::Vec3( 0, -1, 0)), // +X - CubemapFace(osg::Vec3(-1, 0, 0), osg::Vec3( 0, -1, 0)), // -X - CubemapFace(osg::Vec3( 0, 1, 0), osg::Vec3( 0, 0, 1)), // +Y - CubemapFace(osg::Vec3( 0, -1, 0), osg::Vec3( 0, 0, -1)), // -Y - CubemapFace(osg::Vec3( 0, 0, 1), osg::Vec3( 0, -1, 0)), // +Z - CubemapFace(osg::Vec3( 0, 0, -1), osg::Vec3( 0, -1, 0)) // -Z + CubemapFace(osg::Vec3d( 1.0, 0.0, 0.0), osg::Vec3d( 0.0, -1.0, 0.0)), // +X + CubemapFace(osg::Vec3d(-1.0, 0.0, 0.0), osg::Vec3d( 0.0, -1.0, 0.0)), // -X + CubemapFace(osg::Vec3d( 0.0, 1.0, 0.0), osg::Vec3d( 0.0, 0.0, 1.0)), // +Y + CubemapFace(osg::Vec3d( 0.0, -1.0, 0.0), osg::Vec3d( 0.0, 0.0, -1.0)), // -Y + CubemapFace(osg::Vec3d( 0.0, 0.0, 1.0), osg::Vec3d( 0.0, -1.0, 0.0)), // +Z + CubemapFace(osg::Vec3d( 0.0, 0.0, -1.0), osg::Vec3d( 0.0, -1.0, 0.0)) // -Z }; - osg::Matrix cubemap_view_matrix; + osg::Matrixd cubemap_view_matrix; cubemap_view_matrix.makeLookAt(camera_pos, camera_pos + id[_cubemap_face].first, - camera_pos + id[_cubemap_face].second); + id[_cubemap_face].second); camera->setViewMatrix(cubemap_view_matrix); - camera->setProjectionMatrixAsFrustum(-1.0, 1.0, -1.0, 1.0, - znear, zfar); + camera->setProjectionMatrixAsPerspective(90.0, 1.0, znear, zfar); } } protected: @@ -706,29 +741,10 @@ public: float zFar = root->getFloatValue("z-far", 0.0f); pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar); - PropertyList p_shadow_passes = root->getChildren("use-shadow-pass"); - for (const auto &p_shadow_pass : p_shadow_passes) { - std::string shadow_pass_name = p_shadow_pass->getStringValue(); - if (!shadow_pass_name.empty()) { - Pass *shadow_pass = compositor->getPass(shadow_pass_name); - if (shadow_pass) { - CSMCullCallback *cullcb = - dynamic_cast( - shadow_pass->camera->getCullCallback()); - if (cullcb) { - camera->getOrCreateStateSet()->addUniform( - cullcb->getLightMatrixUniform()); - } else { - SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '" - << shadow_pass_name << "is not a shadow pass"); - } - } - } - } - osg::StateSet *ss = camera->getOrCreateStateSet(); auto &uniforms = compositor->getUniforms(); ss->addUniform(uniforms[Compositor::FCOEF]); + ss->addUniform(uniforms[Compositor::SUN_DIRECTION]); osg::ref_ptr clustered_shading_enabled = new osg::Uniform("fg_ClusteredEnabled", clustered ? true : false);