Browse Source

Shaders: ability to disable Phong specular contribution.

In cases when specular highlights are not desired, results in 30%
speedup (on Intel) and ~25% speedup on AMD, compared to setting the
specular color to transparent black.

Testing was easy thanks to already having a ground truth image for this
case.
pull/518/head
Vladimír Vondruš 5 years ago
parent
commit
cc74784d40
  1. 3
      doc/changelog.dox
  2. 10
      src/Magnum/Shaders/Phong.frag
  3. 8
      src/Magnum/Shaders/Phong.h
  4. 36
      src/Magnum/Shaders/PhongGL.cpp
  5. 32
      src/Magnum/Shaders/PhongGL.h
  6. 2
      src/Magnum/Shaders/Test/CMakeLists.txt
  7. 57
      src/Magnum/Shaders/Test/PhongGLTest.cpp
  8. 4
      src/Magnum/Shaders/Test/PhongGL_Test.cpp
  9. 0
      src/Magnum/Shaders/Test/PhongTestFiles/shininess-no-specular.tga
  10. 1
      src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp

3
doc/changelog.dox

@ -161,6 +161,9 @@ See also:
@ref Trade::LightData
- Added @ref Shaders::PhongGL::setLightSpecularColors() for better control
over specular highlights
- Added @ref Shaders::PhongGL::Flag::NoSpecular as a significantly faster
alternative to setting specular color to @cpp 0x00000000_rgbaf @ce in case
specular highlights are not desired
@subsubsection changelog-latest-new-shadertools ShaderTools library

10
src/Magnum/Shaders/Phong.frag

@ -63,6 +63,7 @@ uniform lowp vec4 diffuseColor
#endif
;
#ifndef NO_SPECULAR
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 7)
#endif
@ -81,6 +82,7 @@ uniform mediump float shininess
#endif
;
#endif
#endif
#ifdef NORMAL_TEXTURE
#ifdef EXPLICIT_UNIFORM_LOCATION
@ -136,6 +138,7 @@ uniform lowp vec3 lightColors[LIGHT_COUNT]
#endif
;
#ifndef NO_SPECULAR
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = LIGHT_SPECULAR_COLORS_LOCATION)
#endif
@ -144,6 +147,7 @@ uniform lowp vec3 lightSpecularColors[LIGHT_COUNT]
= vec3[](LIGHT_COLOR_INITIALIZER)
#endif
;
#endif
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = LIGHT_RANGES_LOCATION)
@ -386,12 +390,14 @@ void main() {
interpolatedVertexColor*
#endif
diffuseColor;
#ifndef NO_SPECULAR
lowp const vec4 finalSpecularColor =
#ifdef SPECULAR_TEXTURE
texture(specularTexture, interpolatedTextureCoordinates)*
#endif
specularColor;
#endif
#endif
/* Ambient color */
fragmentColor = finalAmbientColor;
@ -439,6 +445,7 @@ void main() {
i].light_color
#endif
;
#ifndef NO_SPECULAR
lowp const vec3 lightSpecularColor =
#ifndef UNIFORM_BUFFERS
lightSpecularColors[i]
@ -450,6 +457,7 @@ void main() {
i].light_specularColor
#endif
;
#endif
lowp const float lightRange =
#ifndef UNIFORM_BUFFERS
lightRanges[i]
@ -490,6 +498,7 @@ void main() {
lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation;
fragmentColor.rgb += finalDiffuseColor.rgb*lightColor*intensity;
#ifndef NO_SPECULAR
/* Add specular color, if needed */
if(intensity > 0.001) {
highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal);
@ -497,6 +506,7 @@ void main() {
mediump float specularity = clamp(pow(max(0.0, dot(cameraDirection, reflection)), shininess), 0.0, 1.0)*attenuation;
fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a);
}
#endif
}
fragmentColor.a += finalDiffuseColor.a;

8
src/Magnum/Shaders/Phong.h

