Browse Source

Shaders: allow Phong with zero lights.

Which makes it equivalent to Flat3D. Useful to reduce complexity in apps
that render models with pre-baked lighting.
pull/364/head
Vladimír Vondruš 7 years ago
parent
commit
a3c8d8ec63
  1. 3
      doc/changelog.dox
  2. 7
      src/Magnum/Shaders/Flat.h
  3. 100
      src/Magnum/Shaders/Phong.cpp
  4. 10
      src/Magnum/Shaders/Phong.frag
  5. 42
      src/Magnum/Shaders/Phong.h
  6. 10
      src/Magnum/Shaders/Phong.vert
  7. 5
      src/Magnum/Shaders/Test/CMakeLists.txt
  8. 108
      src/Magnum/Shaders/Test/PhongGLTest.cpp

3
doc/changelog.dox

@ -346,6 +346,9 @@ See also:
- @ref Shaders::Phong now clamps the specular factor to minimize artifacts - @ref Shaders::Phong now clamps the specular factor to minimize artifacts
when shininess is near zero when shininess is near zero
- @ref Shaders::Phong can now handle zero lights, in which case its output is
equivalent to @ref Shaders::Flat3D. See @ref Shaders-Phong-zero-lights for
more information.
@subsubsection changelog-latest-changes-texturetools TextureTools library @subsubsection changelog-latest-changes-texturetools TextureTools library

7
src/Magnum/Shaders/Flat.h

@ -65,10 +65,13 @@ and then at render time don't forget to bind also the texture via
@ref bindTexture(). The texture is multipled by the color, which is by default @ref bindTexture(). The texture is multipled by the color, which is by default
set to @cpp 0xffffffff_rgbaf @ce. set to @cpp 0xffffffff_rgbaf @ce.
For coloring the texture based on intensity you can use the @ref Vector shader.
@image html shaders-flat.png width=256px @image html shaders-flat.png width=256px
For coloring the texture based on intensity you can use the @ref Vector shader.
The 3D version of this shader is equivalent to @ref Phong with zero lights,
however this implementation is much simpler and thus likely also faster. See
@ref Shaders-Phong-zero-lights "its documentation" for more information.
@section Shaders-Flat-usage Example usage @section Shaders-Flat-usage Example usage
@subsection Shaders-Flat-usage-colored Colored mesh @subsection Shaders-Flat-usage-colored Colored mesh

100
src/Magnum/Shaders/Phong.cpp

