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.
This commit is contained in:
Fernando García Liñán 2021-04-15 11:00:09 +02:00
parent 877c3a68e6
commit b3b863a3c4
7 changed files with 179 additions and 123 deletions

View File

@ -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) );

View File

@ -31,6 +31,9 @@
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <stdio.h>
#include <iostream>
@ -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");

View File

@ -35,6 +35,10 @@
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/props/propsfwd.hxx>
namespace simgear {
class SGReaderWriterOptions;
}
class SGStars : public SGReferenced {
osg::ref_ptr<osg::Vec4Array> 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

View File

@ -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<SGUpdateVisitor *>(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()

View File

@ -59,7 +59,7 @@ public:
CAMERA_POSITION_GEOD,
NEAR_FAR_PLANES,
FCOEF,
LIGHT_DIRECTION,
SUN_DIRECTION,
TOTAL_BUILTIN_UNIFORMS
};

View File

@ -16,6 +16,7 @@
#include "CompositorBuffer.hxx"
#include <osg/GL>
#include <osg/Texture1D>
#include <osg/Texture2D>
#include <osg/Texture2DArray>
@ -23,7 +24,6 @@
#include <osg/Texture3D>
#include <osg/TextureRectangle>
#include <osg/TextureCubeMap>
#include <osg/FrameBufferObject>
#include <simgear/props/props.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
@ -45,13 +45,18 @@ struct BufferFormat {
PropStringMap<BufferFormat> 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;

View File

@ -55,6 +55,76 @@ PropStringMap<osg::Camera::BufferComponent> 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<osg::Camera *>(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<osg::Uniform> _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<osg::LightSource *>(&node);
if (light_source)
_light = light_source->getLight();
}
traverse(node);
}
osg::ref_ptr<osg::Light> getLight() const {
osg::ref_ptr<osg::Light> light_ref;
_light.lock(light_ref);
return light_ref;
}
protected:
std::string _name;
osg::observer_ptr<osg::Light> _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<CSMCullCallback *>(
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<QuadPassBuilder> 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<osg::Camera *>(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<osg::Uniform> _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<osg::LightSource *>(&node);
if (light_source)
_light = light_source->getLight();
}
traverse(node);
}
osg::ref_ptr<osg::Light> getLight() const {
osg::ref_ptr<osg::Light> light_ref;
_light.lock(light_ref);
return light_ref;
}
protected:
std::string _name;
osg::observer_ptr<osg::Light> _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<osg::Vec3, osg::Vec3> CubemapFace;
typedef std::pair<osg::Vec3d, osg::Vec3d> 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<CSMCullCallback *>(
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<osg::Uniform> clustered_shading_enabled =
new osg::Uniform("fg_ClusteredEnabled", clustered ? true : false);