diff --git a/doc/changelog.dox b/doc/changelog.dox index 502ea4978..0083b0ff5 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -83,6 +83,8 @@ See also: - @ref Shaders::Phong was reworked to support directional and range-attenuated point lights to follow the additions to @ref Trade::LightData +- Added @ref Shaders::Phong::setLightSpecularColors() for better control over + speculat highlights @subsubsection changelog-latest-new-scenegraph SceneGraph library diff --git a/doc/snippets/MagnumShaders.cpp b/doc/snippets/MagnumShaders.cpp index 75211da65..048a03cd9 100644 --- a/doc/snippets/MagnumShaders.cpp +++ b/doc/snippets/MagnumShaders.cpp @@ -546,6 +546,7 @@ shader .setLightColors({0xf0f0ff_srgbf*0.1f, 0xff8080_srgbf*10.0f, 0x80ff80_srgbf*10.0f}) + .setLightColors(DOXYGEN_IGNORE({0xf0f0ff_srgbf})) .setLightRanges({Constants::inf(), 2.0f, 2.0f}); diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 0e7e444d4..42b52efc8 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -55,7 +55,7 @@ namespace { }; } -Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _lightCount{lightCount}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)}, _lightRangesUniform{_lightPositionsUniform + 2*Int(lightCount)} { +Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _lightCount{lightCount}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)}, _lightSpecularColorsUniform{_lightPositionsUniform + 2*Int(lightCount)}, _lightRangesUniform{_lightPositionsUniform + 3*Int(lightCount)} { CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), "Shaders::Phong: texture transformation enabled but the shader is not textured", ); @@ -156,10 +156,12 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l .addSource(Utility::formatString( "#define LIGHT_COUNT {}\n" "#define LIGHT_COLORS_LOCATION {}\n" + "#define LIGHT_SPECULAR_COLORS_LOCATION {}\n" "#define LIGHT_RANGES_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount, - _lightPositionsUniform + 2*lightCount)); + _lightPositionsUniform + 2*lightCount, + _lightPositionsUniform + 3*lightCount)); #ifndef MAGNUM_TARGET_GLES if(lightCount) frag.addSource(std::move(lightInitializerFragment)); #endif @@ -224,6 +226,7 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l _normalTextureScaleUniform = uniformLocation("normalTextureScale"); _lightPositionsUniform = uniformLocation("lightPositions"); _lightColorsUniform = uniformLocation("lightColors"); + _lightSpecularColorsUniform = uniformLocation("lightSpecularColors"); _lightRangesUniform = uniformLocation("lightRanges"); } if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); @@ -258,7 +261,9 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l if(flags & Flag::NormalTexture) setNormalTextureScale(1.0f); setLightPositions(Containers::Array{Containers::DirectInit, lightCount, Vector4{0.0f, 0.0f, 1.0f, 0.0f}}); - setLightColors(Containers::Array{Containers::DirectInit, lightCount, Magnum::Color3{1.0f}}); + Containers::Array colors{Containers::DirectInit, lightCount, Magnum::Color3{1.0f}}; + setLightColors(colors); + setLightSpecularColors(colors); setLightRanges(Containers::Array{Containers::DirectInit, lightCount, Constants::inf()}); /* Light position is zero by default */ setNormalMatrix({}); @@ -461,6 +466,24 @@ Phong& Phong::setLightColor(const Magnum::Color4& color) { } #endif +Phong& Phong::setLightSpecularColors(const Containers::ArrayView colors) { + CORRADE_ASSERT(_lightCount == colors.size(), + "Shaders::Phong::setLightSpecularColors(): expected" << _lightCount << "items but got" << colors.size(), *this); + if(_lightCount) setUniform(_lightSpecularColorsUniform, colors); + return *this; +} + +Phong& Phong::setLightSpecularColors(const std::initializer_list colors) { + return setLightSpecularColors(Containers::arrayView(colors)); +} + +Phong& Phong::setLightSpecularColor(const UnsignedInt id, const Magnum::Color3& color) { + CORRADE_ASSERT(id < _lightCount, + "Shaders::Phong::setLightSpecularColor(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); + setUniform(_lightSpecularColorsUniform + id, color); + return *this; +} + Phong& Phong::setLightRanges(const Containers::ArrayView ranges) { CORRADE_ASSERT(_lightCount == ranges.size(), "Shaders::Phong::setLightRanges(): expected" << _lightCount << "items but got" << ranges.size(), *this); diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index c8fa162a1..46109b924 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -140,7 +140,8 @@ uniform highp uint objectId; /* defaults to zero */ #if LIGHT_COUNT /* Needs to be last because it uses locations 11 + LIGHT_COUNT to 11 + 2*LIGHT_COUNT - 1. Location 11 is lightPositions. Also it can't be - specified as 11 + LIGHT_COUNT because that requires ARB_enhanced_layouts. */ + specified as 11 + LIGHT_COUNT because that requires ARB_enhanced_layouts. + Same for lightSpecularColors and lightRanges below. */ #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = LIGHT_COLORS_LOCATION) /* I fear this will blow up some drivers */ #endif @@ -150,6 +151,15 @@ uniform lowp vec3 lightColors[LIGHT_COUNT] #endif ; +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = LIGHT_SPECULAR_COLORS_LOCATION) +#endif +uniform lowp vec3 lightSpecularColors[LIGHT_COUNT] + #ifndef GL_ES + = vec3[](LIGHT_COLOR_INITIALIZER) + #endif + ; + #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = LIGHT_RANGES_LOCATION) #endif @@ -272,7 +282,7 @@ void main() { highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); /* Use attenuation for the specularity as well */ mediump float specularity = clamp(pow(max(0.0, dot(normalize(cameraDirection), reflection)), shininess), 0.0, 1.0)*attenuation; - fragmentColor += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a); + fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColors[i].rgb*specularity, finalSpecularColor.a); } } #endif diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 77bb29cbb..f0501a6c7 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -78,9 +78,10 @@ Common rendering setup: By default, the shader provides a single directional "fill" light, coming from the center of the camera. Using the @p lightCount parameter in constructor, you can specify how many lights you want, and then control light parameters using -the following @ref setLightPositions(), @ref setLightColors() and -@ref setLightRanges(). Light positions are specified as four-component vectors, -the last component distinguishing between directional and point lights. +@ref setLightPositions(), @ref setLightColors(), @ref setLightSpecularColors() +and @ref setLightRanges(). Light positions are specified as four-component +vectors, the last component distinguishing between directional and point +lights.
  • Point lights are specified with camera-relative position and the last component @@ -106,7 +107,7 @@ any way: @f[ Light color and intensity, corresponding to @ref Trade::LightData::color() and @ref Trade::LightData::intensity(), is meant to be multiplied together and -passed to @ref setLightColors(). +passed to @ref setLightColors() and @ref setLightSpecularColors(). The following example shows a three-light setup with one dim directional light shining from the top and two stronger but range-limited point lights: @@ -941,6 +942,38 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { CORRADE_DEPRECATED("use setLightColor(std::initializer_list) instead") Phong& setLightColor(const Magnum::Color4& color); #endif + /** + * @brief Set light specular colors + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Usually you'd set this value to the same as @ref setLightColors(), + * but it allows for greater flexibility such as disabling specular + * highlights on certain lights. Initial values are + * @cpp 0xffffff_rgbf @ce. Expects that the size of the @p colors array + * is the same as @ref lightCount(). + * @see @ref Shaders-Phong-lights, @ref setLightColor() + */ + Phong& setLightSpecularColors(Containers::ArrayView colors); + + /** + * @overload + * @m_since_latest + */ + Phong& setLightSpecularColors(std::initializer_list colors); + + /** + * @brief Set position for given light + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Unlike @ref setLightSpecularColors() updates just a single light + * color. If updating more than one light, prefer the batch function + * instead to reduce the count of GL API calls. Expects that @p id is + * less than @ref lightCount(). + */ + Phong& setLightSpecularColor(UnsignedInt id, const Magnum::Color3& color); + /** * @brief Set light attenuation ranges * @return Reference to self (for method chaining) @@ -996,7 +1029,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { #endif Int _lightPositionsUniform{11}, _lightColorsUniform, /* 11 + lightCount, set in the constructor */ - _lightRangesUniform; /* 11 + 2*lightCount, set in the constructor */ + _lightSpecularColorsUniform, /* 11 + 2*lightCount */ + _lightRangesUniform; /* 11 + 3*lightCount */ }; /** @debugoperatorclassenum{Phong,Phong::Flag} */ diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 6850dfff6..d1ae4b61d 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -246,6 +246,7 @@ if(BUILD_GL_TESTS) PhongTestFiles/light-point-attenuated-specular.tga PhongTestFiles/light-point-intensity10-range0.5.tga PhongTestFiles/light-point-range1.5.tga + PhongTestFiles/light-point-specular-color.tga PhongTestFiles/light-point.tga # For zero lights test (equivalency to Flat3D) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 6472745cf..4c2b16cae 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -344,12 +344,14 @@ const struct { const char* name; const char* file; Vector4 position; + Color3 specularColor, lightSpecularColor; Float intensity; Float range; Containers::Array> picks; } RenderLightsData[] { {"directional", "light-directional.tga", - {1.0f, -1.5f, 0.5f, 0.0f}, 1.0f, Constants::inf(), + {1.0f, -1.5f, 0.5f, 0.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, Constants::inf(), {Containers::InPlaceInit, { /* Ambient isn't affected by light direction, otherwise it's a dot product of a normalized direction */ @@ -360,18 +362,23 @@ const struct { /* These two should produce the same output as the *normalized* dot product is the same */ {"directional, from the other side", "light-directional.tga", - {-1.0f, 1.5f, 0.5f, 0.0f}, 1.0f, Constants::inf(), {}}, + {-1.0f, 1.5f, 0.5f, 0.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, Constants::inf(), {}}, {"directional, scaled direction", "light-directional.tga", - {10.0f, -15.0f, 5.0f, 0.0f}, 1.0f, Constants::inf(), {}}, + {10.0f, -15.0f, 5.0f, 0.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, Constants::inf(), {}}, /* Range should have no effect either, especially zero range should not cause any NaNs */ {"directional, range=0.1", "light-directional.tga", - {1.0f, -1.5f, 0.5f, 0.0f}, 1.0f, 1.0f, {}}, + {1.0f, -1.5f, 0.5f, 0.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, 1.0f, {}}, {"directional, range=0", "light-directional.tga", - {1.0f, -1.5f, 0.5f, 0.0f}, 1.0f, 1.0f, {}}, + {1.0f, -1.5f, 0.5f, 0.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, 1.0f, {}}, /* Light from the other side doesn't contribute anything */ {"directional, from back", "light-none.tga", - {-1.0f, 1.5f, -0.5f, 0.0f}, 1.0f, Constants::inf(), + {-1.0f, 1.5f, -0.5f, 0.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, Constants::inf(), {Containers::InPlaceInit, { /* Only ambient color left */ {{40, 40}, 0x222222_rgb} @@ -379,12 +386,14 @@ const struct { /* This is the same as above, except that twice the intensity causes it to be 2x brighter */ {"directional, intensity=2", "light-directional-intensity2.tga", - {1.0f, -1.5f, 0.5f, 0.0f}, 2.0f, 1.0f, + {1.0f, -1.5f, 0.5f, 0.0f}, Color3{1.0f}, Color3{1.0f}, + 2.0f, 1.0f, {Containers::InPlaceInit, { {{40, 40}, 0x222222_rgb + 0xff8080_rgb*dot(Vector3{1.0f, -1.5f, 0.5f}.normalized(), Vector3::zAxis())*2.0f} }}}, {"point", "light-point.tga", - {0.75f, -0.75f, -1.25f, 1.0f}, 1.0f, Constants::inf(), + {0.75f, -0.75f, -1.25f, 1.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, Constants::inf(), {Containers::InPlaceInit, { /* The range is inf, so it doesn't get fully ambient even at the edge */ @@ -394,15 +403,31 @@ const struct { /* Specular highlight */ {{60, 19}, 0xfefefe_rgb} }}}, + {"point, specular material color", "light-point-specular-color.tga", + {0.75f, -0.75f, -1.25f, 1.0f}, 0x80ff80_rgbf, Color3{1.0f}, + 1.0f, Constants::inf(), + {Containers::InPlaceInit, { + /* Colored specular highlight */ + {{60, 19}, 0xf2fcb0_rgb} + }}}, + {"point, specular light color", "light-point-specular-color.tga", + {0.75f, -0.75f, -1.25f, 1.0f}, Color3{1.0f}, 0x80ff80_rgbf, + 1.0f, Constants::inf(), + {Containers::InPlaceInit, { + /* Colored specular highlight */ + {{60, 19}, 0xf2fcb0_rgb} + }}}, {"point, attenuated specular", "light-point-attenuated-specular.tga", - {1.0f, -1.0f, -0.25f, 1.0f}, 1.0f, 2.5f, + {1.0f, -1.0f, -0.25f, 1.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, 2.5f, {Containers::InPlaceInit, { /* Specular highlight shouldn't be brighter than the attenuated intensity */ {{57, 22}, 0x665656_rgb} }}}, - {"point, range=1.5", "light-point-range1.5.tga", - {0.75f, -0.75f, -1.25f, 1.0f}, 1.0f, 1.5f, + {"point, range=1.5, specular color", "light-point-range1.5.tga", + {0.75f, -0.75f, -1.25f, 1.0f}, Color3{1.0f}, 0x80ff80_rgbf, + 1.0f, 1.5f, {Containers::InPlaceInit, { /* Color goes back to ambient at distance = 1.5 */ {{59, 60}, 0x222222_rgb}, @@ -410,21 +435,25 @@ const struct { {{19, 14}, 0x222222_rgb}, /* But the center and specular stays ~ the same */ {{63, 16}, 0xc57474_rgb}, - {{60, 19}, 0xfefcfc_rgb} + {{60, 19}, 0xf2fcb0_rgb} }}}, {"point, intensity=10, range=0.5", "light-point-intensity10-range0.5.tga", - {0.75f, -0.75f, -1.25f, 1.0f}, 10.0f, 0.5f, {}}, + {0.75f, -0.75f, -1.25f, 1.0f}, Color3{1.0f}, Color3{1.0f}, + 10.0f, 0.5f, {}}, /* Range ends right at the surface, so no contribution */ {"point, range=0.25", "light-none.tga", - {0.75f, -0.75f, -1.25f, 1.0f}, 1.0f, 0.25f, {}}, + {0.75f, -0.75f, -1.25f, 1.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, 0.25f, {}}, /* Zero range should not cause any NaNs, so the ambient contribution is still there */ {"point, range=0.0", "light-none.tga", - {0.75f, -0.75f, -1.25f, 1.0f}, 1.0f, 0.0f, {}}, + {0.75f, -0.75f, -1.25f, 1.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, 0.0f, {}}, /* Distance is 0, which means the direction is always prependicular and thus contributes nothing */ {"point, distance=0", "light-none.tga", - {0.75f, -0.75f, -1.25f, 1.0f}, 1.0f, 0.0f, {}} + {0.75f, -0.75f, -1.25f, 1.0f}, Color3{1.0f}, Color3{1.0f}, + 1.0f, 0.0f, {}} }; constexpr struct { @@ -1476,8 +1505,10 @@ void PhongGLTest::renderLights() { /* Set non-black ambient to catch accidental NaNs -- the render should never be fully black */ .setAmbientColor(0x222222_rgbf) + .setSpecularColor(data.specularColor) .setLightPositions({data.position}) .setLightColors({0xff8080_rgbf*data.intensity}) + .setLightSpecularColors({data.lightSpecularColor}) .setLightRanges({data.range}) .setShininess(60.0f) .setTransformationMatrix(transformation) @@ -1531,6 +1562,8 @@ void PhongGLTest::renderLightsSetOneByOne() { .setLightPosition(1, {0.75f, -0.75f, -1.25f, 1.0f}) .setLightColor(0, 0x00ffff_rgbf) .setLightColor(1, 0xff8080_rgbf) + .setLightSpecularColor(0, 0x0000ff_rgbf) + .setLightSpecularColor(1, 0x80ff80_rgbf) .setLightRange(0, Constants::inf()) .setLightRange(1, 1.5f) .setShininess(60.0f) diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga index 4a0ff7aa2..3ab90d057 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-point-specular-color.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-specular-color.tga new file mode 100644 index 000000000..394a895e3 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-specular-color.tga differ