@ -69,23 +69,25 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
/* Initializer for the light color array -- we need a list of vec4(1.0)
joined by commas. For GLES we'll simply upload the values directly. */
constexpr const char lightInitializerPreamble[] = "#define LIGHT_COLOR_INITIALIZER ";
constexpr std::size_t lightInitializerPreambleSize =
Containers::arraySize(lightInitializerPreamble) - 1;
constexpr const char lightInitializerItem[] = "vec4(1.0), ";
constexpr std::size_t lightInitializerItemSize =
Containers::arraySize(lightInitializerItem) - 1;
std::string lightInitializer; std::string lightInitializer;
lightInitializer.reserve(Containers::arraySize(lightInitializerPreamble) - 1 + lightCount*lightInitializerItemSize); if(lightCount) {
lightInitializer.append(lightInitializerPreamble, lightInitializerPreambleSize); /* Initializer for the light color array -- we need a list of vec4(1.0)
for(std::size_t i = 0; i != lightCount; ++i) joined by commas. For GLES we'll simply upload the values directly. */
lightInitializer.append(lightInitializerItem, lightInitializerItemSize); constexpr const char lightInitializerPreamble[] = "#define LIGHT_COLOR_INITIALIZER ";
constexpr std::size_t lightInitializerPreambleSize =
/* Drop the last comma and add a newline at the end */ Containers::arraySize(lightInitializerPreamble) - 1;
lightInitializer[lightInitializer.size() - 2] = '\n'; constexpr const char lightInitializerItem[] = "vec4(1.0), ";
lightInitializer.resize(lightInitializer.size() - 1); constexpr std::size_t lightInitializerItemSize =
Containers::arraySize(lightInitializerItem) - 1;
lightInitializer.reserve(Containers::arraySize(lightInitializerPreamble) - 1 + lightCount*lightInitializerItemSize);
lightInitializer.append(lightInitializerPreamble, lightInitializerPreambleSize);
for(std::size_t i = 0; i != lightCount; ++i)
lightInitializer.append(lightInitializerItem, lightInitializerItemSize);
/* Drop the last comma and add a newline at the end */
lightInitializer[lightInitializer.size() - 2] = '\n';
lightInitializer.resize(lightInitializer.size() - 1);
}
#endif #endif
vert.addSource(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture) ? "#define TEXTURED\n" : "") vert.addSource(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture) ? "#define TEXTURED\n" : "")
@ -103,11 +105,11 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
#endif #endif
.addSource(Utility::formatString( .addSource(Utility::formatString(
"#define LIGHT_COUNT {}\n" "#define LIGHT_COUNT {}\n"
"#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount)) "#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount));
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
.addSource(std::move(lightInitializer)) if(lightCount) frag.addSource(std::move(lightInitializer));
#endif #endif
.addSource(rs.get("generic.glsl")) frag.addSource(rs.get("generic.glsl"))
.addSource(rs.get("Phong.frag")); .addSource(rs.get("Phong.frag"));
CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag}));
@ -122,8 +124,9 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
#endif #endif
{ {
bindAttributeLocation(Position::Location, "position"); bindAttributeLocation(Position::Location, "position");
bindAttributeLocation(Normal::Location, "normal"); if(lightCount)
if(flags & Flag::NormalTexture) bindAttributeLocation(Normal::Location, "normal");
if((flags & Flag::NormalTexture) && lightCount)
bindAttributeLocation(Tangent::Location, "tangent"); bindAttributeLocation(Tangent::Location, "tangent");
if(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture)) if(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture))
bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates");
@ -144,17 +147,19 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
{ {
_transformationMatrixUniform = uniformLocation("transformationMatrix"); _transformationMatrixUniform = uniformLocation("transformationMatrix");
_projectionMatrixUniform = uniformLocation("projectionMatrix"); _projectionMatrixUniform = uniformLocation("projectionMatrix");
_normalMatrixUniform = uniformLocation("normalMatrix");
_ambientColorUniform = uniformLocation("ambientColor"); _ambientColorUniform = uniformLocation("ambientColor");
_diffuseColorUniform = uniformLocation("diffuseColor"); if(lightCount) {
_specularColorUniform = uniformLocation("specularColor"); _normalMatrixUniform = uniformLocation("normalMatrix");
_shininessUniform = uniformLocation("shininess"); _diffuseColorUniform = uniformLocation("diffuseColor");
_specularColorUniform = uniformLocation("specularColor");
_shininessUniform = uniformLocation("shininess");
_lightPositionsUniform = uniformLocation("lightPositions");
_lightColorsUniform = uniformLocation("lightColors");
}
if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask");
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId");
#endif #endif
_lightPositionsUniform = uniformLocation("lightPositions");
_lightColorsUniform = uniformLocation("lightColors");
} }
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -162,9 +167,11 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
#endif #endif
{ {
if(flags & Flag::AmbientTexture) setUniform(uniformLocation("ambientTexture"), AmbientTextureLayer); if(flags & Flag::AmbientTexture) setUniform(uniformLocation("ambientTexture"), AmbientTextureLayer);
if(flags & Flag::DiffuseTexture) setUniform(uniformLocation("diffuseTexture"), DiffuseTextureLayer); if(lightCount) {
if(flags & Flag::SpecularTexture) setUniform(uniformLocation("specularTexture"), SpecularTextureLayer); if(flags & Flag::DiffuseTexture) setUniform(uniformLocation("diffuseTexture"), DiffuseTextureLayer);
if(flags & Flag::NormalTexture) setUniform(uniformLocation("normalTexture"), NormalTextureLayer); if(flags & Flag::SpecularTexture) setUniform(uniformLocation("specularTexture"), SpecularTextureLayer);
if(flags & Flag::NormalTexture) setUniform(uniformLocation("normalTexture"), NormalTextureLayer);
}
} }
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
@ -172,17 +179,18 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
/* Default to fully opaque white so we can see the textures */ /* Default to fully opaque white so we can see the textures */
if(flags & Flag::AmbientTexture) setAmbientColor(Color4{1.0f}); if(flags & Flag::AmbientTexture) setAmbientColor(Color4{1.0f});
else setAmbientColor(Color4{0.0f}); else setAmbientColor(Color4{0.0f});
setDiffuseColor(Color4{1.0f});
setSpecularColor(Color4{1.0f});
setShininess(80.0f);
if(flags & Flag::AlphaMask) setAlphaMask(0.5f);
/* Object ID is zero by default */
setLightColors(Containers::Array<Color4>{Containers::DirectInit, lightCount, Color4{1.0f}});
/* Light position is zero by default */
setTransformationMatrix({}); setTransformationMatrix({});
setProjectionMatrix({}); setProjectionMatrix({});
setNormalMatrix({}); if(lightCount) {
setDiffuseColor(Color4{1.0f});
setSpecularColor(Color4{1.0f});
setShininess(80.0f);
if(flags & Flag::AlphaMask) setAlphaMask(0.5f);
/* Object ID is zero by default */
setLightColors(Containers::Array<Color4>{Containers::DirectInit, lightCount, Color4{1.0f}});
/* Light position is zero by default */
setNormalMatrix({});
}
#endif #endif
} }
@ -196,21 +204,21 @@ Phong& Phong::bindAmbientTexture(GL::Texture2D& texture) {
Phong& Phong::bindDiffuseTexture(GL::Texture2D& texture) { Phong& Phong::bindDiffuseTexture(GL::Texture2D& texture) {
CORRADE_ASSERT(_flags & Flag::DiffuseTexture, CORRADE_ASSERT(_flags & Flag::DiffuseTexture,
"Shaders::Phong::bindDiffuseTexture(): the shader was not created with diffuse texture enabled", *this); "Shaders::Phong::bindDiffuseTexture(): the shader was not created with diffuse texture enabled", *this);
texture.bind(DiffuseTextureLayer); if(_lightCount) texture.bind(DiffuseTextureLayer);
return *this; return *this;
} }
Phong& Phong::bindSpecularTexture(GL::Texture2D& texture) { Phong& Phong::bindSpecularTexture(GL::Texture2D& texture) {
CORRADE_ASSERT(_flags & Flag::SpecularTexture, CORRADE_ASSERT(_flags & Flag::SpecularTexture,
"Shaders::Phong::bindSpecularTexture(): the shader was not created with specular texture enabled", *this); "Shaders::Phong::bindSpecularTexture(): the shader was not created with specular texture enabled", *this);
texture.bind(SpecularTextureLayer); if(_lightCount) texture.bind(SpecularTextureLayer);
return *this; return *this;
} }
Phong& Phong::bindNormalTexture(GL::Texture2D& texture) { Phong& Phong::bindNormalTexture(GL::Texture2D& texture) {
CORRADE_ASSERT(_flags & Flag::NormalTexture, CORRADE_ASSERT(_flags & Flag::NormalTexture,
"Shaders::Phong::bindNormalTexture(): the shader was not created with normal texture enabled", *this); "Shaders::Phong::bindNormalTexture(): the shader was not created with normal texture enabled", *this);
texture.bind(NormalTextureLayer); if(_lightCount) texture.bind(NormalTextureLayer);
return *this; return *this;
} }
@ -240,7 +248,7 @@ Phong& Phong::setObjectId(UnsignedInt id) {
Phong& Phong::setLightPositions(const Containers::ArrayView<const Vector3> positions) { Phong& Phong::setLightPositions(const Containers::ArrayView<const Vector3> positions) {
CORRADE_ASSERT(_lightCount == positions.size(), CORRADE_ASSERT(_lightCount == positions.size(),
"Shaders::Phong::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this); "Shaders::Phong::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this);
setUniform(_lightPositionsUniform, positions); if(_lightCount) setUniform(_lightPositionsUniform, positions);
return *this; return *this;
} }
@ -254,7 +262,7 @@ Phong& Phong::setLightPosition(UnsignedInt id, const Vector3& position) {
Phong& Phong::setLightColors(const Containers::ArrayView<const Color4> colors) { Phong& Phong::setLightColors(const Containers::ArrayView<const Color4> colors) {
CORRADE_ASSERT(_lightCount == colors.size(), CORRADE_ASSERT(_lightCount == colors.size(),
"Shaders::Phong::setLightColors(): expected" << _lightCount << "items but got" << colors.size(), *this); "Shaders::Phong::setLightColors(): expected" << _lightCount << "items but got" << colors.size(), *this);
setUniform(_lightColorsUniform, colors); if(_lightCount) setUniform(_lightColorsUniform, colors);
return *this; return *this;
} }

10
src/Magnum/Shaders/Phong.frag

@ -53,6 +53,7 @@ uniform lowp vec4 ambientColor
#endif #endif
; ;
#if LIGHT_COUNT
#ifdef DIFFUSE_TEXTURE #ifdef DIFFUSE_TEXTURE
#ifdef EXPLICIT_TEXTURE_LAYER #ifdef EXPLICIT_TEXTURE_LAYER
layout(binding = 1) layout(binding = 1)
@ -100,6 +101,7 @@ uniform mediump float shininess
= 80.0 = 80.0
#endif #endif
; ;
#endif
#ifdef ALPHA_MASK #ifdef ALPHA_MASK
#ifdef EXPLICIT_UNIFORM_LOCATION #ifdef EXPLICIT_UNIFORM_LOCATION
@ -120,6 +122,7 @@ layout(location = 9)
uniform highp uint objectId; /* defaults to zero */ uniform highp uint objectId; /* defaults to zero */
#endif #endif
#if LIGHT_COUNT
/* Needs to be last because it uses locations 10 + LIGHT_COUNT to /* Needs to be last because it uses locations 10 + LIGHT_COUNT to
10 + 2*LIGHT_COUNT - 1. Location 10 is lightPositions. Also it can't be 10 + 2*LIGHT_COUNT - 1. Location 10 is lightPositions. Also it can't be
specified as 10 + LIGHT_COUNT because that requires ARB_enhanced_layouts. */ specified as 10 + LIGHT_COUNT because that requires ARB_enhanced_layouts. */
@ -131,13 +134,16 @@ uniform lowp vec4 lightColors[LIGHT_COUNT]
= vec4[](LIGHT_COLOR_INITIALIZER) = vec4[](LIGHT_COLOR_INITIALIZER)
#endif #endif
; ;
#endif
#if LIGHT_COUNT
in mediump vec3 transformedNormal; in mediump vec3 transformedNormal;
#ifdef NORMAL_TEXTURE #ifdef NORMAL_TEXTURE
in mediump vec3 transformedTangent; in mediump vec3 transformedTangent;
#endif #endif
in highp vec3 lightDirections[LIGHT_COUNT]; in highp vec3 lightDirections[LIGHT_COUNT];
in highp vec3 cameraDirection; in highp vec3 cameraDirection;
#endif
#if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE) #if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE)
in mediump vec2 interpolatedTextureCoords; in mediump vec2 interpolatedTextureCoords;
@ -163,6 +169,7 @@ void main() {
texture(ambientTexture, interpolatedTextureCoords)* texture(ambientTexture, interpolatedTextureCoords)*
#endif #endif
ambientColor; ambientColor;
#if LIGHT_COUNT
lowp const vec4 finalDiffuseColor = lowp const vec4 finalDiffuseColor =
#ifdef DIFFUSE_TEXTURE #ifdef DIFFUSE_TEXTURE
texture(diffuseTexture, interpolatedTextureCoords)* texture(diffuseTexture, interpolatedTextureCoords)*
@ -173,10 +180,12 @@ void main() {
texture(specularTexture, interpolatedTextureCoords)* texture(specularTexture, interpolatedTextureCoords)*
#endif #endif
specularColor; specularColor;
#endif
/* Ambient color */ /* Ambient color */
fragmentColor = finalAmbientColor; fragmentColor = finalAmbientColor;
#if LIGHT_COUNT
/* Normal */ /* Normal */
mediump vec3 normalizedTransformedNormal = normalize(transformedNormal); mediump vec3 normalizedTransformedNormal = normalize(transformedNormal);
#ifdef NORMAL_TEXTURE #ifdef NORMAL_TEXTURE
@ -203,6 +212,7 @@ void main() {
fragmentColor += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a); fragmentColor += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a);
} }
} }
#endif
#ifdef ALPHA_MASK #ifdef ALPHA_MASK
/* Using <= because if mask is set to 1.0, it should discard all, similarly /* Using <= because if mask is set to 1.0, it should discard all, similarly

42
src/Magnum/Shaders/Phong.h

@ -107,6 +107,14 @@ example.
@requires_gles30 Object ID output requires integer buffer attachments, which @requires_gles30 Object ID output requires integer buffer attachments, which
are not available in OpenGL ES 2.0 or WebGL 1.0. are not available in OpenGL ES 2.0 or WebGL 1.0.
@section Shaders-Phong-zero-lights Zero lights
Creating this shader with zero lights makes its output equivalent to the
@ref Flat3D shader --- only @ref setAmbientColor() and @ref bindAmbientTexture()
(if @ref Flag::AmbientTexture is enabled) are taken into account, which
correspond to @ref Flat::setColor() and @ref Flat::bindTexture(). This is
useful to reduce complexity in apps that render models with pre-baked lights.
@see @ref shaders @see @ref shaders
*/ */
class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
@ -305,11 +313,13 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* @brief Set diffuse color * @brief Set diffuse color
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)
* *
* Initial value is @cpp 0xffffffff_rgbaf @ce. * Initial value is @cpp 0xffffffff_rgbaf @ce. If @ref lightCount() is
* zero, this function is a no-op, as diffuse color doesn't contribute
* to the output in that case.
* @see @ref bindDiffuseTexture() * @see @ref bindDiffuseTexture()
*/ */
Phong& setDiffuseColor(const Color4& color) { Phong& setDiffuseColor(const Color4& color) {
setUniform(_diffuseColorUniform, color); if(_lightCount) setUniform(_diffuseColorUniform, color);
return *this; return *this;
} }
@ -318,7 +328,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)
* *
* Expects that the shader was created with @ref Flag::DiffuseTexture * Expects that the shader was created with @ref Flag::DiffuseTexture
* enabled. * enabled. If @ref lightCount() is zero, this function is a no-op, as
* diffuse color doesn't contribute to the output in that case.
* @see @ref bindTextures(), @ref setDiffuseColor() * @see @ref bindTextures(), @ref setDiffuseColor()
*/ */
Phong& bindDiffuseTexture(GL::Texture2D& texture); Phong& bindDiffuseTexture(GL::Texture2D& texture);
@ -337,7 +348,9 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)
* *
* Expects that the shader was created with @ref Flag::NormalTexture * Expects that the shader was created with @ref Flag::NormalTexture
* enabled and the @ref Tangent attribute was supplied. * enabled and the @ref Tangent attribute was supplied. If
* @ref lightCount() is zero, this function is a no-op, as normals
* dosn't contribute to the output in that case.
* @see @ref bindTextures() * @see @ref bindTextures()
*/ */
Phong& bindNormalTexture(GL::Texture2D& texture); Phong& bindNormalTexture(GL::Texture2D& texture);
@ -349,11 +362,13 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* Initial value is @cpp 0xffffffff_rgbaf @ce. Color will be multiplied * Initial value is @cpp 0xffffffff_rgbaf @ce. Color will be multiplied
* with specular texture if @ref Flag::SpecularTexture is set. If you * with specular texture if @ref Flag::SpecularTexture is set. If you
* want to have a fully diffuse material, set specular color to * want to have a fully diffuse material, set specular color to
* @cpp 0x000000ff_rgbaf @ce. * @cpp 0x000000ff_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.
* @see @ref bindSpecularTexture() * @see @ref bindSpecularTexture()
*/ */
Phong& setSpecularColor(const Color4& color) { Phong& setSpecularColor(const Color4& color) {
setUniform(_specularColorUniform, color); if(_lightCount) setUniform(_specularColorUniform, color);
return *this; return *this;
} }
@ -362,7 +377,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)
* *
* Expects that the shader was created with @ref Flag::SpecularTexture * Expects that the shader was created with @ref Flag::SpecularTexture
* enabled. * enabled. If @ref lightCount() is zero, this function is a no-op, as
* specular color doesn't contribute to the output in that case.
* @see @ref bindTextures(), @ref setSpecularColor() * @see @ref bindTextures(), @ref setSpecularColor()
*/ */
Phong& bindSpecularTexture(GL::Texture2D& texture); Phong& bindSpecularTexture(GL::Texture2D& texture);
@ -409,10 +425,12 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)
* *
* The larger value, the harder surface (smaller specular highlight). * The larger value, the harder surface (smaller specular highlight).
* Initial value is @cpp 80.0f @ce. * Initial value is @cpp 80.0f @ce. If @ref lightCount() is zero, this
* function is a no-op, as specular color doesn't contribute to the
* output in that case.
*/ */
Phong& setShininess(Float shininess) { Phong& setShininess(Float shininess) {
setUniform(_shininessUniform, shininess); if(_lightCount) setUniform(_shininessUniform, shininess);
return *this; return *this;
} }
@ -462,10 +480,12 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* The matrix doesn't need to be normalized, as the renormalization * The matrix doesn't need to be normalized, as the renormalization
* must be done in the shader anyway. You need to set also * must be done in the shader anyway. You need to set also
* @ref setTransformationMatrix() with a corresponding value. Initial * @ref setTransformationMatrix() with a corresponding value. Initial
* value is an identity matrix. * value is an identity matrix. If @ref lightCount() is zero, this
* function is a no-op, as normals don't contribute to the output in
* that case.
*/ */
Phong& setNormalMatrix(const Matrix3x3& matrix) { Phong& setNormalMatrix(const Matrix3x3& matrix) {
setUniform(_normalMatrixUniform, matrix); if(_lightCount) setUniform(_normalMatrixUniform, matrix);
return *this; return *this;
} }

