Fix directional lights for AMD

Some AMD drivers do not like triangles for point sprites, which
breaks directional lights.  This works around this by allowing
users to set /rendering/triangle-directional-lights=false which
falls back to the non-directional implementation of a point.
This commit is contained in:
Stuart Buchanan 2020-01-27 09:28:25 +00:00
parent faf43d2c4b
commit 9f862dcc0e
4 changed files with 77 additions and 38 deletions

View File

@ -153,41 +153,49 @@ SGLightFactory::getLightDrawable(const SGLightBin::Light& light)
}
osg::Drawable*
SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light)
SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light, bool useTriangles)
{
osg::Vec3Array* vertices = new osg::Vec3Array;
osg::Vec4Array* colors = new osg::Vec4Array;
SGVec4f visibleColor(light.color);
SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
visibleColor[2], 0);
SGVec3f normal = normalize(light.normal);
SGVec3f perp1 = perpendicular(normal);
SGVec3f perp2 = cross(normal, perp1);
SGVec3f position = light.position;
vertices->push_back(toOsg(position));
vertices->push_back(toOsg(position + perp1));
vertices->push_back(toOsg(position + perp2));
colors->push_back(toOsg(visibleColor));
colors->push_back(toOsg(invisibleColor));
colors->push_back(toOsg(invisibleColor));
if (useTriangles) {
SGVec4f visibleColor(light.color);
SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
visibleColor[2], 0);
SGVec3f normal = normalize(light.normal);
SGVec3f perp1 = perpendicular(normal);
SGVec3f perp2 = cross(normal, perp1);
SGVec3f position = light.position;
vertices->push_back(toOsg(position));
vertices->push_back(toOsg(position + perp1));
vertices->push_back(toOsg(position + perp2));
colors->push_back(toOsg(visibleColor));
colors->push_back(toOsg(invisibleColor));
colors->push_back(toOsg(invisibleColor));
osg::Geometry* geometry = new osg::Geometry;
geometry->setDataVariance(osg::Object::STATIC);
geometry->setVertexArray(vertices);
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
geometry->setColorArray(colors);
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
osg::Geometry* geometry = new osg::Geometry;
geometry->setDataVariance(osg::Object::STATIC);
geometry->setVertexArray(vertices);
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
geometry->setColorArray(colors);
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
// Enlarge the bounding box to avoid such light nodes being victim to
// small feature culling.
geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
// Enlarge the bounding box to avoid such light nodes being victim to
// small feature culling.
geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
osg::DrawArrays* drawArrays;
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
0, vertices->size());
geometry->addPrimitiveSet(drawArrays);
return geometry;
osg::DrawArrays* drawArrays;
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
0, vertices->size());
geometry->addPrimitiveSet(drawArrays);
return geometry;
} else {
// Workaround for driver issue where point sprites cannot be triangles,
// (which we use to provide a sensible normal). So fall back to a simple
// non-directional point sprite.
const SGLightBin::Light nonDirectionalLight = SGLightBin::Light(light.position, light.color);
return getLightDrawable(nonDirectionalLight);
}
}
namespace
@ -367,6 +375,9 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReader
if (lights.getNumLights() <= 0)
return 0;
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights();
// generate a repeatable random seed
sg_srandom(unsigned(lights.getLight(0).position[0]));
float flashTime = 0.065 + 0.003 * sg_random();
@ -377,7 +388,7 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReader
for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
EffectGeode* egeode = new EffectGeode;
egeode->setEffect(effect);
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
egeode->addDrawable(getLightDrawable(lights.getLight(i), useTriangles));
sequence->addChild(egeode, flashTime);
}
sequence->addChild(new osg::Group, 1.9 + (0.1 * sg_random()) - (lights.getNumLights() * flashTime));
@ -394,6 +405,9 @@ SGLightFactory::getReil(const SGDirectionalLightBin& lights, const SGReaderWrite
if (lights.getNumLights() <= 0)
return 0;
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights();
// generate a repeatable random seed
sg_srandom(unsigned(lights.getLight(0).position[0]));
float flashTime = 0.065 + 0.003 * sg_random();
@ -405,7 +419,7 @@ SGLightFactory::getReil(const SGDirectionalLightBin& lights, const SGReaderWrite
egeode->setEffect(effect);
for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
egeode->addDrawable(getLightDrawable(lights.getLight(i), useTriangles));
}
sequence->addChild(egeode, flashTime);
sequence->addChild(new osg::Group, 1.9 + 0.1 * sg_random() - flashTime);
@ -463,6 +477,9 @@ SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReader
if (lights.getNumLights() < 2)
return 0;
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights();
sg_srandom(unsigned(lights.getLight(0).position[0]));
float flashTime = 0.9 + 0.2 * sg_random();
osg::Sequence* sequence = new osg::Sequence;
@ -476,7 +493,7 @@ SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReader
EffectGeode* egeode = new EffectGeode;
egeode->setEffect(effect);
for (unsigned int j = 0; j < lights.getNumLights(); ++j) {
egeode->addDrawable(getLightDrawable(lights.getLight(j)));
egeode->addDrawable(getLightDrawable(lights.getLight(j), useTriangles));
}
sequence->addChild(egeode, (i==4) ? flashTime : 0.1);
}
@ -494,6 +511,9 @@ SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWrit
if (lights.getNumLights() < 2)
return 0;
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
bool useTriangles = sceneFeatures->getEnableTriangleDirectionalLights();
// generate a repeatable random seed
sg_srandom(unsigned(lights.getLight(0).position[0]));
float flashTime = 0.9 + 0.2 * sg_random();
@ -504,7 +524,7 @@ SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWrit
for (unsigned int i = 0; i < lights.getNumLights(); ++i) {
EffectGeode* egeode = new EffectGeode;
egeode->setEffect(effect);
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
egeode->addDrawable(getLightDrawable(lights.getLight(i), useTriangles));
sequence->addChild(egeode, flashTime);
}
sequence->setInterval(osg::Sequence::LOOP, 0, -1);

