Browse Source

Shaders: add an ability to control specular light color to Phong.

pull/470/head
Vladimír Vondruš 6 years ago
parent
commit
567b15486f
  1. 2
      doc/changelog.dox
  2. 1
      doc/snippets/MagnumShaders.cpp
  3. 29
      src/Magnum/Shaders/Phong.cpp
  4. 14
      src/Magnum/Shaders/Phong.frag
  5. 44
      src/Magnum/Shaders/Phong.h
  6. 1
      src/Magnum/Shaders/Test/CMakeLists.txt
  7. 65
      src/Magnum/Shaders/Test/PhongGLTest.cpp
  8. BIN
      src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga
  9. BIN
      src/Magnum/Shaders/Test/PhongTestFiles/light-point-specular-color.tga

2
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

1
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});

29
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<Vector4>{Containers::DirectInit, lightCount, Vector4{0.0f, 0.0f, 1.0f, 0.0f}});
setLightColors(Containers::Array<Magnum::Color3>{Containers::DirectInit, lightCount, Magnum::Color3{1.0f}});
Containers::Array<Magnum::Color3> colors{Containers::DirectInit, lightCount, Magnum::Color3{1.0f}};
setLightColors(colors);
setLightSpecularColors(colors);
setLightRanges(Containers::Array<Float>{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<const Magnum::Color3> 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<Magnum::Color3> 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<const Float> ranges) {
CORRADE_ASSERT(_lightCount == ranges.size(),
"Shaders::Phong::setLightRanges(): expected" << _lightCount << "items but got" << ranges.size(), *this);

14
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

44
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.
<ul><li>
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<Color3>) 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<const Magnum::Color3> colors);
/**
* @overload
* @m_since_latest
*/
Phong& setLightSpecularColors(std::initializer_list<Magnum::Color3> 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} */

1
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)

65
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<std::pair<Vector2i, Color3ub>> 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)

BIN
src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/PhongTestFiles/light-point-specular-color.tga

Binary file not shown.
Loading…
Cancel
Save