@ -326,8 +326,8 @@ struct PhongMaterialUniform {
*
* Default value is @cpp 0xffffff00_rgbaf @ce.
*
* Used only if the effective light count for given draw is not zero,
* ignored otherwise.
* Used only if the effective light count for given draw is not zero and
* @ref PhongGL::Flag::NoSpecular is not set, ignored otherwise.
* @see @ref PhongGL::setSpecularColor()
*/
Color4 specularColor;
@ -351,8 +351,8 @@ struct PhongMaterialUniform {
* The larger value, the harder surface (smaller specular highlight).
* Default value is @cpp 80.0f @ce.
*
* Used only if the effective light count for given draw is not zero,
* ignored otherwise.
* Used only if the effective light count for given draw is not zero and
* @ref PhongGL::Flag::NoSpecular is not set, ignored otherwise.
* @see @ref PhongGL::setShininess()
*/
Float shininess;

36
src/Magnum/Shaders/PhongGL.cpp

@ -111,6 +111,9 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
"Shaders::PhongGL: light culling requires uniform buffers to be enabled", );
#endif
CORRADE_ASSERT(!(flags & Flag::SpecularTexture) || !(flags & (Flag::NoSpecular)),
"Shaders::PhongGL: specular texture requires the shader to not have specular disabled", );
#ifndef MAGNUM_TARGET_GLES
if(flags >= Flag::UniformBuffers)
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object);
@ -243,6 +246,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
.addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "")
.addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "")
#endif
.addSource(flags & Flag::NoSpecular ? "#define NO_SPECULAR\n" : "")
;
#ifndef MAGNUM_TARGET_GLES2
if(flags >= Flag::UniformBuffers) {
@ -338,13 +342,16 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
if(lightCount) {
_normalMatrixUniform = uniformLocation("normalMatrix");
_diffuseColorUniform = uniformLocation("diffuseColor");
_specularColorUniform = uniformLocation("specularColor");
_shininessUniform = uniformLocation("shininess");
if(!(flags & Flag::NoSpecular)) {
_specularColorUniform = uniformLocation("specularColor");
_shininessUniform = uniformLocation("shininess");
}
if(flags & Flag::NormalTexture)
_normalTextureScaleUniform = uniformLocation("normalTextureScale");
_lightPositionsUniform = uniformLocation("lightPositions");
_lightColorsUniform = uniformLocation("lightColors");
_lightSpecularColorsUniform = uniformLocation("lightSpecularColors");
if(!(flags & Flag::NoSpecular))
_lightSpecularColorsUniform = uniformLocation("lightSpecularColors");
_lightRangesUniform = uniformLocation("lightRanges");
}
if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask");
@ -393,14 +400,17 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
setProjectionMatrix(Matrix4{Math::IdentityInit});
if(lightCount) {
setDiffuseColor(Magnum::Color4{1.0f});
setSpecularColor(Magnum::Color4{1.0f, 0.0f});
setShininess(80.0f);
if(!(flags & Flag::NoSpecular)) {
setSpecularColor(Magnum::Color4{1.0f, 0.0f});
setShininess(80.0f);
}
if(flags & Flag::NormalTexture)
setNormalTextureScale(1.0f);
setLightPositions(Containers::Array<Vector4>{DirectInit, lightCount, Vector4{0.0f, 0.0f, 1.0f, 0.0f}});
Containers::Array<Magnum::Color3> colors{DirectInit, lightCount, Magnum::Color3{1.0f}};
setLightColors(colors);
setLightSpecularColors(colors);
if(!(flags & Flag::NoSpecular))
setLightSpecularColors(colors);
setLightRanges(Containers::Array<Float>{DirectInit, lightCount, Constants::inf()});
/* Light position is zero by default */
setNormalMatrix(Matrix3x3{Math::IdentityInit});
@ -441,6 +451,8 @@ PhongGL& PhongGL::setSpecularColor(const Magnum::Color4& color) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::PhongGL::setSpecularColor(): the shader was created with uniform buffers enabled", *this);
#endif
CORRADE_ASSERT(!(_flags >= Flag::NoSpecular),
"Shaders::PhongGL::setSpecularColor(): the shader was created with specular disabled", *this);
if(_lightCount) setUniform(_specularColorUniform, color);
return *this;
}
@ -450,6 +462,8 @@ PhongGL& PhongGL::setShininess(Float shininess) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::PhongGL::setShininess(): the shader was created with uniform buffers enabled", *this);
#endif
CORRADE_ASSERT(!(_flags >= Flag::NoSpecular),
"Shaders::PhongGL::setShininess(): the shader was created with specular disabled", *this);
if(_lightCount) setUniform(_shininessUniform, shininess);
return *this;
}
@ -651,6 +665,8 @@ PhongGL& PhongGL::setLightSpecularColors(const Containers::ArrayView<const Magnu
#endif
CORRADE_ASSERT(_lightCount == colors.size(),
"Shaders::PhongGL::setLightSpecularColors(): expected" << _lightCount << "items but got" << colors.size(), *this);
CORRADE_ASSERT(!(_flags >= Flag::NoSpecular),
"Shaders::PhongGL::setLightSpecularColors(): the shader was created with specular disabled", *this);
if(_lightCount) setUniform(_lightSpecularColorsUniform, colors);
return *this;
}
@ -666,6 +682,8 @@ PhongGL& PhongGL::setLightSpecularColor(const UnsignedInt id, const Magnum::Colo
#endif
CORRADE_ASSERT(id < _lightCount,
"Shaders::PhongGL::setLightSpecularColor(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this);
CORRADE_ASSERT(!(_flags >= Flag::NoSpecular),
"Shaders::PhongGL::setLightSpecularColor(): the shader was created with specular disabled", *this);
setUniform(_lightSpecularColorsUniform + id, color);
return *this;
}
@ -920,11 +938,12 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) {
_c(TextureArrays)
_c(LightCulling)
#endif
_c(NoSpecular)
#undef _c
/* LCOV_EXCL_STOP */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")";
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedInt(value)) << Debug::nospace << ")";
}
Debug& operator<<(Debug& debug, const PhongGL::Flags value) {
@ -947,8 +966,9 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) {
PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */
PhongGL::Flag::UniformBuffers,
PhongGL::Flag::TextureArrays,
PhongGL::Flag::LightCulling
PhongGL::Flag::LightCulling,
#endif
PhongGL::Flag::NoSpecular
});
}