10
src/Magnum/Shaders/Phong.vert

@ -46,6 +46,7 @@ uniform highp mat4 projectionMatrix
#endif #endif
; ;
#if LIGHT_COUNT
#ifdef EXPLICIT_UNIFORM_LOCATION #ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2) layout(location = 2)
#endif #endif
@ -54,18 +55,22 @@ uniform mediump mat3 normalMatrix
= mat3(1.0) = mat3(1.0)
#endif #endif
; ;
#endif
#if LIGHT_COUNT
/* Needs to be last because it uses locations 10 to 10 + LIGHT_COUNT - 1 */ /* Needs to be last because it uses locations 10 to 10 + LIGHT_COUNT - 1 */
#ifdef EXPLICIT_UNIFORM_LOCATION #ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 10) layout(location = 10)
#endif #endif
uniform highp vec3 lightPositions[LIGHT_COUNT]; /* defaults to zero */ uniform highp vec3 lightPositions[LIGHT_COUNT]; /* defaults to zero */
#endif
#ifdef EXPLICIT_ATTRIB_LOCATION #ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = POSITION_ATTRIBUTE_LOCATION) layout(location = POSITION_ATTRIBUTE_LOCATION)
#endif #endif
in highp vec4 position; in highp vec4 position;
#if LIGHT_COUNT
#ifdef EXPLICIT_ATTRIB_LOCATION #ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = NORMAL_ATTRIBUTE_LOCATION) layout(location = NORMAL_ATTRIBUTE_LOCATION)
#endif #endif
@ -77,6 +82,7 @@ layout(location = TANGENT_ATTRIBUTE_LOCATION)
#endif #endif
in mediump vec3 tangent; in mediump vec3 tangent;
#endif #endif
#endif
#ifdef TEXTURED #ifdef TEXTURED
#ifdef EXPLICIT_ATTRIB_LOCATION #ifdef EXPLICIT_ATTRIB_LOCATION
@ -87,18 +93,21 @@ in mediump vec2 textureCoords;
out mediump vec2 interpolatedTextureCoords; out mediump vec2 interpolatedTextureCoords;
#endif #endif
#if LIGHT_COUNT
out mediump vec3 transformedNormal; out mediump vec3 transformedNormal;
#ifdef NORMAL_TEXTURE #ifdef NORMAL_TEXTURE
out mediump vec3 transformedTangent; out mediump vec3 transformedTangent;
#endif #endif
out highp vec3 lightDirections[LIGHT_COUNT]; out highp vec3 lightDirections[LIGHT_COUNT];
out highp vec3 cameraDirection; out highp vec3 cameraDirection;
#endif
void main() { void main() {
/* Transformed vertex position */ /* Transformed vertex position */
highp vec4 transformedPosition4 = transformationMatrix*position; highp vec4 transformedPosition4 = transformationMatrix*position;
highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w; highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w;
#if LIGHT_COUNT
/* Transformed normal and tangent vector */ /* Transformed normal and tangent vector */
transformedNormal = normalMatrix*normal; transformedNormal = normalMatrix*normal;
#ifdef NORMAL_TEXTURE #ifdef NORMAL_TEXTURE
@ -111,6 +120,7 @@ void main() {
/* Direction to the camera */ /* Direction to the camera */
cameraDirection = -transformedPosition; cameraDirection = -transformedPosition;
#endif
/* Transform the position */ /* Transform the position */
gl_Position = projectionMatrix*transformedPosition4; gl_Position = projectionMatrix*transformedPosition4;

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

@ -139,7 +139,10 @@ if(BUILD_GL_TESTS)
PhongTestFiles/textured-diffuse.tga PhongTestFiles/textured-diffuse.tga
PhongTestFiles/textured-normal.tga PhongTestFiles/textured-normal.tga
PhongTestFiles/textured-specular.tga PhongTestFiles/textured-specular.tga
PhongTestFiles/textured.tga) PhongTestFiles/textured.tga
# For zero lights test (equivalency to Flat3D)
FlatTestFiles/textured3D-alpha-mask0.5.tga)
if(NOT BUILD_PLUGINS_STATIC) if(NOT BUILD_PLUGINS_STATIC)
target_include_directories(ShadersPhongGLTest PRIVATE $<TARGET_FILE_DIR:ShadersPhongGLTest>) target_include_directories(ShadersPhongGLTest PRIVATE $<TARGET_FILE_DIR:ShadersPhongGLTest>)
else() else()

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

@ -95,6 +95,8 @@ struct PhongGLTest: GL::OpenGLTester {
void renderObjectId(); void renderObjectId();
#endif #endif
void renderZeroLights();
private: private:
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"}; PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
@ -139,7 +141,8 @@ constexpr struct {
{"object ID", Phong::Flag::ObjectId, 1}, {"object ID", Phong::Flag::ObjectId, 1},
{"object ID + alpha mask + specular texture", Phong::Flag::ObjectId|Phong::Flag::AlphaMask|Phong::Flag::SpecularTexture, 1}, {"object ID + alpha mask + specular texture", Phong::Flag::ObjectId|Phong::Flag::AlphaMask|Phong::Flag::SpecularTexture, 1},
#endif #endif
{"five lights", {}, 5} {"five lights", {}, 5},
{"zero lights", {}, 0}
}; };
using namespace Math::Literals; using namespace Math::Literals;
@ -299,6 +302,16 @@ PhongGLTest::PhongGLTest() {
&PhongGLTest::renderObjectIdTeardown); &PhongGLTest::renderObjectIdTeardown);
#endif #endif
addTests({&PhongGLTest::renderZeroLights},
#ifndef MAGNUM_TARGET_GLES2
&PhongGLTest::renderObjectIdSetup,
&PhongGLTest::renderObjectIdTeardown
#else
&PhongGLTest::renderSetup,
&PhongGLTest::renderTeardown
#endif
);
/* Load the plugins directly from the build tree. Otherwise they're either /* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree */ static and already loaded or not present in the build tree */
#ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME
@ -1065,6 +1078,99 @@ void PhongGLTest::renderObjectId() {
} }
#endif #endif
void PhongGLTest::renderZeroLights() {
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete);
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found.");
GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32,
Primitives::UVSphereTextureCoords::Generate));
Phong shader{
Phong::Flag::AmbientTexture|Phong::Flag::AlphaMask
#ifndef MAGNUM_TARGET_GLES2
|Phong::Flag::ObjectId
#endif
, 0};
Containers::Pointer<Trade::AbstractImporter> importer = _manager.loadAndInstantiate("AnyImageImporter");
CORRADE_VERIFY(importer);
GL::Texture2D ambient;
Containers::Optional<Trade::ImageData2D> ambientImage;
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/diffuse-alpha-texture.tga")) && (ambientImage = importer->image2D(0)));
ambient.setMinificationFilter(GL::SamplerFilter::Linear)
.setMagnificationFilter(GL::SamplerFilter::Linear)
.setWrapping(GL::SamplerWrapping::ClampToEdge)
.setStorage(1, TextureFormatRGBA, ambientImage->size())
.setSubImage(0, {}, *ambientImage);
GL::Texture2D bogus;
shader
.bindAmbientTexture(ambient)
.setAmbientColor(0x9999ff_rgbf)
.setTransformationMatrix(
Matrix4::translation(Vector3::zAxis(-2.15f))*
Matrix4::rotationY(-15.0_degf)*
Matrix4::rotationX(15.0_degf))
.setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f))
.setAlphaMask(0.5f)
#ifndef MAGNUM_TARGET_GLES2
.setObjectId(65534)
#endif
/* Passing a zero-sized light position / color array, shouldn't assert */
.setLightPositions({})
.setLightColors({})
/* Using a bogus normal matrix -- it's not used so it should be okay.
Same for all other unused values, they should get ignored. */
.setNormalMatrix(Matrix3x3{Math::ZeroInit})
.setDiffuseColor(0xfa9922_rgbf)
.setSpecularColor(0xfa9922_rgbf)
.setShininess(0.2f);
/* For proper Z order draw back faces first and then front faces */
GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front);
sphere.draw(shader);
GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back);
sphere.draw(shader);
MAGNUM_VERIFY_NO_GL_ERROR();
#if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL))
/* Compared to FlatGLTest::renderAlpha3D(0.5), there's a bit more different
pixels on the edges, caused by matrix multiplication being done in the
shader and not on the CPU side. */
const Float maxThreshold = 139.0f, meanThreshold = 0.122f;
#else
/* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */
const Float maxThreshold = 139.0f, meanThreshold = 2.896f;
#endif
CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
/* Should be equivalent to masked Flat3D */
Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/textured3D-alpha-mask0.5.tga"),
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
#ifndef MAGNUM_TARGET_GLES2
/* Object ID -- no need to verify the whole image, just check that pixels
on known places have expected values. SwiftShader insists that the read
format has to be 32bit, so the renderbuffer format is that too to make
it the same (ES3 Mesa complains if these don't match). */
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
MAGNUM_VERIFY_NO_GL_ERROR();
/* Outside of the object, cleared to 27 */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[10][10], 27);
/* Inside of the object. Verify that it can hold 16 bits at least. */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[40][46], 65534);
#endif
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongGLTest) CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongGLTest)

Loading…
Cancel
Save