diff --git a/doc/changelog.dox b/doc/changelog.dox index 4992773ed..1d01cf92c 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -77,6 +77,9 @@ See also: - Added @ref Shaders::Phong::setNormalTextureScale(), consuming the recently added @ref Trade::MaterialAttribute::NormalTextureScale material attribute +- @ref Shaders::Phong was reworked to support directional and + range-attenuated point lights to follow the additions to + @ref Trade::LightData @subsubsection changelog-latest-new-scenegraph SceneGraph library @@ -179,6 +182,13 @@ See also: @subsection changelog-latest-deprecated Deprecated APIs +- @ref Shaders::Phong::setLightPositions() and + @ref Shaders::Phong::setLightPosition() taking three-component vectors are + deprecated in favor of variants taking four-component vectors, where the + last component distinguishes between directional and point lights +- @cpp Shaders::Phong::setLightPosition(const Vector3&) @ce is deprecated in + favor of @ref Shaders::Phong::setLightPositions() with a single item --- + it's short enough to not warrant the existence of a dedicated overload - @cpp Trade::AbstractMaterialData @ce as well as its containing header `Magnum/Trade/AbstractMaterialData.h` is now a deprecated alias to the new and more flexible @ref Trade::MaterialData. @@ -264,6 +274,11 @@ See also: afterwards. This can cause compilation breakages in case the type constructor has the parent parameter non-optional, pass the parent explicitly in that case. +- Due to the rework of @ref Shaders::Phong to support directional and + attenuated point lights, the original behavior of unattenuated point lights + isn't available anymore. For backwards compatibility, light positions + supplied through three-component vectors are now represented as directional + lights, which is close, but not exactly the same as before. - Mutable access to @ref Trade::PhongMaterialData color and texture information, deprecated in 2020.06, is now removed, as it's impossible to implement through the redesigned @ref Trade::MaterialData APIs. However @@ -1499,7 +1514,7 @@ Released 2019-10-24, tagged as - @ref Shaders::Phong now clamps the specular factor to minimize artifacts 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 + equivalent to @ref Shaders::Flat3D. See @ref Shaders-Phong-lights-zero for more information. - @ref Shaders::MeshVisualizer is fixed to work properly on Intel Windows drivers, adding a new diff --git a/doc/generated/primitives.cpp b/doc/generated/primitives.cpp index a0e8ec4de..df60eee77 100644 --- a/doc/generated/primitives.cpp +++ b/doc/generated/primitives.cpp @@ -321,7 +321,7 @@ int PrimitiveVisualizer::exec() { phong.setAmbientColor(0x22272e_srgbf) .setDiffuseColor(BaseColor) .setSpecularColor(0x000000_srgbf) - .setLightPosition({5.0f, 5.0f, 7.0f}) + .setLightPositions({{5.0f, 5.0f, 7.0f, 0.0f}}) .setProjectionMatrix(Projection3D) .setTransformationMatrix(Transformation3D) .setNormalMatrix(Transformation3D.normalMatrix()); diff --git a/doc/generated/shaders.cpp b/doc/generated/shaders.cpp index f2fd5aa3f..ac10c18d9 100644 --- a/doc/generated/shaders.cpp +++ b/doc/generated/shaders.cpp @@ -165,7 +165,7 @@ std::string ShaderVisualizer::phong() { .setAmbientColor(0x22272e_srgbf) .setDiffuseColor(BaseColor) .setShininess(200.0f) - .setLightPosition({5.0f, 5.0f, 7.0f}) + .setLightPositions({{5.0f, 5.0f, 7.0f, 0.0f}}) .setProjectionMatrix(Projection) .setTransformationMatrix(Transformation) .setNormalMatrix(Transformation.normalMatrix()) diff --git a/doc/snippets/MagnumSceneGraph-gl.cpp b/doc/snippets/MagnumSceneGraph-gl.cpp index 9c295e131..6aaf43ef2 100644 --- a/doc/snippets/MagnumSceneGraph-gl.cpp +++ b/doc/snippets/MagnumSceneGraph-gl.cpp @@ -103,8 +103,6 @@ class RedCubeDrawable: public SceneGraph::Drawable3D { using namespace Math::Literals; _shader.setDiffuseColor(0xa5c9ea_rgbf) - .setLightPosition(camera.cameraMatrix().transformPoint( - {5.0f, 5.0f, 7.0f})) .setTransformationMatrix(transformationMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) .setProjectionMatrix(camera.projectionMatrix()) @@ -185,7 +183,8 @@ struct MyApplication: Platform::Application { SceneGraph::Object* _cameraObject; SceneGraph::Camera3D* _camera; - Vector3 _lightPositionRelativeToCamera, _lightColor, _ambientColor; + Vector4 _lightPositionRelativeToCamera; + Vector3 _lightColor, _ambientColor; /* [Drawable-multiple-groups] */ // ... @@ -195,8 +194,8 @@ struct MyApplication: Platform::Application { void MyApplication::drawEvent() { _shader.setProjectionMatrix(_camera->projectionMatrix()) - .setLightPosition(_lightPositionRelativeToCamera) - .setLightColor(_lightColor) + .setLightPositions({_lightPositionRelativeToCamera}) + .setLightColors({_lightColor}) .setAmbientColor(_ambientColor); /* Each drawable sets only unique properties such as transformation matrix diff --git a/doc/snippets/MagnumShaders.cpp b/doc/snippets/MagnumShaders.cpp index 9e25cfc9b..75211da65 100644 --- a/doc/snippets/MagnumShaders.cpp +++ b/doc/snippets/MagnumShaders.cpp @@ -52,6 +52,9 @@ #include "Magnum/Shaders/Phong.h" #include "Magnum/Shaders/Vector.h" #include "Magnum/Shaders/VertexColor.h" +#include "Magnum/Trade/LightData.h" + +#define DOXYGEN_IGNORE(...) __VA_ARGS__ using namespace Magnum; using namespace Magnum::Math::Literals; @@ -90,7 +93,6 @@ GL::Texture2D diffuseTexture, specularTexture; Shaders::Phong shader{Shaders::Phong::Flag::DiffuseTexture}; shader.bindDiffuseTexture(diffuseTexture) - .setLightPosition({5.0f, 5.0f, 7.0f}) .setTransformationMatrix(transformationMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) .setProjectionMatrix(projectionMatrix) @@ -489,7 +491,6 @@ Matrix4 projectionMatrix = Shaders::Phong shader; shader.setDiffuseColor(0x2f83cc_rgbf) .setShininess(200.0f) - .setLightPosition({5.0f, 5.0f, 7.0f}) .setTransformationMatrix(transformationMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) .setProjectionMatrix(projectionMatrix) @@ -525,7 +526,6 @@ GL::Texture2D diffuseTexture, specularTexture; Shaders::Phong shader{Shaders::Phong::Flag::DiffuseTexture| Shaders::Phong::Flag::SpecularTexture}; shader.bindTextures(nullptr, &diffuseTexture, &specularTexture, nullptr) - .setLightPosition({5.0f, 5.0f, 7.0f}) .setTransformationMatrix(transformationMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) .setProjectionMatrix(projectionMatrix) @@ -534,6 +534,38 @@ shader.bindTextures(nullptr, &diffuseTexture, &specularTexture, nullptr) } #endif +{ +/* [Phong-usage-lights] */ +Matrix4 directionalLight, pointLight1, pointLight2; // camera-relative + +Shaders::Phong shader{{}, 3}; // 3 lights +shader + .setLightPositions({Vector4{directionalLight.up(), 0.0f}, + Vector4{pointLight1.translation(), 1.0f}, + Vector4{pointLight2.translation(), 1.0f}}) + .setLightColors({0xf0f0ff_srgbf*0.1f, + 0xff8080_srgbf*10.0f, + 0x80ff80_srgbf*10.0f}) + .setLightRanges({Constants::inf(), + 2.0f, + 2.0f}); +/* [Phong-usage-lights] */ +} + +{ +Color3 ambientColor; +GL::Texture2D diffuseTexture; +/* [Phong-usage-lights-ambient] */ +Trade::LightData ambientLight = DOXYGEN_IGNORE(Trade::LightData{{}, {}, {}}); + +Shaders::Phong shader{Shaders::Phong::Flag::AmbientTexture|DOXYGEN_IGNORE(Shaders::Phong::Flag::DiffuseTexture), DOXYGEN_IGNORE(3)}; +shader + .setAmbientColor(ambientColor + ambientLight.color()*ambientLight.intensity()) + .bindAmbientTexture(diffuseTexture) + .bindDiffuseTexture(diffuseTexture); +/* [Phong-usage-lights-ambient] */ +} + { GL::Texture2D diffuseAlphaTexture; Color3 diffuseRgb, specularRgb; diff --git a/src/Magnum/MeshTools/Test/CompileGLTest.cpp b/src/Magnum/MeshTools/Test/CompileGLTest.cpp index e257ca150..de73c7e7a 100644 --- a/src/Magnum/MeshTools/Test/CompileGLTest.cpp +++ b/src/Magnum/MeshTools/Test/CompileGLTest.cpp @@ -303,6 +303,12 @@ CompileGLTest::CompileGLTest() { #endif {4, 4}) .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {4, 4}, ImageData}); + /* Use a point light instead of a directional light to better highlight the + difference in normals; disable specular as that only causes unnecessary + rounding errors across GPUs */ + _phong.setLightPositions({{0.0f, 0.0f, -0.0f, 1.0f}}) + .setLightColors({0xffffff_rgbf*9.0f}) + .setSpecularColor(0x00000000_rgbaf); #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES @@ -762,7 +768,7 @@ template void CompileGLTest::threeDimensions() { _framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}), Utility::Directory::join(COMPILEGLTEST_TEST_DIR, "phong-flat.tga"), /* SwiftShader has some minor off-by-one precision differences */ - (DebugTools::CompareImageToFile{_manager, 0.5f, 0.0079f})); + (DebugTools::CompareImageToFile{_manager, 0.5f, 0.012f})); } else if(data.flags & Flag::GeneratedSmoothNormals) { _framebuffer.clear(GL::FramebufferClear::Color); _phong @@ -777,7 +783,7 @@ template void CompileGLTest::threeDimensions() { _framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}), Utility::Directory::join(COMPILEGLTEST_TEST_DIR, "phong-smooth.tga"), /* SwiftShader has some minor off-by-one precision differences */ - (DebugTools::CompareImageToFile{_manager, 0.25f, 0.0059f})); + (DebugTools::CompareImageToFile{_manager, 0.5f, 0.0088f})); } /* Check with the colored shader, if we have colors */ diff --git a/src/Magnum/MeshTools/Test/CompileTestFiles/phong-flat.tga b/src/Magnum/MeshTools/Test/CompileTestFiles/phong-flat.tga index 4947cdb13..1be253521 100644 Binary files a/src/Magnum/MeshTools/Test/CompileTestFiles/phong-flat.tga and b/src/Magnum/MeshTools/Test/CompileTestFiles/phong-flat.tga differ diff --git a/src/Magnum/MeshTools/Test/CompileTestFiles/phong-smooth.tga b/src/Magnum/MeshTools/Test/CompileTestFiles/phong-smooth.tga index 07f653f90..d13c88440 100644 Binary files a/src/Magnum/MeshTools/Test/CompileTestFiles/phong-smooth.tga and b/src/Magnum/MeshTools/Test/CompileTestFiles/phong-smooth.tga differ diff --git a/src/Magnum/MeshTools/Test/CompileTestFiles/phong.tga b/src/Magnum/MeshTools/Test/CompileTestFiles/phong.tga index c952c51a2..bbd24f458 100644 Binary files a/src/Magnum/MeshTools/Test/CompileTestFiles/phong.tga and b/src/Magnum/MeshTools/Test/CompileTestFiles/phong.tga differ diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index 4cc85bc66..2973a4e8f 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -90,7 +90,7 @@ Common rendering setup: 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. +@ref Shaders-Phong-lights-zero "its documentation" for more information. Conversely, enabling @ref Flag::VertexColor and using a default color with no texturing makes this shader equivalent to @ref VertexColor. diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index f2c630c65..66b78a3db 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -25,7 +25,7 @@ #include "Phong.h" -#ifdef MAGNUM_TARGET_GLES +#if defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_BUILD_DEPRECATED) #include #endif #include @@ -55,7 +55,7 @@ namespace { }; } -Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _lightCount{lightCount}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)} { +Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _lightCount{lightCount}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)}, _lightRangesUniform{_lightPositionsUniform + 2*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", ); @@ -76,22 +76,53 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES - std::string lightInitializer; + std::string lightInitializerVertex, lightInitializerFragment; if(lightCount) { using namespace Containers::Literals; - /* 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 Containers::StringView lightInitializerPreamble = "#define LIGHT_COLOR_INITIALIZER "_s; - constexpr Containers::StringView lightInitializerItem = "vec4(1.0), "_s; - lightInitializer.reserve(lightInitializerPreamble.size() + lightCount*lightInitializerItem.size()); - lightInitializer.append(lightInitializerPreamble.data(), lightInitializerPreamble.size()); + /* Initializer for the light color / position / range arrays -- we need + a list of initializers joined by commas. For GLES we'll simply + upload the values directly. */ + constexpr Containers::StringView lightPositionInitializerPreamble = "#define LIGHT_POSITION_INITIALIZER "_s; + constexpr Containers::StringView lightColorInitializerPreamble = "#define LIGHT_COLOR_INITIALIZER "_s; + constexpr Containers::StringView lightRangeInitializerPreamble = "#define LIGHT_RANGE_INITIALIZER "_s; + constexpr Containers::StringView lightPositionInitializerItem = "vec4(0.0, 0.0, 1.0, 0.0), "_s; + constexpr Containers::StringView lightColorInitializerItem = "vec4(1.0), "_s; + constexpr Containers::StringView lightRangeInitializerItem = "1.0/0.0, "_s; + + lightInitializerVertex.reserve( + lightPositionInitializerPreamble.size() + + lightCount*(lightPositionInitializerItem.size())); + + lightInitializerVertex.append(lightPositionInitializerPreamble.data(), lightPositionInitializerPreamble.size()); for(std::size_t i = 0; i != lightCount; ++i) - lightInitializer.append(lightInitializerItem.data(), lightInitializerItem.size()); + lightInitializerVertex.append(lightPositionInitializerItem.data(), lightPositionInitializerItem.size()); /* Drop the last comma and add a newline at the end */ - lightInitializer[lightInitializer.size() - 2] = '\n'; - lightInitializer.resize(lightInitializer.size() - 1); + lightInitializerVertex[lightInitializerVertex.size() - 2] = '\n'; + lightInitializerVertex.resize(lightInitializerVertex.size() - 1); + + lightInitializerFragment.reserve( + lightColorInitializerPreamble.size() + + lightRangeInitializerPreamble.size() + + lightCount*(lightColorInitializerItem.size() + + lightRangeInitializerItem.size())); + + lightInitializerFragment.append(lightColorInitializerPreamble.data(), lightColorInitializerPreamble.size()); + for(std::size_t i = 0; i != lightCount; ++i) + lightInitializerFragment.append(lightColorInitializerItem.data(), lightColorInitializerItem.size()); + + /* Drop the last comma and add a newline at the end */ + lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n'; + lightInitializerFragment.resize(lightInitializerFragment.size() - 1); + + lightInitializerFragment.append(lightRangeInitializerPreamble.data(), lightRangeInitializerPreamble.size()); + for(std::size_t i = 0; i != lightCount; ++i) + lightInitializerFragment.append(lightRangeInitializerItem.data(), lightRangeInitializerItem.size()); + + /* Drop the last comma and add a newline at the end */ + lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n'; + lightInitializerFragment.resize(lightInitializerFragment.size() - 1); } #endif @@ -105,8 +136,11 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif .addSource(flags & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "") - .addSource(flags >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : "") - .addSource(rs.get("generic.glsl")) + .addSource(flags >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : ""); + #ifndef MAGNUM_TARGET_GLES + if(lightCount) vert.addSource(std::move(lightInitializerVertex)); + #endif + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.vert")); frag.addSource(flags & Flag::AmbientTexture ? "#define AMBIENT_TEXTURE\n" : "") .addSource(flags & Flag::DiffuseTexture ? "#define DIFFUSE_TEXTURE\n" : "") @@ -121,9 +155,13 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #endif .addSource(Utility::formatString( "#define LIGHT_COUNT {}\n" - "#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount)); + "#define LIGHT_COLORS_LOCATION {}\n" + "#define LIGHT_RANGES_LOCATION {}\n", + lightCount, + _lightPositionsUniform + lightCount, + _lightPositionsUniform + 2*lightCount)); #ifndef MAGNUM_TARGET_GLES - if(lightCount) frag.addSource(std::move(lightInitializer)); + if(lightCount) frag.addSource(std::move(lightInitializerFragment)); #endif frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.frag")); @@ -186,6 +224,7 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l _normalTextureScaleUniform = uniformLocation("normalTextureScale"); _lightPositionsUniform = uniformLocation("lightPositions"); _lightColorsUniform = uniformLocation("lightColors"); + _lightRangesUniform = uniformLocation("lightRanges"); } if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); #ifndef MAGNUM_TARGET_GLES2 @@ -218,7 +257,9 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l setShininess(80.0f); 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::Color4{1.0f}}); + setLightRanges(Containers::Array{Containers::DirectInit, lightCount, Constants::inf()}); /* Light position is zero by default */ setNormalMatrix({}); } @@ -328,26 +369,53 @@ Phong& Phong::setTextureMatrix(const Matrix3& matrix) { return *this; } -Phong& Phong::setLightPositions(const Containers::ArrayView positions) { +Phong& Phong::setLightPositions(const Containers::ArrayView positions) { CORRADE_ASSERT(_lightCount == positions.size(), "Shaders::Phong::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this); if(_lightCount) setUniform(_lightPositionsUniform, positions); return *this; } -Phong& Phong::setLightPosition(UnsignedInt id, const Vector3& position) { +/* It's light, but can't be in the header because MSVC needs to know the size + of Vector3 for the initializer list use */ +Phong& Phong::setLightPositions(const std::initializer_list positions) { + return setLightPositions(Containers::arrayView(positions)); +} + +#ifdef MAGNUM_BUILD_DEPRECATED +Phong& Phong::setLightPositions(const Containers::ArrayView positions) { + Containers::Array fourComponent{Containers::NoInit, positions.size()}; + for(std::size_t i = 0; i != positions.size(); ++i) + fourComponent[i] = Vector4{positions[i], 0.0f}; + setLightPositions(fourComponent); + return *this; +} + +Phong& Phong::setLightPositions(const std::initializer_list positions) { + CORRADE_IGNORE_DEPRECATED_PUSH + return setLightPositions(Containers::arrayView(positions)); + CORRADE_IGNORE_DEPRECATED_POP +} +#endif + +Phong& Phong::setLightPosition(const UnsignedInt id, const Vector4& position) { CORRADE_ASSERT(id < _lightCount, "Shaders::Phong::setLightPosition(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); setUniform(_lightPositionsUniform + id, position); return *this; } -/* It's light, but can't be in the header because MSVC needs to know the size - of Vector3 for the initializer list use */ -Phong& Phong::setLightPositions(std::initializer_list lights) { - return setLightPositions({lights.begin(), lights.size()}); +#ifdef MAGNUM_BUILD_DEPRECATED +Phong& Phong::setLightPosition(UnsignedInt id, const Vector3& position) { + return setLightPosition(id, Vector4{position, 0.0f}); } +Phong& Phong::setLightPosition(const Vector3& position) { + /* Use the list variant to check the shader really has just one light */ + return setLightPositions({Vector4{position, 0.0f}}); +} +#endif + Phong& Phong::setLightColors(const Containers::ArrayView colors) { CORRADE_ASSERT(_lightCount == colors.size(), "Shaders::Phong::setLightColors(): expected" << _lightCount << "items but got" << colors.size(), *this); @@ -357,17 +425,35 @@ Phong& Phong::setLightColors(const Containers::ArrayView c /* It's light, but can't be in the header because MSVC needs to know the size of Color for the initializer list use */ -Phong& Phong::setLightColors(std::initializer_list colors) { - return setLightColors({colors.begin(), colors.size()}); +Phong& Phong::setLightColors(const std::initializer_list colors) { + return setLightColors(Containers::arrayView(colors)); } -Phong& Phong::setLightColor(UnsignedInt id, const Magnum::Color4& color) { +Phong& Phong::setLightColor(const UnsignedInt id, const Magnum::Color4& color) { CORRADE_ASSERT(id < _lightCount, "Shaders::Phong::setLightColor(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); setUniform(_lightColorsUniform + 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); + if(_lightCount) setUniform(_lightRangesUniform, ranges); + return *this; +} + +Phong& Phong::setLightRanges(const std::initializer_list ranges) { + return setLightRanges(Containers::arrayView(ranges)); +} + +Phong& Phong::setLightRange(const UnsignedInt id, const Float range) { + CORRADE_ASSERT(id < _lightCount, + "Shaders::Phong::setLightRange(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); + setUniform(_lightRangesUniform + id, range); + return *this; +} + Debug& operator<<(Debug& debug, const Phong::Flag value) { debug << "Shaders::Phong::Flag" << Debug::nospace; diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index a6edc6849..09587b3c7 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -149,6 +149,15 @@ uniform lowp vec4 lightColors[LIGHT_COUNT] = vec4[](LIGHT_COLOR_INITIALIZER) #endif ; + +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = LIGHT_RANGES_LOCATION) +#endif +uniform lowp float lightRanges[LIGHT_COUNT] + #ifndef GL_ES + = float[](LIGHT_RANGE_INITIALIZER) + #endif + ; #endif #if LIGHT_COUNT @@ -161,7 +170,7 @@ in mediump vec3 transformedTangent; in mediump vec3 transformedBitangent; #endif #endif -in highp vec3 lightDirections[LIGHT_COUNT]; +in highp vec4 lightDirections[LIGHT_COUNT]; in highp vec3 cameraDirection; #endif @@ -244,14 +253,25 @@ void main() { /* Add diffuse color for each light */ for(int i = 0; i < LIGHT_COUNT; ++i) { - highp vec3 normalizedLightDirection = normalize(lightDirections[i]); - lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection)); + /* Attenuation. Directional lights have the .w component set to 0, use + that to make the distance zero -- which will then ensure the + attenuation is always 1.0 */ + highp float dist = length(lightDirections[i].xyz)*lightDirections[i].w; + /* If range is 0 for whatever reason, clamp it to a small value to + avoid a NaN when dist is 0 as well (which is the case for + directional lights). */ + highp float attenuation = clamp(1.0 - pow(dist/max(lightRanges[i], 0.0001), 4.0), 0.0, 1.0)/(1.0 + dist); + attenuation = attenuation*attenuation; + + highp vec3 normalizedLightDirection = normalize(lightDirections[i].xyz); + lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation; fragmentColor += vec4(finalDiffuseColor.rgb*lightColors[i].rgb*intensity, lightColors[i].a*finalDiffuseColor.a/float(LIGHT_COUNT)); /* Add specular color, if needed */ if(intensity > 0.001) { highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); - mediump float specularity = clamp(pow(max(0.0, dot(normalize(cameraDirection), reflection)), shininess), 0.0, 1.0); + /* 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); } } diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 1e48b73c0..b7182d628 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -73,6 +73,82 @@ Common rendering setup: @snippet MagnumShaders.cpp Phong-usage-texture2 +@section Shaders-Phong-lights Light specification + +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. + +
  • +Point lights are specified with camera-relative position and the last component +set to @cpp 1.0f @ce together with @ref setLightRanges() describing the +attenuation. The range corresponds to the @ref Trade::LightData::range() and +the attenuation is calculated as the following --- see +@ref Trade-LightData-attenuation for more information: @f[ + F_{att} = \frac{\operatorname{clamp}(1 - (\frac{d}{\color{m-info} R})^4, 0, 1)^2}{1 + d^2} +@f] + +If you use @ref Constants::inf() as a range (which is also the default), the +equation reduces down to a simple inverse square: @f[ + F_{att} = \lim_{{\color{m-info} R} \to \infty} \frac{{\color{m-dim} \operatorname{clamp}(} 1 \mathbin{\color{m-dim} -} {\color{m-dim} (\frac{d}{R})^4, 0, 1)^2}}{1 + d^2} = \frac{1}{1 + d^2} +@f] +
  • +Directional lights are specified with a camera-relative direction *to* the +light with the last component set to @cpp 0.0f @ce --- which effectively makes +@f$ d = 0 @f$ --- and are not affected by values from @ref setLightRanges() in +any way: @f[ + F_{att} = \lim_{d \to 0} \frac{{\color{m-dim} \operatorname{clamp}(} 1 \mathbin{\color{m-dim} -} {\color{m-dim} (\frac{d}{R})^4, 0, 1)^2}}{1 \mathbin{\color{m-dim} +} {\color{m-dim} d^2}} = 1 +@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(). + +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: + +@snippet MagnumShaders.cpp Phong-usage-lights + +@subsection Shaders-Phong-lights-ambient Ambient lights + +In order to avoid redundant uniform inputs, there's no dedicated way to specify +ambient lights. Instead, they are handled by the ambient color input, as the +math for ambient color and lights is equivalent. Add the ambient colors +together and reuse the diffuse texture in the @ref bindAmbientTexture() slot to +have it affected by the ambient as well: + +@snippet MagnumShaders.cpp Phong-usage-lights-ambient + +@subsection Shaders-Phong-lights-zero Zero lights + +As a special case, 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 corresponds to @ref Flat::setColor() and +@ref Flat::bindTexture(). This is useful to reduce complexity in apps that +render models with pre-baked lights. For instanced workflows using zero lights +means the @ref NormalMatrix instance attribute doesn't need to be supplied +either. In addition, enabling @ref Flag::VertexColor and using a default +ambient color with no texturing makes this shader equivalent to +@ref VertexColor. + +@see @ref Trade::MaterialType::Flat + + + +@m_class{m-note m-dim} + +@par + Attenuation based on constant/linear/quadratic factors (the + @ref Trade::LightData::attenuation() property) and spot lights + (@ref Trade::LightData::innerConeAngle(), + @ref Trade::LightData::outerConeAngle() "outerConeAngle()") are not + implemented at the moment. + @section Shaders-Phong-alpha Alpha blending and masking Alpha / transparency is supported by the shader implicitly, but to have it @@ -169,18 +245,6 @@ well to ensure lighting works: @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} in 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. -For instanced workflows using zero lights means the @ref NormalMatrix instance -attribute doesn't need to be supplied either. In addition, enabling -@ref Flag::VertexColor and using a default ambient color with no texturing -makes this shader equivalent to @ref VertexColor. - @see @ref shaders */ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { @@ -544,7 +608,7 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * If @ref Flag::AmbientTexture is set, default value is * @cpp 0xffffffff_rgbaf @ce and the color will be multiplied with * ambient texture, otherwise default value is @cpp 0x00000000_rgbaf @ce. - * @see @ref bindAmbientTexture() + * @see @ref bindAmbientTexture(), @ref Shaders-Phong-lights-ambient */ Phong& setAmbientColor(const Magnum::Color4& color); @@ -554,7 +618,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * * Expects that the shader was created with @ref Flag::AmbientTexture * enabled. - * @see @ref bindTextures(), @ref setAmbientColor() + * @see @ref bindTextures(), @ref setAmbientColor(), + * @ref Shaders-Phong-lights-ambient */ Phong& bindAmbientTexture(GL::Texture2D& texture); @@ -740,40 +805,77 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { /** * @brief Set light positions * @return Reference to self (for method chaining) + * @m_since_latest * - * Initial values are zero vectors --- that will in most cases cause - * the object to be rendered black (or in the ambient color), as the - * lights are is inside of it. Expects that the size of the @p lights - * array is the same as @ref lightCount(). - * @see @ref setLightPosition(UnsignedInt, const Vector3&), - * @ref setLightPosition(const Vector3&) + * Depending on the fourth component, the value is treated as either a + *camera-relative position of a point light, if the fourth component is + * @cpp 1.0f @ce; or a direction *to* a directional light, if the + * fourth component is @cpp 0.0f @ce. Expects that the size of the + * @p positions array is the same as @ref lightCount(). Initial values + * are @cpp {0.0f, 0.0f, 1.0f, 0.0f} @ce --- a directional "fill" light + * coming from the camera. + * @see @ref Shaders-Phong-lights, @ref setLightPosition() */ - Phong& setLightPositions(Containers::ArrayView lights); + Phong& setLightPositions(Containers::ArrayView positions); - /** @overload */ - Phong& setLightPositions(std::initializer_list lights); + /** + * @overload + * @m_since_latest + */ + Phong& setLightPositions(std::initializer_list positions); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief @copybrief setLightPositions(Containers::ArrayView) + * @m_deprecated_since_latest Use @ref setLightPositions(Containers::ArrayView) + * instead. This function sets the fourth component to + * @cpp 0.0f @ce to preserve the original behavior as close as + * possible. + */ + CORRADE_DEPRECATED("use setLightPositions(Containers::ArrayView) instead") Phong& setLightPositions(Containers::ArrayView positions); + + /** + * @brief @copybrief setLightPositions(std::initializer_list) + * @m_deprecated_since_latest Use @ref setLightPositions(std::initializer_list) + * instead. This function sets the fourth component to + * @cpp 0.0f @ce to preserve the original behavior as close as + * possible. + */ + CORRADE_DEPRECATED("use setLightPositions(std::initializer_list) instead") Phong& setLightPositions(std::initializer_list positions); + #endif /** * @brief Set position for given light * @return Reference to self (for method chaining) + * @m_since_latest * - * Unlike @ref setLightPosition() updates just a single light position. - * Expects that @p id is less than @ref lightCount(). - * @see @ref setLightPosition(const Vector3&) + * Unlike @ref setLightPositions() updates just a single light + * position. 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& setLightPosition(UnsignedInt id, const Vector4& position); + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief @copybrief setLightPosition(UnsignedInt, const Vector4&) + * @m_deprecated_since_latest Use @ref setLightPosition(UnsignedInt, const Vector4&) + * instead. This function sets the fourth component to + * @cpp 0.0f @ce to preserve the original behavior as close as + * possible. */ - Phong& setLightPosition(UnsignedInt id, const Vector3& position); + CORRADE_DEPRECATED("use setLightPosition(UnsignedInt, const Vector4&) instead") Phong& setLightPosition(UnsignedInt id, const Vector3& position); /** * @brief Set light position - * @return Reference to self (for method chaining) - * - * Convenience alternative to @ref setLightPositions() when there is - * just one light. - * @see @ref setLightPosition(UnsignedInt, const Vector3&) + * @m_deprecated_since_latest Use @ref setLightPositions(std::initializer_list) + * with a single item instead --- it's short enough to not warrant + * the existence of a dedicated overload. This function sets the + * fourth component to @cpp 0.0f @ce to preserve the original + * behavior as close as possible. */ - Phong& setLightPosition(const Vector3& position) { - return setLightPositions({&position, 1}); - } + CORRADE_DEPRECATED("use setLightPositions(std::initializer_list) instead") Phong& setLightPosition(const Vector3& position); + #endif /** * @brief Set light colors @@ -809,6 +911,35 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { return setLightColors({&color, 1}); } + /** + * @brief Set light attenuation ranges + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Initial values are @ref Constants::inf(). Expects that the size of + * the @p ranges array is the same as @ref lightCount(). + * @see @ref Shaders-Phong-lights, @ref setLightRange() + */ + Phong& setLightRanges(Containers::ArrayView ranges); + + /** + * @overload + * @m_since_latest + */ + Phong& setLightRanges(std::initializer_list ranges); + + /** + * @brief Set attenuation range for given light + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Unlike @ref setLightRanges() updates just a single light range. 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& setLightRange(UnsignedInt id, Float range); + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES @@ -834,7 +965,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { Int _objectIdUniform{10}; #endif Int _lightPositionsUniform{11}, - _lightColorsUniform; /* 11 + lightCount, set in the constructor */ + _lightColorsUniform, /* 11 + lightCount, set in the constructor */ + _lightRangesUniform; /* 11 + 2*lightCount, set in the constructor */ }; /** @debugoperatorclassenum{Phong,Phong::Flag} */ diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 0eb740916..038f4b1ef 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -77,7 +77,11 @@ uniform mediump mat3 textureMatrix #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 11) #endif -uniform highp vec3 lightPositions[LIGHT_COUNT]; /* defaults to zero */ +uniform highp vec4 lightPositions[LIGHT_COUNT] + #ifndef GL_ES + = vec4[](LIGHT_POSITION_INITIALIZER); + #endif + ; #endif #ifdef EXPLICIT_ATTRIB_LOCATION @@ -168,7 +172,7 @@ out mediump vec3 transformedTangent; out mediump vec3 transformedBitangent; #endif #endif -out highp vec3 lightDirections[LIGHT_COUNT]; +out highp vec4 lightDirections[LIGHT_COUNT]; out highp vec3 cameraDirection; #endif @@ -209,9 +213,10 @@ void main() { #endif #endif - /* Direction to the light */ + /* Direction to the light. Directional lights have the last component set + to 0, which gets used to ignore the transformed position. */ for(int i = 0; i < LIGHT_COUNT; ++i) - lightDirections[i] = lightPositions[i] - transformedPosition; + lightDirections[i] = vec4(lightPositions[i].xyz - transformedPosition*lightPositions[i].w, lightPositions[i].w); /* Direction to the camera */ cameraDirection = -transformedPosition; diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 9781971f1..6850dfff6 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -240,6 +240,13 @@ if(BUILD_GL_TESTS) PhongTestFiles/textured-specular.tga PhongTestFiles/textured.tga PhongTestFiles/vertexColor.tga + PhongTestFiles/light-directional-intensity2.tga + PhongTestFiles/light-directional.tga + PhongTestFiles/light-none.tga + PhongTestFiles/light-point-attenuated-specular.tga + PhongTestFiles/light-point-intensity10-range0.5.tga + PhongTestFiles/light-point-range1.5.tga + PhongTestFiles/light-point.tga # For zero lights test (equivalency to Flat3D) FlatTestFiles/textured3D-alpha-mask0.5.tga) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 76217abfb..a1d8cafa2 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -103,6 +104,7 @@ struct PhongGLTest: GL::OpenGLTester { void renderObjectId(); #endif + void renderLights(); void renderLowLightAngle(); void renderZeroLights(); @@ -337,6 +339,93 @@ constexpr struct { }; #endif +const struct { + const char* name; + const char* file; + Vector4 position; + Float intensity; + Float range; + Containers::Array> picks; +} RenderLightsData[] { + {"directional", "light-directional.tga", + {1.0f, -1.5f, 0.5f, 0.0f}, 1.0f, Constants::inf(), + {Containers::InPlaceInit, { + /* Ambient isn't affected by light direction, otherwise it's a + dot product of a normalized direction */ + {{40, 40}, 0x222222_rgb + 0xff8080_rgb*dot(Vector3{1.0f, -1.5f, 0.5f}.normalized(), Vector3::zAxis())}, + /* and it's the same across the whole surface */ + {{70, 70}, 0x222222_rgb + 0xff8080_rgb*dot(Vector3{1.0f, -1.5f, 0.5f}.normalized(), Vector3::zAxis())}, + }}}, + /* 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(), {}}, + {"directional, scaled direction", "light-directional.tga", + {10.0f, -15.0f, 5.0f, 0.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, {}}, + {"directional, range=0", "light-directional.tga", + {1.0f, -1.5f, 0.5f, 0.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(), + {Containers::InPlaceInit, { + /* Only ambient color left */ + {{40, 40}, 0x222222_rgb} + }}}, + /* 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, + {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(), + {Containers::InPlaceInit, { + /* The range is inf, so it doesn't get fully ambient even at the + edge */ + {{8, 71}, 0x242324_rgb}, + /* Closest to the light. TODO figure out the equation, sigh */ + {{63, 16}, 0xc57474_rgb /*0x222222_rgb + 0xff8080_rgb/(1 + 0.25f*0.25f)*/}, + /* Specular highlight */ + {{60, 19}, 0xfefefe_rgb} + }}}, + {"point, attenuated specular", "light-point-attenuated-specular.tga", + {1.0f, -1.0f, -0.25f, 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, + {Containers::InPlaceInit, { + /* Color goes back to ambient at distance = 1.5 */ + {{59, 60}, 0x222222_rgb}, + {{29, 50}, 0x222222_rgb}, + {{19, 14}, 0x222222_rgb}, + /* But the center and specular stays ~ the same */ + {{63, 16}, 0xc57474_rgb}, + {{60, 19}, 0xfefcfc_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, {}}, + /* 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, {}}, + /* 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, {}}, + /* 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, {}} +}; + constexpr struct { const char* name; const char* file; @@ -346,19 +435,19 @@ constexpr struct { {"diffuse", "instanced.tga", {}, #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* AMD has one off pixel; SwiftShader a bit more */ - 93.67f, 0.106f, + 96.34f, 0.113f, #else /* WebGL 1 doesn't have 8bit renderbuffer storage */ - 93.67f, 0.106f, + 96.34f, 0.113f, #endif }, {"diffuse + normal", "instanced-normal.tga", Phong::Flag::NormalTexture, #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* AMD has one off pixel, llvmpipe more */ - 94.5f, 0.333f, + 96.0f, 0.333f, #else /* WebGL 1 doesn't have 8bit renderbuffer storage */ - 94.5f, 0.132f, + 96.0f, 0.333f, #endif } }; @@ -425,6 +514,11 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderObjectIdTeardown); #endif + addInstancedTests({&PhongGLTest::renderLights}, + Containers::arraySize(RenderLightsData), + &PhongGLTest::renderSetup, + &PhongGLTest::renderTeardown); + addTests({&PhongGLTest::renderLowLightAngle, &PhongGLTest::renderZeroLights}, #ifndef MAGNUM_TARGET_GLES2 @@ -606,17 +700,20 @@ void PhongGLTest::setWrongLightCount() { /* This is okay */ shader.setLightColors({{}, {}, {}, {}, {}}) - .setLightPositions({{}, {}, {}, {}, {}}); + .setLightPositions(Containers::arrayView({{}, {}, {}, {}, {}})) + .setLightRanges({0.0f, 0.0f, 0.0f, 0.0f, 0.0f}); MAGNUM_VERIFY_NO_GL_ERROR(); /* This is not */ - shader.setLightColor({}) - .setLightPosition({}); + shader.setLightColors({Color3{}}) + .setLightPositions({Vector4{}}) + .setLightRanges({0.0f}); CORRADE_COMPARE(out.str(), "Shaders::Phong::setLightColors(): expected 5 items but got 1\n" - "Shaders::Phong::setLightPositions(): expected 5 items but got 1\n"); + "Shaders::Phong::setLightPositions(): expected 5 items but got 1\n" + "Shaders::Phong::setLightRanges(): expected 5 items but got 1\n"); } void PhongGLTest::setWrongLightId() { @@ -631,17 +728,20 @@ void PhongGLTest::setWrongLightId() { /* This is okay */ shader.setLightColor(2, {}) - .setLightPosition(2, {}); + .setLightPosition(2, Vector4{}) + .setLightRange(2, 0.0f); MAGNUM_VERIFY_NO_GL_ERROR(); /* This is not */ shader.setLightColor(3, {}) - .setLightPosition(3, {}); + .setLightPosition(3, Vector4{}) + .setLightRange(3, 0.0f); CORRADE_COMPARE(out.str(), "Shaders::Phong::setLightColor(): light ID 3 is out of bounds for 3 lights\n" - "Shaders::Phong::setLightPosition(): light ID 3 is out of bounds for 3 lights\n"); + "Shaders::Phong::setLightPosition(): light ID 3 is out of bounds for 3 lights\n" + "Shaders::Phong::setLightRange(): light ID 3 is out of bounds for 3 lights\n"); } constexpr Vector2i RenderSize{80, 80}; @@ -673,16 +773,7 @@ void PhongGLTest::renderTeardown() { } void PhongGLTest::renderDefaults() { - /* The light is at the center by default, so we scale the sphere to half - and move the vertices back a bit to avoid a fully-black render but - still have the thing in the default [-1; 1] cube */ - Trade::MeshData meshData = Primitives::uvSphereSolid(16, 32); - Matrix4 transformation = - Matrix4::translation(Vector3::zAxis(-1.0f))*Matrix4::scaling(Vector3(1.0f, 1.0f, 0.25f)); - MeshTools::transformPointsInPlace(transformation, meshData.mutableAttribute(Trade::MeshAttribute::Position)); - /** @todo use Matrix4::normalMatrix() */ - MeshTools::transformVectorsInPlace(transformation.inverted().transposed(), meshData.mutableAttribute(Trade::MeshAttribute::Normal)); - GL::Mesh sphere = MeshTools::compile(meshData); + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); Phong{} .draw(sphere); @@ -716,8 +807,8 @@ void PhongGLTest::renderColored() { Phong{{}, 2} .setLightColors({data.lightColor1, data.lightColor2}) - .setLightPositions({{data.lightPosition1, -3.0f, 0.0f}, - {data.lightPosition2, -3.0f, 0.0f}}) + .setLightPositions({{data.lightPosition1, -3.0f, 2.0f, 0.0f}, + {data.lightPosition2, -3.0f, 2.0f, 0.0f}}) .setAmbientColor(0x330033_rgbf) .setDiffuseColor(0xccffcc_rgbf) .setSpecularColor(0x6666ff_rgbf) @@ -799,8 +890,8 @@ void PhongGLTest::renderSinglePixelTextured() { Phong shader{Phong::Flag::AmbientTexture|Phong::Flag::DiffuseTexture|Phong::Flag::SpecularTexture, 2}; shader.setLightColors({0x993366_rgbf, 0x669933_rgbf}) - .setLightPositions({{-3.0f, -3.0f, 0.0f}, - { 3.0f, -3.0f, 0.0f}}) + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)); @@ -905,8 +996,8 @@ void PhongGLTest::renderTextured() { /* Using default (white) light colors to have the texture data visible better */ - shader.setLightPositions({{-3.0f, -3.0f, 0.0f}, - { 3.0f, -3.0f, 0.0f}}) + shader.setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) .setTransformationMatrix( Matrix4::translation(Vector3::zAxis(-2.15f))* Matrix4::rotationY(-15.0_degf)* @@ -921,10 +1012,10 @@ void PhongGLTest::renderTextured() { #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has few rounding errors at the edges (giving a large max error), but that's basically it. Apple A8 has more. */ - const Float maxThreshold = 210.4f, meanThreshold = 0.202f; + const Float maxThreshold = 227.0f, meanThreshold = 0.202f; #else /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's a bit worse */ - const Float maxThreshold = 210.4f, meanThreshold = 3.434f; + const Float maxThreshold = 227.0f, meanThreshold = 3.434f; #endif CORRADE_COMPARE_WITH( /* Dropping the alpha channel, as it's always 1.0 */ @@ -979,8 +1070,8 @@ void PhongGLTest::renderTexturedNormal() { exactly the same images. */ Phong shader{Phong::Flag::NormalTexture|data.flags, 2}; shader.setLightPositions({ - Matrix4::rotationZ(data.rotation).transformPoint({-3.0f, -3.0f, 0.0f}), - Matrix4::rotationZ(data.rotation).transformPoint({ 3.0f, -3.0f, 0.0f})}) + Matrix4::rotationZ(data.rotation)*Vector4{-3.0f, -3.0f, 2.0f, 0.0f}, + Matrix4::rotationZ(data.rotation)*Vector4{ 3.0f, -3.0f, 2.0f, 0.0f}}) .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.35f))* Matrix4::rotationZ(data.rotation)* Matrix4::rotationY(-15.0_degf)* @@ -1068,8 +1159,8 @@ template void PhongGLTest::renderVertexColor() { .setSubImage(0, {}, *image); Phong{Phong::Flag::DiffuseTexture|Phong::Flag::VertexColor, 2} - .setLightPositions({{-3.0f, -3.0f, 0.0f}, - { 3.0f, -3.0f, 0.0f}}) + .setLightPositions({{-3.0f, -3.0f, 0.0f, 0.0f}, + { 3.0f, -3.0f, 0.0f, 0.0f}}) .setTransformationMatrix( Matrix4::translation(Vector3::zAxis(-2.15f))* Matrix4::rotationY(-15.0_degf)* @@ -1106,7 +1197,7 @@ void PhongGLTest::renderShininess() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); Phong{} - .setLightPosition({-3.0f, -3.0f, 0.0f}) + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}}) .setDiffuseColor(0xff3333_rgbf) .setSpecularColor(data.specular) .setShininess(data.shininess) @@ -1180,7 +1271,7 @@ void PhongGLTest::renderShininess() { Utility::Directory::join({_testDir, "PhongTestFiles", "shininess0-overflow.tga"}), /* The threshold = 0.001 case has a slight reddish tone on SwiftShader; ARM Mali has one pixel off */ - (DebugTools::CompareImageToFile{_manager, 255.0f, 1.476f})); + (DebugTools::CompareImageToFile{_manager, 255.0f, 23.1f})); } } @@ -1240,8 +1331,8 @@ void PhongGLTest::renderAlpha() { Primitives::UVSphereFlag::TextureCoordinates)); Phong shader{data.flags, 2}; - shader.setLightPositions({{-3.0f, -3.0f, 0.0f}, - { 3.0f, -3.0f, 0.0f}}) + shader.setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) .setTransformationMatrix( Matrix4::translation(Vector3::zAxis(-2.15f))* Matrix4::rotationY(-15.0_degf)* @@ -1272,10 +1363,10 @@ void PhongGLTest::renderAlpha() { That's okay, as we have only 8bit texture precision. SwiftShader has additionally a few minor rounding errors at the edges, Apple A8 a bit more. */ - const Float maxThreshold = 172.667f, meanThreshold = 0.385f; + const Float maxThreshold = 189.4f, meanThreshold = 0.385f; #else /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ - const Float maxThreshold = 172.667f, meanThreshold = 4.736f; + const Float maxThreshold = 189.4f, meanThreshold = 4.736f; #endif CORRADE_COMPARE_WITH( /* Dropping the alpha channel, as it's always 1.0 */ @@ -1343,8 +1434,8 @@ void PhongGLTest::renderObjectId() { Phong{data.flags, 2} .setLightColors({0x993366_rgbf, 0x669933_rgbf}) - .setLightPositions({{-3.0f, -3.0f, 0.0f}, - { 3.0f, -3.0f, 0.0f}}) + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) .setAmbientColor(0x330033_rgbf) .setDiffuseColor(0xccffcc_rgbf) .setSpecularColor(0x6666ff_rgbf) @@ -1389,6 +1480,58 @@ void PhongGLTest::renderObjectId() { } #endif +void PhongGLTest::renderLights() { + auto&& data = RenderLightsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + GL::Mesh plane = MeshTools::compile(Primitives::planeSolid()); + + Matrix4 transformation = + Matrix4::translation({0.0f, 0.0f, -1.5f}); + + Phong{{}, 1} + /* Set non-black ambient to catch accidental NaNs -- the render should + never be fully black */ + .setAmbientColor(0x222222_rgbf) + .setLightPositions({data.position}) + .setLightColors({0xff8080_rgbf*data.intensity}) + .setLightRanges({data.range}) + .setShininess(60.0f) + .setTransformationMatrix(transformation) + .setNormalMatrix(transformation.normalMatrix()) + .setProjectionMatrix(Matrix4::perspectiveProjection(80.0_degf, 1.0f, 0.1f, 20.0f)) + .draw(plane); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + const Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}); + + /* Analytical output check. Comment this out when image comparison fails + for easier debugging. */ + for(const auto& pick: data.picks) { + CORRADE_ITERATION(pick.first); + CORRADE_COMPARE_WITH( + image.pixels()[pick.first.y()][pick.first.x()].xyz(), + pick.second, TestSuite::Compare::around(0x010101_rgb)); + } + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + const Float maxThreshold = 3.0f, meanThreshold = 0.02f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 3.0f, meanThreshold = 0.02f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(image.pixels()), + Utility::Directory::join({_testDir, "PhongTestFiles", data.file}), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} + void PhongGLTest::renderLowLightAngle() { GL::Mesh plane = MeshTools::compile(Primitives::planeSolid()); @@ -1404,7 +1547,7 @@ void PhongGLTest::renderLowLightAngle() { fragment-interpolated light direction being incorrect, most visible with long polygons and low light angles. */ Phong{{}, 1} - .setLightPosition({0.0f, 0.1f, 0.0f}) + .setLightPositions({{0.0f, 0.1f, 0.0f, 1.0f}}) .setShininess(200) .setTransformationMatrix(transformation) .setNormalMatrix(transformation.normalMatrix()) @@ -1476,7 +1619,7 @@ void PhongGLTest::renderZeroLights() { .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) /* Keep alpha mask at the default 0.5 to test the default */ /* Passing a zero-sized light position / color array, shouldn't assert */ - .setLightPositions({}) + .setLightPositions(Containers::ArrayView{}) .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. */ @@ -1622,8 +1765,8 @@ void PhongGLTest::renderInstanced() { Phong::Flag::InstancedTransformation| Phong::Flag::InstancedTextureOffset|data.flags, 2}; shader - .setLightPositions({{-3.0f, -3.0f, 0.0f}, - { 3.0f, -3.0f, 0.0f}}) + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) .setTransformationMatrix( Matrix4::translation(Vector3::zAxis(-1.75f))* Matrix4::rotationY(-15.0_degf)* diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/colored.tga b/src/Magnum/Shaders/Test/PhongTestFiles/colored.tga index 6d99eeb91..7192259a0 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/colored.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/colored.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/defaults.tga b/src/Magnum/Shaders/Test/PhongTestFiles/defaults.tga index 83e4a1818..a5466f9e0 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/defaults.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/defaults.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/instanced-normal.tga b/src/Magnum/Shaders/Test/PhongTestFiles/instanced-normal.tga index 7b2fe00b4..b8db63113 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/instanced-normal.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/instanced-normal.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga b/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga index c148e581a..a761d9413 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-directional-intensity2.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-directional-intensity2.tga new file mode 100644 index 000000000..a8299eaa9 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-directional-intensity2.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-directional.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-directional.tga new file mode 100644 index 000000000..c313c2cd2 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-directional.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-none.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-none.tga new file mode 100644 index 000000000..d82fb25b8 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-none.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-point-attenuated-specular.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-attenuated-specular.tga new file mode 100644 index 000000000..1352508f9 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-attenuated-specular.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-point-intensity10-range0.5.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-intensity10-range0.5.tga new file mode 100644 index 000000000..9d5412af6 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-intensity10-range0.5.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga new file mode 100644 index 000000000..4a0ff7aa2 Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-point-range1.5.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/light-point.tga b/src/Magnum/Shaders/Test/PhongTestFiles/light-point.tga new file mode 100644 index 000000000..0d6c9806b Binary files /dev/null and b/src/Magnum/Shaders/Test/PhongTestFiles/light-point.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/low-light-angle.tga b/src/Magnum/Shaders/Test/PhongTestFiles/low-light-angle.tga index e26497c00..04aa4057f 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/low-light-angle.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/low-light-angle.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/shininess-black-specular.tga b/src/Magnum/Shaders/Test/PhongTestFiles/shininess-black-specular.tga index 1a439c90f..6aae4829c 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/shininess-black-specular.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/shininess-black-specular.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/shininess0.tga b/src/Magnum/Shaders/Test/PhongTestFiles/shininess0.tga index 704c51993..7a907c283 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/shininess0.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/shininess0.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/shininess10.tga b/src/Magnum/Shaders/Test/PhongTestFiles/shininess10.tga index c969fc091..f176d0b30 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/shininess10.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/shininess10.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/shininess80.tga b/src/Magnum/Shaders/Test/PhongTestFiles/shininess80.tga index 6fa309822..d05f10363 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/shininess80.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/shininess80.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-ambient.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-ambient.tga index b67b086f4..fb0215875 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-ambient.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-ambient.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha-mask0.5.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha-mask0.5.tga index 633ff9c18..29883d4e4 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha-mask0.5.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha-mask0.5.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha.tga index c541bb420..fc7b0183a 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-alpha.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga index 201778990..dc8346477 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse-transformed.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse.tga index 1b0ed212b..68bfdf5e2 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-diffuse.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal-left.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal-left.tga index 7bf697376..2770024f8 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal-left.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal-left.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal.tga index f6d72ad78..d6c1b5f76 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.0.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.0.tga index e5aa174be..b210cbf26 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.0.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.0.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.5.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.5.tga index b85fd7aac..3038fea2d 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.5.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-normal0.5.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured-specular.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured-specular.tga index 70e53bec0..98e09278f 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured-specular.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured-specular.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/textured.tga b/src/Magnum/Shaders/Test/PhongTestFiles/textured.tga index 1c7d6d153..cbd4f0ea3 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/textured.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/textured.tga differ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/vertexColor.tga b/src/Magnum/Shaders/Test/PhongTestFiles/vertexColor.tga index b674db5ba..e3699668a 100644 Binary files a/src/Magnum/Shaders/Test/PhongTestFiles/vertexColor.tga and b/src/Magnum/Shaders/Test/PhongTestFiles/vertexColor.tga differ