32
src/Magnum/Shaders/PhongGL.h

@ -484,7 +484,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
*
* @see @ref Flags, @ref flags()
*/
enum class Flag: UnsignedShort {
enum class Flag: UnsignedInt {
/**
* Multiply ambient color with a texture.
* @see @ref setAmbientColor(), @ref bindAmbientTexture()
@ -705,8 +705,17 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
* 1.0.
* @m_since_latest
*/
LightCulling = 1 << 15
LightCulling = 1 << 15,
#endif
/**
* Disable specular contribution in light calculation. Can result
* in a significant performance improvement compared to calling
* @ref setSpecularColor() with @cpp 0x00000000_rgbaf @ce when
* specular highlights are not desired.
* @m_since_latest
*/
NoSpecular = 1 << 16
};
/**
@ -900,10 +909,13 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
* @brief Set specular color
* @return Reference to self (for method chaining)
*
* Initial value is @cpp 0xffffff00_rgbaf @ce. If
* Initial value is @cpp 0xffffff00_rgbaf @ce. Expects that the shader
* was not created with @ref Flag::NoSpecular. If
* @ref Flag::SpecularTexture is set, the color will be multiplied with
* the texture. If you want to have a fully diffuse material, set
* the specular color to @cpp 0x00000000_rgbaf @ce. If
* the texture. If you want to have a fully diffuse material, it's
* recommended to disable the specular contribution altogether with
* @ref Flag::NoSpecular. If having a dedicated shader variant is not
* possible, set the specular color to @cpp 0x00000000_rgbaf @ce. If
* @ref lightCount() is zero, this function is a no-op, as specular
* color doesn't contribute to the output in that case.
*
@ -1530,7 +1542,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
* @return Reference to self (for method chaining)
*
* Expects that the shader was created with @ref Flag::SpecularTexture
* enabled. If @ref Flag::TextureArrays is enabled as well, use
* enabled and that @ref Flag::NoSpecular is not set. If
* @ref Flag::TextureArrays is enabled as well, use
* @ref bindSpecularTexture(GL::Texture2DArray&) instead. If
* @ref lightCount() is zero, this function is a no-op, as specular
* color doesn't contribute to the output in that case.
@ -1545,9 +1558,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
* @m_since_latest
*
* Expects that the shader was created with both
* @ref Flag::SpecularTexture and @ref Flag::TextureArrays enabled. If
* @ref Flag::UniformBuffers is not enabled, the layer is set via
* @ref setTextureLayer(); if @ref Flag::UniformBuffers is enabled,
* @ref Flag::SpecularTexture and @ref Flag::TextureArrays enabled and
* that @ref Flag::NoSpecular is not set. If @ref Flag::UniformBuffers
* is not enabled, the layer is set via @ref setTextureLayer(); if
* @ref Flag::UniformBuffers is enabled,
* @ref Flag::TextureTransformation has to be enabled as well and the
* layer is set via @ref TextureTransformationUniform::layer. If
* @ref lightCount() is zero, this function is a no-op, as specular

2
src/Magnum/Shaders/Test/CMakeLists.txt

@ -280,7 +280,7 @@ if(BUILD_GL_TESTS)
PhongTestFiles/instanced.tga
PhongTestFiles/instanced-normal.tga
PhongTestFiles/low-light-angle.tga
PhongTestFiles/shininess-black-specular.tga
PhongTestFiles/shininess-no-specular.tga
PhongTestFiles/shininess0-overflow.tga
PhongTestFiles/shininess0.tga
PhongTestFiles/shininess10.tga

57
src/Magnum/Shaders/Test/PhongGLTest.cpp

@ -100,6 +100,7 @@ struct PhongGLTest: GL::OpenGLTester {
void bindTextureArraysInvalid();
#endif
void setAlphaMaskNotEnabled();
void setSpecularDisabled();
void setTextureMatrixNotEnabled();
void setNormalTextureScaleNotEnabled();
#ifndef MAGNUM_TARGET_GLES2
@ -233,6 +234,7 @@ constexpr struct {
{"instanced object ID", PhongGL::Flag::InstancedObjectId, 1},
{"object ID + alpha mask + specular texture", PhongGL::Flag::ObjectId|PhongGL::Flag::AlphaMask|PhongGL::Flag::SpecularTexture, 1},
#endif
{"no specular", PhongGL::Flag::NoSpecular, 1},
{"five lights", {}, 5},
{"zero lights", {}, 0},
{"instanced transformation", PhongGL::Flag::InstancedTransformation, 3},
@ -265,6 +267,7 @@ constexpr struct {
{"normal texture + separate bitangents", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1},
{"alpha mask", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AlphaMask, 1, 1, 1},
{"object ID", PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId, 1, 1, 1},
{"no specular", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NoSpecular, 1, 1, 1},
{"multidraw with all the things", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::LightCulling, 8, 16, 24}
};
#endif
@ -284,6 +287,9 @@ constexpr struct {
PhongGL::Flag::Bitangent|PhongGL::Flag::InstancedObjectId,
"Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead"},
#endif
{"specular texture but no specular",
PhongGL::Flag::SpecularTexture|PhongGL::Flag::NoSpecular,
"specular texture requires the shader to not have specular disabled"}
};
#ifndef MAGNUM_TARGET_GLES2
@ -472,14 +478,22 @@ const struct {
const struct {
const char* name;
const char* expected;
PhongGL::Flags flags;
Float shininess;
Color4 specular;
} RenderShininessData[] {
{"80", "shininess80.tga", 80.0f, 0xffffff_rgbf},
{"10", "shininess10.tga", 10.0f, 0xffffff_rgbf},
{"0", "shininess0.tga", 0.0f, 0xffffff_rgbf},
{"0.001", "shininess0.tga", 0.001f, 0xffffff_rgbf},
{"black specular", "shininess-black-specular.tga", 80.0f, 0x000000_rgbf}
{"80", "shininess80.tga",
{}, 80.0f, 0xffffff_rgbf},
{"10", "shininess10.tga",
{}, 10.0f, 0xffffff_rgbf},
{"0", "shininess0.tga",
{}, 0.0f, 0xffffff_rgbf},
{"0.001", "shininess0.tga",
{}, 0.001f, 0xffffff_rgbf},
{"black specular", "shininess-no-specular.tga",
{}, 80.0f, 0x000000_rgbf},
{"no specular", "shininess-no-specular.tga",
PhongGL::Flag::NoSpecular, 80.0f, 0xffffff_rgbf}
};
const struct {
@ -777,6 +791,7 @@ PhongGLTest::PhongGLTest() {
addTests({
&PhongGLTest::setAlphaMaskNotEnabled,
&PhongGLTest::setSpecularDisabled,
&PhongGLTest::setTextureMatrixNotEnabled,
&PhongGLTest::setNormalTextureScaleNotEnabled,
#ifndef MAGNUM_TARGET_GLES2
@ -1306,6 +1321,27 @@ void PhongGLTest::setAlphaMaskNotEnabled() {
"Shaders::PhongGL::setAlphaMask(): the shader was not created with alpha mask enabled\n");
}
void PhongGLTest::setSpecularDisabled() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
GL::Texture2D texture;
PhongGL shader{PhongGL::Flag::NoSpecular};
shader.setSpecularColor({})
.setShininess({})
.setLightSpecularColors({{}})
.setLightSpecularColor(0, {});
CORRADE_COMPARE(out.str(),
"Shaders::PhongGL::setSpecularColor(): the shader was created with specular disabled\n"
"Shaders::PhongGL::setShininess(): the shader was created with specular disabled\n"
"Shaders::PhongGL::setLightSpecularColors(): the shader was created with specular disabled\n"
"Shaders::PhongGL::setLightSpecularColor(): the shader was created with specular disabled\n");
}
void PhongGLTest::setTextureMatrixNotEnabled() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
@ -2427,13 +2463,14 @@ template<PhongGL::Flag flag> void PhongGLTest::renderShininess() {
GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32));
PhongGL shader{flag};
PhongGL shader{flag|data.flags};
if(flag == PhongGL::Flag{}) {
if(!(data.flags & PhongGL::Flag::NoSpecular)) shader
.setSpecularColor(data.specular)
.setShininess(data.shininess);
shader
.setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}})
.setDiffuseColor(0xff3333_rgbf)
.setSpecularColor(data.specular)
.setShininess(data.shininess)
.setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f)))
.setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f))
.draw(sphere);
@ -2459,8 +2496,8 @@ template<PhongGL::Flag flag> void PhongGLTest::renderShininess() {
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
PhongMaterialUniform{}
.setDiffuseColor(0xff3333_rgbf)
.setSpecularColor(data.specular)
.setShininess(data.shininess)
.setSpecularColor(data.specular) /* ignored if NoSpecular */
.setShininess(data.shininess) /* ignored if NoSpecular */
}};
shader
.bindProjectionBuffer(projectionUniform)