View File

@ -55,10 +55,12 @@ class Effect;
// appropriate extensions are available.)
inline void SGConfigureDirectionalLights( bool use_point_sprites,
bool distance_attenuation ) {
bool distance_attenuation,
bool use_triangles ) {
static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
sceneFeatures->setEnablePointSpriteLights(use_point_sprites);
sceneFeatures->setEnableDistanceAttenuationLights(distance_attenuation);
sceneFeatures->setEnableTriangleDirectionalLights(use_triangles);
}
class SGLightFactory {
@ -68,7 +70,7 @@ public:
getLightDrawable(const SGLightBin::Light& light);
static osg::Drawable*
getLightDrawable(const SGDirectionalLightBin::Light& light);
getLightDrawable(const SGDirectionalLightBin::Light& light, bool useTriangles);
/**
* Return a drawable for a very simple point light that isn't

View File

@ -1,6 +1,6 @@
/* -*-c++-*-
*
* Copyright (C) 2006-2007 Mathias Froehlich
* Copyright (C) 2006-2007 Mathias Froehlich
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -46,6 +46,7 @@ SGSceneFeatures::SGSceneFeatures() :
_TextureCacheActive(false),
_shaderLights(true),
_pointSpriteLights(true),
_triangleDirectionalLights(true),
_distanceAttenuationLights(true),
_textureFilter(1)
{
@ -108,7 +109,7 @@ SGSceneFeatures::getHaveFragmentPrograms(unsigned contextId) const
return false;
if (!fpe->isFragmentProgramSupported())
return false;
return true;
#else
const osg::GLExtensions* ex = osg::GLExtensions::Get(contextId, true);
@ -126,7 +127,7 @@ SGSceneFeatures::getHaveVertexPrograms(unsigned contextId) const
return false;
if (!vpe->isVertexProgramSupported())
return false;
return true;
#else
const osg::GLExtensions* ex = osg::GLExtensions::Get(contextId, true);
@ -158,4 +159,3 @@ SGSceneFeatures::getHavePointParameters(unsigned contextId) const
return ex && ex->isPointParametersSupported;
#endif
}

View File

@ -75,6 +75,21 @@ public:
return getHavePointSprites(contextId);
}
void setEnableTriangleDirectionalLights(bool enable)
{
_triangleDirectionalLights = enable;
}
bool getEnableTriangleDirectionalLights() const
{
return _triangleDirectionalLights;
}
bool getEnableTriangleDirectionalLights(unsigned contextId) const
{
if (!_triangleDirectionalLights)
return false;
return getHaveTriangleDirectionals(contextId);
}
void setEnableDistanceAttenuationLights(bool enable)
{
_distanceAttenuationLights = enable;
@ -108,6 +123,7 @@ public:
protected:
bool getHavePointSprites(unsigned contextId) const;
bool getHaveTriangleDirectionals(unsigned contextId) const;
bool getHaveFragmentPrograms(unsigned contextId) const;
bool getHaveVertexPrograms(unsigned contextId) const;
bool getHaveShaderPrograms(unsigned contextId) const;
@ -126,6 +142,7 @@ private:
bool _TextureCacheActive;
bool _shaderLights;
bool _pointSpriteLights;
bool _triangleDirectionalLights;
bool _distanceAttenuationLights;
int _textureFilter;
};