Make Compositor default
- Remove prepending shader filenames with Compositor/. - Always check for fallback effects in Effects/schemes.xml. - Shadow mapping can now be disabled at night. - New clustered shading implementation for older systems (GLSL 120 compatible). - Miscellaneous bug fixes.
This commit is contained in:
@@ -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<std::mutex> g(realizeTechniques_lock);
|
||||
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
|
||||
mergeSchemesFallbacks(this, options);
|
||||
mergeSchemesFallbacks(this, options);
|
||||
|
||||
if (_isRealized)
|
||||
return true;
|
||||
|
||||
@@ -18,11 +18,9 @@
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <osg/BufferIndexBinding>
|
||||
#include <osg/BufferObject>
|
||||
#include <osg/RenderInfo>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture3D>
|
||||
#include <osg/TextureBuffer>
|
||||
#include <osg/Version>
|
||||
|
||||
#include <osg/io_utils>
|
||||
@@ -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<osg::Texture3D> 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<osg::Texture3D> 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<osg::Uniform> light_grid_uniform =
|
||||
new osg::Uniform("fg_ClusteredLightGrid", light_grid_bind_unit);
|
||||
ss->addUniform(light_grid_uniform.get());
|
||||
osg::ref_ptr<osg::Uniform> 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<osg::TextureBuffer> light_indices_tbo =
|
||||
new osg::TextureBuffer;
|
||||
light_indices_tbo->setInternalFormat(GL_R16UI);
|
||||
light_indices_tbo->setImage(_light_indices.get());
|
||||
osg::ref_ptr<osg::Texture2D> 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<osg::Uniform> light_indices_uniform =
|
||||
new osg::Uniform("fg_ClusteredLightIndices", light_indices_bind_unit);
|
||||
ss->addUniform(light_indices_uniform.get());
|
||||
osg::ref_ptr<osg::Uniform> 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<osg::UniformBufferObject> pointlight_data_ubo =
|
||||
new osg::UniformBufferObject;
|
||||
_pointlight_data->setBufferObject(pointlight_data_ubo.get());
|
||||
osg::ref_ptr<osg::Texture2D> 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<osg::UniformBufferBinding> 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<osg::UniformBufferBinding> 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<osg::UniformBufferObject> 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<osg::UniformBufferBinding> 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<osg::UniformBufferBinding> 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<osg::Uniform> 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<GLuint *>(_light_grid->data());
|
||||
GLushort *indices = reinterpret_cast<GLushort *>(_light_indices->data());
|
||||
GLfloat *clusters = reinterpret_cast<GLfloat *>(_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<GLfloat *>(&(*_pointlight_data)[0]);
|
||||
GLfloat *data = reinterpret_cast<GLfloat *>(_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<GLfloat *>(&(*_spotlight_data)[0]);
|
||||
GLfloat *data = reinterpret_cast<GLfloat *>(_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
|
||||
|
||||
@@ -67,33 +67,32 @@ protected:
|
||||
|
||||
osg::ref_ptr<osg::Uniform> _slice_scale;
|
||||
osg::ref_ptr<osg::Uniform> _slice_bias;
|
||||
osg::ref_ptr<osg::Uniform> _horizontal_tiles;
|
||||
osg::ref_ptr<osg::Uniform> _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<osg::Image> _light_grid;
|
||||
osg::ref_ptr<osg::Image> _light_indices;
|
||||
osg::ref_ptr<osg::FloatArray> _pointlight_data;
|
||||
osg::ref_ptr<osg::FloatArray> _spotlight_data;
|
||||
osg::ref_ptr<osg::Image> _clusters;
|
||||
osg::ref_ptr<osg::Image> _pointlights;
|
||||
osg::ref_ptr<osg::Image> _spotlights;
|
||||
|
||||
std::unique_ptr<Subfrustum[]> _subfrusta;
|
||||
|
||||
std::vector<PointlightBound> _point_bounds;
|
||||
std::vector<SpotlightBound> _spot_bounds;
|
||||
|
||||
std::atomic<GLuint> _global_light_count;
|
||||
};
|
||||
|
||||
} // namespace compositor
|
||||
|
||||
@@ -55,6 +55,11 @@ Compositor::create(osg::View *view,
|
||||
osg::ref_ptr<Compositor> 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
|
||||
|
||||
@@ -104,7 +104,7 @@ buildBuffer(Compositor *compositor, const SGPropertyNode *node,
|
||||
}
|
||||
|
||||
osg::ref_ptr<Buffer> buffer = new Buffer;
|
||||
osg::Texture *texture;
|
||||
osg::Texture *texture = nullptr;
|
||||
|
||||
int width = 0;
|
||||
const SGPropertyNode *p_width = getPropertyChild(node, "width");
|
||||
|
||||
@@ -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<SGVec4d>());
|
||||
@@ -376,9 +376,9 @@ RegisterPassBuilder<QuadPassBuilder> 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<osg::Light> _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<ShadowMapCullCallback> _cull_callback;
|
||||
osg::observer_ptr<CSMCullCallback> _cull_callback;
|
||||
osg::ref_ptr<LightFinder> _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> 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<ShadowMapPassBuilder> registerShadowMapPass("shadow-map");
|
||||
RegisterPassBuilder<CSMPassBuilder> 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<ShadowMapCullCallback *>(
|
||||
CSMCullCallback *cullcb =
|
||||
dynamic_cast<CSMCullCallback *>(
|
||||
shadow_pass->camera->getCullCallback());
|
||||
if (cullcb) {
|
||||
camera->getOrCreateStateSet()->addUniform(
|
||||
|
||||
Reference in New Issue
Block a user