4
src/Magnum/Shaders/Test/PhongGL_Test.cpp

@ -72,8 +72,8 @@ void PhongGL_Test::constructCopy() {
void PhongGL_Test::debugFlag() {
std::ostringstream out;
Debug{&out} << PhongGL::Flag::AmbientTexture << PhongGL::Flag(0xf0);
CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::AmbientTexture Shaders::PhongGL::Flag(0xf0)\n");
Debug{&out} << PhongGL::Flag::AmbientTexture << PhongGL::Flag(0xcafedead);
CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::AmbientTexture Shaders::PhongGL::Flag(0xcafedead)\n");
}
void PhongGL_Test::debugFlags() {

0
src/Magnum/Shaders/Test/PhongTestFiles/shininess-black-specular.tga → src/Magnum/Shaders/Test/PhongTestFiles/shininess-no-specular.tga

1
src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp

@ -171,6 +171,7 @@ const struct {
{"", {}, 1, 1, 1, false},
{"zero lights", {}, 0, 1, 1, false},
{"five lights", {}, 5, 1, 1, false},
{"no specular", PhongGL::Flag::NoSpecular, 1, 1, 1, false},
{"vertex color", PhongGL::Flag::VertexColor, 1, 1, 1, false},
#ifndef MAGNUM_TARGET_GLES2
{"object ID", PhongGL::Flag::ObjectId, 1, 1, 1, false},

Loading…
Cancel
Save